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