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::runtime_decl_for_bundle_producer_election_api::HashT;
9use crate::{
10    ChainId, DomainId, HeaderHashFor, HeaderHashingFor, HeaderNumberFor, OperatorId,
11    OperatorSignature, ProofOfElection,
12};
13#[cfg(not(feature = "std"))]
14use alloc::collections::BTreeSet;
15#[cfg(not(feature = "std"))]
16use alloc::string::String;
17#[cfg(not(feature = "std"))]
18use alloc::vec::Vec;
19use parity_scale_codec::{Decode, Encode};
20use scale_info::TypeInfo;
21use sp_core::H256;
22use sp_runtime::traits::{CheckedAdd, Header as HeaderT, NumberFor, Zero};
23use sp_std::collections::btree_map::BTreeMap;
24use subspace_runtime_primitives::BlockHashFor;
25
26/// Execution Receipt Versions.
27#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
28pub enum ExecutionReceiptVersion {
29    /// V0 execution receipt.
30    V0,
31}
32
33#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)]
34pub struct BlockFees<Balance> {
35    /// The consensus chain storage fee
36    pub consensus_storage_fee: Balance,
37    /// The domain execution fee including the storage and compute fee on domain chain,
38    /// tip, and the XDM reward.
39    pub domain_execution_fee: Balance,
40    /// Burned balances on domain chain
41    pub burned_balance: Balance,
42    /// Rewards for the chain.
43    pub chain_rewards: BTreeMap<ChainId, Balance>,
44}
45
46impl<Balance> BlockFees<Balance>
47where
48    Balance: CheckedAdd + Zero,
49{
50    pub fn new(
51        domain_execution_fee: Balance,
52        consensus_storage_fee: Balance,
53        burned_balance: Balance,
54        chain_rewards: BTreeMap<ChainId, Balance>,
55    ) -> Self {
56        BlockFees {
57            consensus_storage_fee,
58            domain_execution_fee,
59            burned_balance,
60            chain_rewards,
61        }
62    }
63
64    /// Returns the total fees that was collected and burned on the Domain.
65    pub fn total_fees(&self) -> Option<Balance> {
66        let total_chain_reward = self
67            .chain_rewards
68            .values()
69            .try_fold(Zero::zero(), |acc: Balance, cr| acc.checked_add(cr))?;
70        self.consensus_storage_fee
71            .checked_add(&self.domain_execution_fee)
72            .and_then(|balance| balance.checked_add(&self.burned_balance))
73            .and_then(|balance| balance.checked_add(&total_chain_reward))
74    }
75}
76
77/// Type that holds the transfers(in/out) for a given chain.
78#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)]
79pub struct Transfers<Balance> {
80    /// Total transfers that came into the domain.
81    pub transfers_in: BTreeMap<ChainId, Balance>,
82    /// Total transfers that went out of the domain.
83    pub transfers_out: BTreeMap<ChainId, Balance>,
84    /// Total transfers from this domain that were reverted.
85    pub rejected_transfers_claimed: BTreeMap<ChainId, Balance>,
86    /// Total transfers to this domain that were rejected.
87    pub transfers_rejected: BTreeMap<ChainId, Balance>,
88}
89
90impl<Balance> Transfers<Balance> {
91    pub fn is_valid(&self, chain_id: ChainId) -> bool {
92        !self.transfers_rejected.contains_key(&chain_id)
93            && !self.transfers_in.contains_key(&chain_id)
94            && !self.transfers_out.contains_key(&chain_id)
95            && !self.rejected_transfers_claimed.contains_key(&chain_id)
96    }
97}
98
99/// Receipt for execution of Domain Bundle holding the reference to various ER versions.
100#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Copy, Clone)]
101pub enum ExecutionReceiptRef<'a, Number, Hash, DomainNumber, DomainHash, Balance> {
102    V0(&'a ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
103}
104
105impl<'a, Number, Hash, DomainNumber, DomainHash, Balance>
106    ExecutionReceiptRef<'a, Number, Hash, DomainNumber, DomainHash, Balance>
107where
108    Number: Encode + Clone,
109    Hash: Encode + Clone,
110    DomainNumber: Encode + Clone,
111    DomainHash: Encode + Clone,
112    Balance: Encode + Clone,
113{
114    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
115        match self {
116            // for v0, we need hash of inner execution receipt v0
117            ExecutionReceiptRef::V0(receipt) => receipt.hash::<DomainHashing>(),
118        }
119    }
120
121    /// Returns a cloned ER. Used in tests.
122    pub fn to_owned_er(self) -> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance> {
123        match self {
124            ExecutionReceiptRef::V0(er) => ExecutionReceipt::V0(er.clone()),
125        }
126    }
127
128    /// Returns the consensus block number at which ER is derived.
129    pub fn consensus_block_number(&self) -> &Number {
130        match self {
131            ExecutionReceiptRef::V0(er) => &er.consensus_block_number,
132        }
133    }
134
135    /// Return the execution receipt version.
136    pub fn version(&self) -> ExecutionReceiptVersion {
137        match self {
138            ExecutionReceiptRef::V0(_) => ExecutionReceiptVersion::V0,
139        }
140    }
141}
142
143/// Receipt for execution of Domain Bundle holding the mutable reference to various ER versions.
144#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq)]
145pub enum ExecutionReceiptMutRef<'a, Number, Hash, DomainNumber, DomainHash, Balance> {
146    V0(&'a mut ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
147}
148
149/// Receipt for execution of Domain Bundle.
150#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
151pub enum ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance> {
152    V0(ExecutionReceiptV0<Number, Hash, DomainNumber, DomainHash, Balance>),
153}
154
155impl<Number, Hash, DomainNumber, DomainHash, Balance>
156    ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
157where
158    Number: Encode + Zero,
159    Hash: Encode + Default,
160    DomainNumber: Encode + Zero,
161    DomainHash: Clone + Encode + Default + Copy,
162    Balance: Encode + Zero + Default,
163{
164    /// Returns the domain block number.
165    pub fn domain_block_number(&self) -> &DomainNumber {
166        let ExecutionReceipt::V0(receipt) = self;
167        &receipt.domain_block_number
168    }
169
170    /// Returns the domain block hash.
171    pub fn domain_block_hash(&self) -> &DomainHash {
172        let ExecutionReceipt::V0(receipt) = self;
173        &receipt.domain_block_hash
174    }
175
176    /// Returns the final state root of the execution.
177    pub fn final_state_root(&self) -> &DomainHash {
178        let ExecutionReceipt::V0(receipt) = self;
179        &receipt.final_state_root
180    }
181
182    /// Returns the parent's receipt hash.
183    pub fn parent_domain_block_receipt_hash(&self) -> &DomainHash {
184        let ExecutionReceipt::V0(receipt) = self;
185        &receipt.parent_domain_block_receipt_hash
186    }
187
188    /// Returns the consensus block number.
189    pub fn consensus_block_number(&self) -> &Number {
190        let ExecutionReceipt::V0(receipt) = self;
191        &receipt.consensus_block_number
192    }
193
194    /// Returns the consensus block hash.
195    pub fn consensus_block_hash(&self) -> &Hash {
196        let ExecutionReceipt::V0(receipt) = self;
197        &receipt.consensus_block_hash
198    }
199
200    /// Returns the inboxed bundles.
201    pub fn inboxed_bundles(&self) -> &[InboxedBundle<DomainHash>] {
202        let ExecutionReceipt::V0(receipt) = self;
203        &receipt.inboxed_bundles
204    }
205
206    /// Returns the execution traces of the execution.
207    pub fn execution_traces(&self) -> &[DomainHash] {
208        let ExecutionReceipt::V0(receipt) = self;
209        &receipt.execution_trace
210    }
211
212    /// Returns Domain block extrinsics root.
213    pub fn domain_block_extrinsics_root(&self) -> &DomainHash {
214        let ExecutionReceipt::V0(receipt) = self;
215        &receipt.domain_block_extrinsic_root
216    }
217
218    /// Returns the Block fees of the Execution.
219    pub fn block_fees(&self) -> &BlockFees<Balance> {
220        let ExecutionReceipt::V0(receipt) = self;
221        &receipt.block_fees
222    }
223
224    /// Returns the transfers of the Execution.
225    pub fn transfers(&self) -> &Transfers<Balance> {
226        let ExecutionReceipt::V0(receipt) = self;
227        &receipt.transfers
228    }
229
230    /// Returns the valid bundle digests in the ER.
231    pub fn valid_bundle_digests(&self) -> Vec<DomainHash> {
232        let ExecutionReceipt::V0(receipt) = self;
233        receipt.valid_bundle_digests()
234    }
235
236    /// Returns extrinsics roots of each bundle.
237    pub fn bundles_extrinsics_roots(&self) -> Vec<&DomainHash> {
238        let ExecutionReceipt::V0(receipt) = self;
239        receipt.bundles_extrinsics_roots()
240    }
241
242    /// Returns indexes of valid bundles.
243    pub fn valid_bundle_indexes(&self) -> Vec<u32> {
244        let ExecutionReceipt::V0(receipt) = self;
245        receipt.valid_bundle_indexes()
246    }
247
248    /// Returns a valid bundle digest at specific index in the ER.
249    pub fn valid_bundle_digest_at(&self, idx: usize) -> Option<DomainHash> {
250        let ExecutionReceipt::V0(receipt) = self;
251        receipt.valid_bundle_digest_at(idx)
252    }
253
254    /// Sets final state root on ER
255    pub fn set_final_state_root(&mut self, final_state_root: DomainHash) {
256        let ExecutionReceipt::V0(receipt) = self;
257        receipt.final_state_root = final_state_root;
258    }
259
260    /// Sets inboxed bundles on Er
261    pub fn set_inboxed_bundles(&mut self, inboxed_bundles: Vec<InboxedBundle<DomainHash>>) {
262        let ExecutionReceipt::V0(receipt) = self;
263        receipt.inboxed_bundles = inboxed_bundles;
264    }
265
266    /// Sets domain block number on ER
267    pub fn set_domain_block_number(&mut self, domain_block_number: DomainNumber) {
268        let ExecutionReceipt::V0(receipt) = self;
269        receipt.domain_block_number = domain_block_number;
270    }
271
272    /// Sets consensus block number on ER
273    pub fn set_consensus_block_number(&mut self, consensus_block_number: Number) {
274        let ExecutionReceipt::V0(receipt) = self;
275        receipt.consensus_block_number = consensus_block_number;
276    }
277
278    /// Sets consensus block hash on ER
279    pub fn set_consensus_block_hash(&mut self, consensus_block_hash: Hash) {
280        let ExecutionReceipt::V0(receipt) = self;
281        receipt.consensus_block_hash = consensus_block_hash;
282    }
283
284    /// Sets parent receipt hash on ER
285    pub fn set_parent_receipt_hash(&mut self, receipt_hash: DomainHash) {
286        let ExecutionReceipt::V0(receipt) = self;
287        receipt.parent_domain_block_receipt_hash = receipt_hash;
288    }
289
290    pub fn set_execution_traces(&mut self, execution_traces: Vec<DomainHash>) {
291        let ExecutionReceipt::V0(receipt) = self;
292        receipt.execution_trace = execution_traces;
293    }
294
295    pub fn set_execution_trace_root(&mut self, execution_trace_root: H256) {
296        let ExecutionReceipt::V0(receipt) = self;
297        receipt.execution_trace_root = execution_trace_root;
298    }
299
300    /// Returns the Execution receipt as a ref.
301    pub fn as_execution_receipt_ref(
302        &self,
303    ) -> ExecutionReceiptRef<Number, Hash, DomainNumber, DomainHash, Balance> {
304        let ExecutionReceipt::V0(receipt) = self;
305        ExecutionReceiptRef::V0(receipt)
306    }
307
308    /// Returns the hash of this execution receipt.
309    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
310        match self {
311            // for v0,we need hash of inner execution receipt v0
312            ExecutionReceipt::V0(receipt) => receipt.hash::<DomainHashing>(),
313        }
314    }
315
316    /// Return the execution receipt version.
317    pub fn version(&self) -> ExecutionReceiptVersion {
318        match self {
319            ExecutionReceipt::V0(_) => ExecutionReceiptVersion::V0,
320        }
321    }
322
323    /// Returns the genesis ER.
324    pub fn genesis(
325        genesis_state_root: DomainHash,
326        genesis_extrinsic_root: DomainHash,
327        genesis_domain_block_hash: DomainHash,
328        execution_receipt_version: ExecutionReceiptVersion,
329    ) -> Self {
330        match execution_receipt_version {
331            ExecutionReceiptVersion::V0 => ExecutionReceipt::V0(ExecutionReceiptV0 {
332                domain_block_number: Zero::zero(),
333                domain_block_hash: genesis_domain_block_hash,
334                domain_block_extrinsic_root: genesis_extrinsic_root,
335                parent_domain_block_receipt_hash: Default::default(),
336                consensus_block_hash: Default::default(),
337                consensus_block_number: Zero::zero(),
338                inboxed_bundles: Vec::new(),
339                final_state_root: genesis_state_root,
340                execution_trace: sp_std::vec![genesis_state_root],
341                execution_trace_root: Default::default(),
342                block_fees: Default::default(),
343                transfers: Default::default(),
344            }),
345        }
346    }
347
348    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
349    pub fn dummy<DomainHashing>(
350        consensus_block_number: Number,
351        consensus_block_hash: Hash,
352        domain_block_number: DomainNumber,
353        parent_domain_block_receipt_hash: DomainHash,
354    ) -> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
355    where
356        DomainHashing: HashT<Output = DomainHash>,
357    {
358        let execution_trace = sp_std::vec![Default::default(), Default::default()];
359        let execution_trace_root = {
360            let trace: Vec<[u8; 32]> = execution_trace
361                .iter()
362                .map(|r: &DomainHash| r.encode().try_into().expect("H256 must fit into [u8; 32]"))
363                .collect();
364            crate::merkle_tree::MerkleTree::from_leaves(trace.as_slice())
365                .root()
366                .expect("Compute merkle root of trace should success")
367                .into()
368        };
369        ExecutionReceipt::V0(ExecutionReceiptV0 {
370            domain_block_number,
371            domain_block_hash: Default::default(),
372            domain_block_extrinsic_root: Default::default(),
373            parent_domain_block_receipt_hash,
374            consensus_block_number,
375            consensus_block_hash,
376            inboxed_bundles: sp_std::vec![InboxedBundle::dummy(Default::default())],
377            final_state_root: Default::default(),
378            execution_trace,
379            execution_trace_root,
380            block_fees: Default::default(),
381            transfers: Default::default(),
382        })
383    }
384}
385
386/// Singleton receipt submit along when there is a gap between `domain_best_number`
387/// and `HeadReceiptNumber`
388#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
389pub struct SingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
390    /// Proof of receipt producer election.
391    pub proof_of_election: ProofOfElection,
392    /// The receipt to submit
393    pub receipt: ExecutionReceipt<
394        Number,
395        Hash,
396        HeaderNumberFor<DomainHeader>,
397        HeaderHashFor<DomainHeader>,
398        Balance,
399    >,
400}
401
402impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
403    SingletonReceipt<Number, Hash, DomainHeader, Balance>
404{
405    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
406        HeaderHashingFor::<DomainHeader>::hash_of(&self)
407    }
408}
409
410/// Singleton receipt with operator signature.
411#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
412pub struct SealedSingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
413    /// A collection of the receipt.
414    pub singleton_receipt: SingletonReceipt<Number, Hash, DomainHeader, Balance>,
415    /// Signature of the receipt bundle.
416    pub signature: OperatorSignature,
417}
418
419impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
420    SealedSingletonReceipt<Number, Hash, DomainHeader, Balance>
421{
422    /// Returns the `domain_id`
423    pub fn domain_id(&self) -> DomainId {
424        self.singleton_receipt.proof_of_election.domain_id
425    }
426
427    /// Return the `operator_id`
428    pub fn operator_id(&self) -> OperatorId {
429        self.singleton_receipt.proof_of_election.operator_id
430    }
431
432    /// Return the `slot_number` of the `proof_of_election`
433    pub fn slot_number(&self) -> u64 {
434        self.singleton_receipt.proof_of_election.slot_number
435    }
436
437    /// Return the receipt
438    pub fn receipt(
439        &self,
440    ) -> &ExecutionReceipt<
441        Number,
442        Hash,
443        HeaderNumberFor<DomainHeader>,
444        HeaderHashFor<DomainHeader>,
445        Balance,
446    > {
447        &self.singleton_receipt.receipt
448    }
449
450    /// Consume this `SealedSingletonReceipt` and return the receipt
451    pub fn into_receipt(
452        self,
453    ) -> ExecutionReceipt<
454        Number,
455        Hash,
456        HeaderNumberFor<DomainHeader>,
457        HeaderHashFor<DomainHeader>,
458        Balance,
459    > {
460        self.singleton_receipt.receipt
461    }
462
463    /// Returns the hash of `SingletonReceipt`
464    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
465        HeaderHashingFor::<DomainHeader>::hash_of(&self.singleton_receipt)
466    }
467
468    /// Return the encode size of `SealedSingletonReceipt`
469    pub fn size(&self) -> u32 {
470        self.encoded_size() as u32
471    }
472}
473
474pub type ExecutionReceiptFor<DomainHeader, CBlock, Balance> = ExecutionReceipt<
475    NumberFor<CBlock>,
476    BlockHashFor<CBlock>,
477    <DomainHeader as HeaderT>::Number,
478    <DomainHeader as HeaderT>::Hash,
479    Balance,
480>;