iota_light_client/
construct.rs1use anyhow::anyhow;
6use iota_rest_api::{CheckpointData, CheckpointTransaction};
7use iota_types::effects::TransactionEffectsAPI;
8
9use crate::proof::{Proof, ProofTarget, TransactionProof};
10
11pub fn construct_proof(targets: ProofTarget, data: &CheckpointData) -> anyhow::Result<Proof> {
18 let checkpoint_summary = data.checkpoint_summary.clone();
19 let mut this_proof = Proof {
20 targets,
21 checkpoint_summary,
22 contents_proof: None,
23 };
24
25 if let Some(committee) = &this_proof.targets.committee {
28 if this_proof.checkpoint_summary.epoch() + 1 != committee.epoch {
30 return Err(anyhow!("Epoch mismatch between checkpoint and committee"));
31 }
32
33 if this_proof.checkpoint_summary.end_of_epoch_data.is_none() {
35 return Err(anyhow!("Expected end of epoch checkpoint"));
36 }
37 }
38
39 let object_tx = this_proof
43 .targets
44 .objects
45 .iter()
46 .map(|(_, o)| o.previous_transaction);
47 let event_tx = this_proof
48 .targets
49 .events
50 .iter()
51 .map(|(eid, _)| eid.tx_digest);
52 let mut all_tx = object_tx.chain(event_tx);
53
54 let target_tx_id = if let Some(first_tx) = all_tx.next() {
56 first_tx
57 } else {
58 return Ok(this_proof);
60 };
61
62 if !all_tx.all(|tx| tx == target_tx_id) {
64 return Err(anyhow!("All targets must refer to the same transaction"));
65 }
66
67 let tx = data
69 .transactions
70 .iter()
71 .find(|t| t.effects.transaction_digest() == &target_tx_id)
72 .ok_or(anyhow!("Transaction not found in checkpoint data"))?
73 .clone();
74
75 let CheckpointTransaction {
76 transaction,
77 effects,
78 events,
79 ..
80 } = tx;
81
82 this_proof.contents_proof = Some(TransactionProof {
84 checkpoint_contents: data.checkpoint_contents.clone(),
85 transaction,
86 effects,
87 events,
88 });
89
90 Ok(this_proof)
95}