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>;