1pub mod execution_receipt_v0;
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6use crate::bundle::InboxedBundle;
7use crate::execution_receipt::execution_receipt_v0::ExecutionReceiptV0;
8use crate::{
9    ChainId, DomainId, HeaderHashFor, HeaderHashingFor, HeaderNumberFor, OperatorId,
10    OperatorSignature, ProofOfElection,
11};
12#[cfg(not(feature = "std"))]
13use alloc::collections::BTreeSet;
14#[cfg(not(feature = "std"))]
15use alloc::string::String;
16#[cfg(not(feature = "std"))]
17use alloc::vec::Vec;
18use parity_scale_codec::{Decode, Encode};
19use scale_info::TypeInfo;
20use sp_core::H256;
21use sp_runtime::traits::{CheckedAdd, Hash as HashT, Header as HeaderT, NumberFor, Zero};
22use sp_std::collections::btree_map::BTreeMap;
23use subspace_runtime_primitives::BlockHashFor;
24
25#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
27pub enum ExecutionReceiptVersion {
28    V0,
30}
31
32#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)]
33pub struct BlockFees<Balance> {
34    pub consensus_storage_fee: Balance,
36    pub domain_execution_fee: Balance,
39    pub burned_balance: Balance,
41    pub chain_rewards: BTreeMap<ChainId, Balance>,
43}
44
45impl<Balance> BlockFees<Balance>
46where
47    Balance: CheckedAdd + Zero,
48{
49    pub fn new(
50        domain_execution_fee: Balance,
51        consensus_storage_fee: Balance,
52        burned_balance: Balance,
53        chain_rewards: BTreeMap<ChainId, Balance>,
54    ) -> Self {
55        BlockFees {
56            consensus_storage_fee,
57            domain_execution_fee,
58            burned_balance,
59            chain_rewards,
60        }
61    }
62
63    pub fn total_fees(&self) -> Option<Balance> {
65        let total_chain_reward = self
66            .chain_rewards
67            .values()
68            .try_fold(Zero::zero(), |acc: Balance, cr| acc.checked_add(cr))?;
69        self.consensus_storage_fee
70            .checked_add(&self.domain_execution_fee)
71            .and_then(|balance| balance.checked_add(&self.burned_balance))
72            .and_then(|balance| balance.checked_add(&total_chain_reward))
73    }
74}
75
76#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)]
78pub struct Transfers<Balance> {
79    pub transfers_in: BTreeMap<ChainId, Balance>,
81    pub transfers_out: BTreeMap<ChainId, Balance>,
83    pub rejected_transfers_claimed: BTreeMap<ChainId, Balance>,
85    pub transfers_rejected: BTreeMap<ChainId, Balance>,
87}
88
89impl<Balance> Transfers<Balance> {
90    pub fn is_valid(&self, chain_id: ChainId) -> bool {
91        !self.transfers_rejected.contains_key(&chain_id)
92            && !self.transfers_in.contains_key(&chain_id)
93            && !self.transfers_out.contains_key(&chain_id)
94            && !self.rejected_transfers_claimed.contains_key(&chain_id)
95    }
96}
97
98#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Copy, Clone)]
100pub enum ExecutionReceiptRef<'a, Number, Hash, DomainNumber, DomainHash, Balance> {
101    V0(&'a ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
102}
103
104impl<'a, Number, Hash, DomainNumber, DomainHash, Balance>
105    ExecutionReceiptRef<'a, Number, Hash, DomainNumber, DomainHash, Balance>
106where
107    Number: Encode + Clone,
108    Hash: Encode + Clone,
109    DomainNumber: Encode + Clone,
110    DomainHash: Encode + Clone,
111    Balance: Encode + Clone,
112{
113    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
114        match self {
115            ExecutionReceiptRef::V0(receipt) => receipt.hash::<DomainHashing>(),
117        }
118    }
119
120    pub fn to_owned_er(self) -> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance> {
122        match self {
123            ExecutionReceiptRef::V0(er) => ExecutionReceipt::V0(er.clone()),
124        }
125    }
126
127    pub fn consensus_block_number(&self) -> &Number {
129        match self {
130            ExecutionReceiptRef::V0(er) => &er.consensus_block_number,
131        }
132    }
133
134    pub fn version(&self) -> ExecutionReceiptVersion {
136        match self {
137            ExecutionReceiptRef::V0(_) => ExecutionReceiptVersion::V0,
138        }
139    }
140}
141
142#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq)]
144pub enum ExecutionReceiptMutRef<'a, Number, Hash, DomainNumber, DomainHash, Balance> {
145    V0(&'a mut ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
146}
147
148#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
150pub enum ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance> {
151    V0(ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
152}
153
154impl<Number, Hash, DomainNumber, DomainHash, Balance>
155    ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
156where
157    Number: Encode + Zero,
158    Hash: Encode + Default,
159    DomainNumber: Encode + Zero,
160    DomainHash: Clone + Encode + Default + Copy,
161    Balance: Encode + Zero + Default,
162{
163    pub fn domain_block_number(&self) -> &DomainNumber {
165        let ExecutionReceipt::V0(receipt) = self;
166        &receipt.domain_block_number
167    }
168
169    pub fn domain_block_hash(&self) -> &DomainHash {
171        let ExecutionReceipt::V0(receipt) = self;
172        &receipt.domain_block_hash
173    }
174
175    pub fn final_state_root(&self) -> &DomainHash {
177        let ExecutionReceipt::V0(receipt) = self;
178        &receipt.final_state_root
179    }
180
181    pub fn parent_domain_block_receipt_hash(&self) -> &DomainHash {
183        let ExecutionReceipt::V0(receipt) = self;
184        &receipt.parent_domain_block_receipt_hash
185    }
186
187    pub fn consensus_block_number(&self) -> &Number {
189        let ExecutionReceipt::V0(receipt) = self;
190        &receipt.consensus_block_number
191    }
192
193    pub fn consensus_block_hash(&self) -> &Hash {
195        let ExecutionReceipt::V0(receipt) = self;
196        &receipt.consensus_block_hash
197    }
198
199    pub fn inboxed_bundles(&self) -> &[InboxedBundle<DomainHash>] {
201        let ExecutionReceipt::V0(receipt) = self;
202        &receipt.inboxed_bundles
203    }
204
205    pub fn execution_traces(&self) -> &[DomainHash] {
207        let ExecutionReceipt::V0(receipt) = self;
208        &receipt.execution_trace
209    }
210
211    pub fn domain_block_extrinsics_root(&self) -> &DomainHash {
213        let ExecutionReceipt::V0(receipt) = self;
214        &receipt.domain_block_extrinsic_root
215    }
216
217    pub fn block_fees(&self) -> &BlockFees<Balance> {
219        let ExecutionReceipt::V0(receipt) = self;
220        &receipt.block_fees
221    }
222
223    pub fn transfers(&self) -> &Transfers<Balance> {
225        let ExecutionReceipt::V0(receipt) = self;
226        &receipt.transfers
227    }
228
229    pub fn valid_bundle_digests(&self) -> Vec<DomainHash> {
231        let ExecutionReceipt::V0(receipt) = self;
232        receipt.valid_bundle_digests()
233    }
234
235    pub fn bundles_extrinsics_roots(&self) -> Vec<&DomainHash> {
237        let ExecutionReceipt::V0(receipt) = self;
238        receipt.bundles_extrinsics_roots()
239    }
240
241    pub fn valid_bundle_indexes(&self) -> Vec<u32> {
243        let ExecutionReceipt::V0(receipt) = self;
244        receipt.valid_bundle_indexes()
245    }
246
247    pub fn valid_bundle_digest_at(&self, idx: usize) -> Option<DomainHash> {
249        let ExecutionReceipt::V0(receipt) = self;
250        receipt.valid_bundle_digest_at(idx)
251    }
252
253    pub fn set_final_state_root(&mut self, final_state_root: DomainHash) {
255        let ExecutionReceipt::V0(receipt) = self;
256        receipt.final_state_root = final_state_root;
257    }
258
259    pub fn set_inboxed_bundles(&mut self, inboxed_bundles: Vec<InboxedBundle<DomainHash>>) {
261        let ExecutionReceipt::V0(receipt) = self;
262        receipt.inboxed_bundles = inboxed_bundles;
263    }
264
265    pub fn set_domain_block_number(&mut self, domain_block_number: DomainNumber) {
267        let ExecutionReceipt::V0(receipt) = self;
268        receipt.domain_block_number = domain_block_number;
269    }
270
271    pub fn set_consensus_block_number(&mut self, consensus_block_number: Number) {
273        let ExecutionReceipt::V0(receipt) = self;
274        receipt.consensus_block_number = consensus_block_number;
275    }
276
277    pub fn set_consensus_block_hash(&mut self, consensus_block_hash: Hash) {
279        let ExecutionReceipt::V0(receipt) = self;
280        receipt.consensus_block_hash = consensus_block_hash;
281    }
282
283    pub fn set_parent_receipt_hash(&mut self, receipt_hash: DomainHash) {
285        let ExecutionReceipt::V0(receipt) = self;
286        receipt.parent_domain_block_receipt_hash = receipt_hash;
287    }
288
289    pub fn set_execution_traces(&mut self, execution_traces: Vec<DomainHash>) {
290        let ExecutionReceipt::V0(receipt) = self;
291        receipt.execution_trace = execution_traces;
292    }
293
294    pub fn set_execution_trace_root(&mut self, execution_trace_root: H256) {
295        let ExecutionReceipt::V0(receipt) = self;
296        receipt.execution_trace_root = execution_trace_root;
297    }
298
299    pub fn as_execution_receipt_ref(
301        &self,
302    ) -> ExecutionReceiptRef<Number, Hash, DomainNumber, DomainHash, Balance> {
303        let ExecutionReceipt::V0(receipt) = self;
304        ExecutionReceiptRef::V0(receipt)
305    }
306
307    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
309        match self {
310            ExecutionReceipt::V0(receipt) => receipt.hash::<DomainHashing>(),
312        }
313    }
314
315    pub fn version(&self) -> ExecutionReceiptVersion {
317        match self {
318            ExecutionReceipt::V0(_) => ExecutionReceiptVersion::V0,
319        }
320    }
321
322    pub fn genesis(
324        genesis_state_root: DomainHash,
325        genesis_extrinsic_root: DomainHash,
326        genesis_domain_block_hash: DomainHash,
327        execution_receipt_version: ExecutionReceiptVersion,
328    ) -> Self {
329        match execution_receipt_version {
330            ExecutionReceiptVersion::V0 => ExecutionReceipt::V0(ExecutionReceiptV0 {
331                domain_block_number: Zero::zero(),
332                domain_block_hash: genesis_domain_block_hash,
333                domain_block_extrinsic_root: genesis_extrinsic_root,
334                parent_domain_block_receipt_hash: Default::default(),
335                consensus_block_hash: Default::default(),
336                consensus_block_number: Zero::zero(),
337                inboxed_bundles: Vec::new(),
338                final_state_root: genesis_state_root,
339                execution_trace: sp_std::vec![genesis_state_root],
340                execution_trace_root: Default::default(),
341                block_fees: Default::default(),
342                transfers: Default::default(),
343            }),
344        }
345    }
346
347    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
348    pub fn dummy<DomainHashing>(
349        consensus_block_number: Number,
350        consensus_block_hash: Hash,
351        domain_block_number: DomainNumber,
352        parent_domain_block_receipt_hash: DomainHash,
353    ) -> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
354    where
355        DomainHashing: HashT<Output = DomainHash>,
356    {
357        let execution_trace = sp_std::vec![Default::default(), Default::default()];
358        let execution_trace_root = {
359            let trace: Vec<[u8; 32]> = execution_trace
360                .iter()
361                .map(|r: &DomainHash| r.encode().try_into().expect("H256 must fit into [u8; 32]"))
362                .collect();
363            crate::merkle_tree::MerkleTree::from_leaves(trace.as_slice())
364                .root()
365                .expect("Compute merkle root of trace should success")
366                .into()
367        };
368        ExecutionReceipt::V0(ExecutionReceiptV0 {
369            domain_block_number,
370            domain_block_hash: Default::default(),
371            domain_block_extrinsic_root: Default::default(),
372            parent_domain_block_receipt_hash,
373            consensus_block_number,
374            consensus_block_hash,
375            inboxed_bundles: sp_std::vec![InboxedBundle::dummy(Default::default())],
376            final_state_root: Default::default(),
377            execution_trace,
378            execution_trace_root,
379            block_fees: Default::default(),
380            transfers: Default::default(),
381        })
382    }
383}
384
385#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
388pub struct SingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
389    pub proof_of_election: ProofOfElection,
391    pub receipt: ExecutionReceipt<
393        Number,
394        Hash,
395        HeaderNumberFor<DomainHeader>,
396        HeaderHashFor<DomainHeader>,
397        Balance,
398    >,
399}
400
401impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
402    SingletonReceipt<Number, Hash, DomainHeader, Balance>
403{
404    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
405        HeaderHashingFor::<DomainHeader>::hash_of(&self)
406    }
407}
408
409#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
411pub struct SealedSingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
412    pub singleton_receipt: SingletonReceipt<Number, Hash, DomainHeader, Balance>,
414    pub signature: OperatorSignature,
416}
417
418impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
419    SealedSingletonReceipt<Number, Hash, DomainHeader, Balance>
420{
421    pub fn domain_id(&self) -> DomainId {
423        self.singleton_receipt.proof_of_election.domain_id
424    }
425
426    pub fn operator_id(&self) -> OperatorId {
428        self.singleton_receipt.proof_of_election.operator_id
429    }
430
431    pub fn slot_number(&self) -> u64 {
433        self.singleton_receipt.proof_of_election.slot_number
434    }
435
436    pub fn receipt(
438        &self,
439    ) -> &ExecutionReceipt<
440        Number,
441        Hash,
442        HeaderNumberFor<DomainHeader>,
443        HeaderHashFor<DomainHeader>,
444        Balance,
445    > {
446        &self.singleton_receipt.receipt
447    }
448
449    pub fn into_receipt(
451        self,
452    ) -> ExecutionReceipt<
453        Number,
454        Hash,
455        HeaderNumberFor<DomainHeader>,
456        HeaderHashFor<DomainHeader>,
457        Balance,
458    > {
459        self.singleton_receipt.receipt
460    }
461
462    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
464        HeaderHashingFor::<DomainHeader>::hash_of(&self.singleton_receipt)
465    }
466
467    pub fn size(&self) -> u32 {
469        self.encoded_size() as u32
470    }
471}
472
473pub type ExecutionReceiptFor<DomainHeader, CBlock, Balance> = ExecutionReceipt<
474    NumberFor<CBlock>,
475    BlockHashFor<CBlock>,
476    <DomainHeader as HeaderT>::Number,
477    <DomainHeader as HeaderT>::Hash,
478    Balance,
479>;