iota_config/
migration_tx_data.rs1use std::{
5 collections::{BTreeMap, HashSet},
6 fs::File,
7 io::BufReader,
8 path::Path,
9};
10
11use anyhow::{Context, Result};
12use iota_sdk_types::{
13 CheckpointContents, CheckpointSummary, TransactionDigest, TransactionEffects, TransactionEvents,
14};
15use iota_types::{
16 effects::TransactionEffectsAPI, message_envelope::Message,
17 messages_checkpoint::CheckpointContentsExt, transaction::TransactionEnvelope,
18};
19use serde::{Deserialize, Serialize};
20use tracing::trace;
21
22use crate::genesis::Genesis;
23
24pub type TransactionsData =
25 BTreeMap<TransactionDigest, (TransactionEnvelope, TransactionEffects, TransactionEvents)>;
26
27#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Default)]
30pub struct MigrationTxData {
31 inner: TransactionsData,
32}
33
34impl MigrationTxData {
35 pub fn txs_data(&self) -> &TransactionsData {
36 &self.inner
37 }
38
39 fn validate_from_genesis_components(
40 &self,
41 checkpoint: &CheckpointSummary,
42 contents: &CheckpointContents,
43 genesis_tx_digest: TransactionDigest,
44 ) -> anyhow::Result<()> {
45 anyhow::ensure!(
46 checkpoint.contents_digest == contents.digest(),
47 "checkpoint contents digest is corrupted"
48 );
49 let mut validation_digests_queue: HashSet<TransactionDigest> =
50 self.inner.keys().copied().collect();
51 for exec_digest in contents.iter() {
52 if exec_digest.transaction == genesis_tx_digest {
55 continue;
56 }
57 let valid_tx_digest = &exec_digest.transaction;
58 let valid_effects_digest = &exec_digest.effects;
59 let (tx, effects, events) = self
60 .inner
61 .get(valid_tx_digest)
62 .ok_or(anyhow::anyhow!("missing transaction digest"))?;
63
64 if &effects.digest() != valid_effects_digest
65 || effects.transaction_digest() != valid_tx_digest
66 || &tx.data().digest() != valid_tx_digest
67 {
68 anyhow::bail!("invalid transaction or effects data");
69 }
70
71 if let Some(valid_events_digest) = effects.events_digest() {
72 if &events.digest() != valid_events_digest {
73 anyhow::bail!("invalid events data");
74 }
75 } else if !events.is_empty() {
76 anyhow::bail!("invalid events data");
77 }
78 validation_digests_queue.remove(valid_tx_digest);
79 }
80 anyhow::ensure!(
81 validation_digests_queue.is_empty(),
82 "the migration data is corrupted"
83 );
84 Ok(())
85 }
86
87 pub fn validate_from_genesis(&self, genesis: &Genesis) -> anyhow::Result<()> {
91 self.validate_from_genesis_components(
92 &genesis.checkpoint(),
93 genesis.checkpoint_contents(),
94 *genesis.transaction().digest(),
95 )
96 }
97
98 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
100 let path = path.as_ref();
101 trace!("reading Migration transaction data from {}", path.display());
102 let read = File::open(path).with_context(|| {
103 format!(
104 "unable to load Migration transaction data from {}",
105 path.display()
106 )
107 })?;
108 bcs::from_reader(BufReader::new(read)).with_context(|| {
109 format!(
110 "unable to parse Migration transaction data from {}",
111 path.display()
112 )
113 })
114 }
115}