sp_domains/
execution_receipt.rs

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/// Execution Receipt Versions.
26#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
27pub enum ExecutionReceiptVersion {
28    /// V0 execution receipt.
29    V0,
30}
31
32#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)]
33pub struct BlockFees<Balance> {
34    /// The consensus chain storage fee
35    pub consensus_storage_fee: Balance,
36    /// The domain execution fee including the storage and compute fee on domain chain,
37    /// tip, and the XDM reward.
38    pub domain_execution_fee: Balance,
39    /// Burned balances on domain chain
40    pub burned_balance: Balance,
41    /// Rewards for the chain.
42    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    /// Returns the total fees that was collected and burned on the Domain.
64    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/// Type that holds the transfers(in/out) for a given chain.
77#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)]
78pub struct Transfers<Balance> {
79    /// Total transfers that came into the domain.
80    pub transfers_in: BTreeMap<ChainId, Balance>,
81    /// Total transfers that went out of the domain.
82    pub transfers_out: BTreeMap<ChainId, Balance>,
83    /// Total transfers from this domain that were reverted.
84    pub rejected_transfers_claimed: BTreeMap<ChainId, Balance>,
85    /// Total transfers to this domain that were rejected.
86    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/// Receipt for execution of Domain Bundle holding the reference to various ER versions.
99#[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            // for v0, we need hash of inner execution receipt v0
116            ExecutionReceiptRef::V0(receipt) => receipt.hash::<DomainHashing>(),
117        }
118    }
119
120    /// Returns a cloned ER. Used in tests.
121    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    /// Returns the consensus block number at which ER is derived.
128    pub fn consensus_block_number(&self) -> &Number {
129        match self {
130            ExecutionReceiptRef::V0(er) => &er.consensus_block_number,
131        }
132    }
133
134    /// Return the execution receipt version.
135    pub fn version(&self) -> ExecutionReceiptVersion {
136        match self {
137            ExecutionReceiptRef::V0(_) => ExecutionReceiptVersion::V0,
138        }
139    }
140}
141
142/// Receipt for execution of Domain Bundle holding the mutable reference to various ER versions.
143#[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/// Receipt for execution of Domain Bundle.
149#[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    /// Returns the domain block number.
164    pub fn domain_block_number(&self) -> &DomainNumber {
165        let ExecutionReceipt::V0(receipt) = self;
166        &receipt.domain_block_number
167    }
168
169    /// Returns the domain block hash.
170    pub fn domain_block_hash(&self) -> &DomainHash {
171        let ExecutionReceipt::V0(receipt) = self;
172        &receipt.domain_block_hash
173    }
174
175    /// Returns the final state root of the execution.
176    pub fn final_state_root(&self) -> &DomainHash {
177        let ExecutionReceipt::V0(receipt) = self;
178        &receipt.final_state_root
179    }
180
181    /// Returns the parent's receipt hash.
182    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    /// Returns the consensus block number.
188    pub fn consensus_block_number(&self) -> &Number {
189        let ExecutionReceipt::V0(receipt) = self;
190        &receipt.consensus_block_number
191    }
192
193    /// Returns the consensus block hash.
194    pub fn consensus_block_hash(&self) -> &Hash {
195        let ExecutionReceipt::V0(receipt) = self;
196        &receipt.consensus_block_hash
197    }
198
199    /// Returns the inboxed bundles.
200    pub fn inboxed_bundles(&self) -> &[InboxedBundle<DomainHash>] {
201        let ExecutionReceipt::V0(receipt) = self;
202        &receipt.inboxed_bundles
203    }
204
205    /// Returns the execution traces of the execution.
206    pub fn execution_traces(&self) -> &[DomainHash] {
207        let ExecutionReceipt::V0(receipt) = self;
208        &receipt.execution_trace
209    }
210
211    /// Returns Domain block extrinsics root.
212    pub fn domain_block_extrinsics_root(&self) -> &DomainHash {
213        let ExecutionReceipt::V0(receipt) = self;
214        &receipt.domain_block_extrinsic_root
215    }
216
217    /// Returns the Block fees of the Execution.
218    pub fn block_fees(&self) -> &BlockFees<Balance> {
219        let ExecutionReceipt::V0(receipt) = self;
220        &receipt.block_fees
221    }
222
223    /// Returns the transfers of the Execution.
224    pub fn transfers(&self) -> &Transfers<Balance> {
225        let ExecutionReceipt::V0(receipt) = self;
226        &receipt.transfers
227    }
228
229    /// Returns the valid bundle digests in the ER.
230    pub fn valid_bundle_digests(&self) -> Vec<DomainHash> {
231        let ExecutionReceipt::V0(receipt) = self;
232        receipt.valid_bundle_digests()
233    }
234
235    /// Returns extrinsics roots of each bundle.
236    pub fn bundles_extrinsics_roots(&self) -> Vec<&DomainHash> {
237        let ExecutionReceipt::V0(receipt) = self;
238        receipt.bundles_extrinsics_roots()
239    }
240
241    /// Returns indexes of valid bundles.
242    pub fn valid_bundle_indexes(&self) -> Vec<u32> {
243        let ExecutionReceipt::V0(receipt) = self;
244        receipt.valid_bundle_indexes()
245    }
246
247    /// Returns a valid bundle digest at specific index in the ER.
248    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    /// Sets final state root on ER
254    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    /// Sets inboxed bundles on Er
260    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    /// Sets domain block number on ER
266    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    /// Sets consensus block number on ER
272    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    /// Sets consensus block hash on ER
278    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    /// Sets parent receipt hash on ER
284    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    /// Returns the Execution receipt as a ref.
300    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    /// Returns the hash of this execution receipt.
308    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
309        match self {
310            // for v0,we need hash of inner execution receipt v0
311            ExecutionReceipt::V0(receipt) => receipt.hash::<DomainHashing>(),
312        }
313    }
314
315    /// Return the execution receipt version.
316    pub fn version(&self) -> ExecutionReceiptVersion {
317        match self {
318            ExecutionReceipt::V0(_) => ExecutionReceiptVersion::V0,
319        }
320    }
321
322    /// Returns the genesis ER.
323    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/// Singleton receipt submit along when there is a gap between `domain_best_number`
386/// and `HeadReceiptNumber`
387#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
388pub struct SingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
389    /// Proof of receipt producer election.
390    pub proof_of_election: ProofOfElection,
391    /// The receipt to submit
392    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/// Singleton receipt with operator signature.
410#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
411pub struct SealedSingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
412    /// A collection of the receipt.
413    pub singleton_receipt: SingletonReceipt<Number, Hash, DomainHeader, Balance>,
414    /// Signature of the receipt bundle.
415    pub signature: OperatorSignature,
416}
417
418impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
419    SealedSingletonReceipt<Number, Hash, DomainHeader, Balance>
420{
421    /// Returns the `domain_id`
422    pub fn domain_id(&self) -> DomainId {
423        self.singleton_receipt.proof_of_election.domain_id
424    }
425
426    /// Return the `operator_id`
427    pub fn operator_id(&self) -> OperatorId {
428        self.singleton_receipt.proof_of_election.operator_id
429    }
430
431    /// Return the `slot_number` of the `proof_of_election`
432    pub fn slot_number(&self) -> u64 {
433        self.singleton_receipt.proof_of_election.slot_number
434    }
435
436    /// Return the receipt
437    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    /// Consume this `SealedSingletonReceipt` and return the receipt
450    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    /// Returns the hash of `SingletonReceipt`
463    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
464        HeaderHashingFor::<DomainHeader>::hash_of(&self.singleton_receipt)
465    }
466
467    /// Return the encode size of `SealedSingletonReceipt`
468    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>;