jam_std_common/
extrinsic.rs

1use crate::{
2	hash_encoded, header::OffendersMark, AvailabilityAssurance, CoreCount, EpochIndex, ErasureRoot,
3	MaxTicketsPerBlock, TicketEnvelope, MAX_REPORT_ELECTIVE_DATA, VALS_PER_CORE,
4};
5use bounded_collections::ConstU32;
6use jam_types::{
7	AuthOutput, AuthorizerHash, BoundedVec, CoreIndex, ExtrinsicHash, FixedVec, MaxWorkItems,
8	RefineContext, SegmentTreeRoot, ServiceId, Slot, UnsignedGas, ValIndex, ValSuperMajority,
9	VecMap, WorkPackageHash, WorkReportHash, WorkResult,
10};
11use scale::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};
12
13pub const MAX_VERDICTS_COUNT: usize = 16;
14pub const MAX_OFFENSES_COUNT: usize = 16;
15
16/// A bunch of preimages.
17pub type PreimagesXt = Vec<Preimage>;
18/// A collection of ticket envelopes.
19pub type TicketsXt = BoundedVec<TicketEnvelope, MaxTicketsPerBlock>;
20/// A bunch of assurances.
21pub type AssurancesXt = BoundedVec<AvailabilityAssurance, jam_types::ValCount>;
22/// A bunch of guarantees.
23pub type GuaranteesXt = BoundedVec<ReportGuarantee, CoreCount>;
24/// The disputes extrinsic.
25#[derive(Clone, Encode, Decode, Debug, Default)]
26pub struct DisputesXt {
27	/// Disputed work reports together with their judgements.
28	pub verdicts: BoundedVec<Verdict, ConstU32<{ MAX_VERDICTS_COUNT as u32 }>>,
29	/// Validators who guaranteed a work report subsequently determined to be invalid.
30	pub culprits: BoundedVec<CulpritProof, ConstU32<{ MAX_OFFENSES_COUNT as u32 }>>,
31	/// Validators who audited a work report in conflict with the final dispute resolution.
32	pub faults: BoundedVec<FaultProof, ConstU32<{ MAX_OFFENSES_COUNT as u32 }>>,
33}
34
35impl DisputesXt {
36	pub fn offenders_mark(&self) -> OffendersMark {
37		let offenders: Vec<_> = self
38			.culprits
39			.iter()
40			.map(|v| v.key)
41			.chain(self.faults.iter().map(|v| v.key))
42			.collect();
43		offenders.try_into().expect("OffendersMark bounds equal culprits + faults")
44	}
45	pub fn implies_offenders_mark(&self, offenders_mark: &OffendersMark) -> bool {
46		self.culprits
47			.iter()
48			.map(|v| v.key)
49			.chain(self.faults.iter().map(|v| v.key))
50			.zip(offenders_mark.iter())
51			.all(|(a, b)| a == *b)
52	}
53}
54
55/// Judgements coming from a supermajority of either the active validators
56/// set (κ) or the previous epoch's validator set (λ).
57pub type VerdictVotes = FixedVec<Judgement, ValSuperMajority>;
58
59/// Collection of judgements for a given target work report.
60#[derive(Clone, Encode, Decode, Debug)]
61pub struct Verdict {
62	/// Target work report.
63	pub target: WorkReportHash,
64	/// Epoch index of the prior state or one less depending on the key set
65	/// used to sign the votes.
66	pub age: EpochIndex,
67	/// Verdict votes collection.
68	pub votes: VerdictVotes,
69}
70
71#[derive(Clone, Encode, Decode, Debug)]
72pub struct Judgement {
73	pub vote: bool,
74	pub index: ValIndex,
75	pub signature: super::ed25519::Signature,
76}
77
78#[derive(Clone, Encode, Decode, Debug)]
79pub struct CulpritProof {
80	pub report_hash: WorkReportHash,
81	pub key: super::ed25519::Public,
82	pub signature: super::ed25519::Signature,
83}
84
85#[derive(Clone, Encode, Decode, Debug)]
86pub struct FaultProof {
87	pub report_hash: WorkReportHash,
88	pub vote: bool,
89	pub key: super::ed25519::Public,
90	pub signature: super::ed25519::Signature,
91}
92
93#[derive(Clone, Encode, Decode, Debug, Default)]
94pub struct Extrinsic {
95	pub tickets: TicketsXt,
96	pub preimages: PreimagesXt,
97	pub guarantees: GuaranteesXt,
98	pub assurances: AssurancesXt,
99	pub disputes: DisputesXt,
100}
101
102impl Extrinsic {
103	pub fn guarantees_prehashed(&self) -> Vec<(WorkReportHash, Slot, &GuaranteeSignatures)> {
104		self.guarantees
105			.iter()
106			.map(|r| (r.report.hash(), r.slot, &r.signatures))
107			.collect()
108	}
109
110	pub fn hash(&self) -> ExtrinsicHash {
111		let tickets_hash = hash_encoded(&self.tickets);
112		let disputes_hash = hash_encoded(&self.disputes);
113		let preimages_hash = hash_encoded(&self.preimages);
114		let assurances_hash = hash_encoded(&self.assurances);
115		let guarantees_hash = hash_encoded(&self.guarantees_prehashed());
116		let top = (tickets_hash, preimages_hash, guarantees_hash, assurances_hash, disputes_hash);
117		hash_encoded(&top).into()
118	}
119}
120
121#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord)]
122pub struct Preimage {
123	pub requester: ServiceId,
124	pub blob: Vec<u8>,
125}
126impl Preimage {
127	pub fn new(requester: ServiceId, blob: impl Into<Vec<u8>>) -> Self {
128		Self { requester, blob: blob.into() }
129	}
130}
131
132/// A Work Report along with a guarantee of its correctness from staked sources.
133#[derive(Clone, Encode, Decode, Debug, MaxEncodedLen)]
134#[cfg_attr(test, derive(PartialEq, Eq))]
135pub struct ReportGuarantee {
136	/// The Work Report which is being attested.
137	pub report: WorkReport,
138	/// The slot following production of the report. The validator set and hence the meaning of the
139	/// validator indices in `signatures` is inferred from this. The indices must be a subset of
140	/// those assigned to `report.core_index` during the slot.
141	pub slot: Slot,
142	/// The signatures from the guarantors whose message is the hash of the `report`.
143	/// The order of the signatures is the same order as the validators appear in
144	/// the epochal validator set.
145	pub signatures: GuaranteeSignatures,
146}
147
148pub type GuaranteeSignatures = BoundedVec<ValSignature, ConstU32<{ VALS_PER_CORE as u32 }>>;
149
150#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
151pub struct ValSignature {
152	pub val_index: ValIndex,
153	pub signature: super::ed25519::Signature,
154}
155
156impl ConstEncodedLen for ValSignature {}
157
158/// Secure reference to a Work Package.
159#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
160pub struct WorkPackageSpec {
161	/// The hash of the Work Package.
162	pub hash: WorkPackageHash,
163	/// The length in bytes of Work Bundle.
164	pub len: u32,
165	/// The erasure root of the Work Bundle and export-segment pieces.
166	pub erasure_root: ErasureRoot,
167	/// The segment root of the Work Package.
168	pub exports_root: SegmentTreeRoot,
169	/// The number of segments exported by the Work Package.
170	pub exports_count: u16,
171}
172
173impl WorkPackageSpec {
174	pub fn wp_srl(&self) -> (WorkPackageHash, SegmentTreeRoot) {
175		(self.hash, self.exports_root)
176	}
177}
178
179/// Execution report of a Work Package, mainly comprising the Results from the Refinement
180/// of its Work Items.
181#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
182pub struct WorkReport {
183	/// The specification of the underlying Work Package.
184	pub package_spec: WorkPackageSpec,
185	/// The context of the underlying Work Package.
186	pub context: RefineContext,
187	/// The Core index under which the Work Package was Refined to generate the Report.
188	pub core_index: CoreIndex,
189	/// The authorizer under which this Work Package got executed. For the Work Package to be
190	/// validly reported, this must appear in the Core's authorizer queue at the time of reporting.
191	pub authorizer_hash: AuthorizerHash,
192	/// The output of the authorizer under which this Work Package got executed.
193	pub auth_output: AuthOutput,
194	/// The segment-root lookup dictionary.
195	pub sr_lookup: VecMap<WorkPackageHash, SegmentTreeRoot>,
196	/// The results of the evaluation of the Items in the underlying Work Package.
197	pub results: BoundedVec<WorkResult, MaxWorkItems>,
198	/// The amount of gas actually used by the IsAuthorized call.
199	#[codec(compact)]
200	pub auth_gas_used: UnsignedGas,
201}
202
203impl WorkReport {
204	/// Report hash.
205	pub fn hash(&self) -> WorkReportHash {
206		hash_encoded(self).into()
207	}
208
209	/// Report total gas requirement.
210	pub fn gas(&self) -> UnsignedGas {
211		self.results.iter().map(|r| r.accumulate_gas).sum()
212	}
213
214	/// Report dependencies derived from both context prerequisites and the segments root
215	/// lookup dictionary.
216	pub fn deps(&self) -> impl Iterator<Item = &WorkPackageHash> {
217		self.sr_lookup.keys().chain(self.context.prerequisites.iter())
218	}
219
220	/// Count report dependencies derived from both context prerequisites and the segments root
221	/// lookup dictionary.
222	pub fn dep_count(&self) -> usize {
223		self.sr_lookup.len() + self.context.prerequisites.len()
224	}
225
226	/// Determine if the report size is within the limit.
227	pub fn check_size(&self) -> bool {
228		let total: usize =
229			self.results.iter().filter_map(|r| Some(r.result.as_ref().ok()?.len())).sum();
230		total + self.auth_output.len() <= MAX_REPORT_ELECTIVE_DATA
231	}
232}
233
234impl MaxEncodedLen for WorkReport {
235	fn max_encoded_len() -> usize {
236		MAX_REPORT_ELECTIVE_DATA + 1024
237	}
238}