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 codec::{Compact, CompactLen, ConstEncodedLen, Decode, Encode, MaxEncodedLen};
7use jam_types::{
8 AuthTrace, AuthorizerHash, BoundedVec, CoreIndex, ExtrinsicHash, FixedVec, MaxWorkItems,
9 RefineContext, SegmentTreeRoot, ServiceId, Slot, UnsignedGas, ValIndex, ValSuperMajority,
10 VecMap, WorkDigest, WorkPackageHash, WorkReportHash,
11};
12
13pub const MAX_VERDICTS_COUNT: usize = 16;
14pub const MAX_OFFENSES_COUNT: usize = 16;
15
16pub type PreimagesXt = Vec<Preimage>;
18pub type TicketsXt = BoundedVec<TicketEnvelope, MaxTicketsPerBlock>;
20pub type AssurancesXt = BoundedVec<AvailabilityAssurance, jam_types::ValCount>;
22pub type GuaranteesXt = BoundedVec<ReportGuarantee, CoreCount>;
24#[derive(Clone, Encode, Decode, Debug, Default)]
26pub struct DisputesXt {
27 pub verdicts: BoundedVec<Verdict, ConstU32<{ MAX_VERDICTS_COUNT as u32 }>>,
29 pub culprits: BoundedVec<CulpritProof, ConstU32<{ MAX_OFFENSES_COUNT as u32 }>>,
31 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
55pub type VerdictVotes = FixedVec<Judgement, ValSuperMajority>;
58
59#[derive(Clone, Encode, Decode, Debug)]
61pub struct Verdict {
62 pub target: WorkReportHash,
64 pub age: EpochIndex,
67 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 pub fn encoded_len(blob_len: usize) -> usize {
132 core::mem::size_of::<ServiceId>() +
133 Compact::<u64>::compact_len(&(blob_len as u64)) +
134 blob_len
135 }
136}
137
138#[derive(Clone, Encode, Decode, Debug, MaxEncodedLen)]
140#[cfg_attr(test, derive(PartialEq, Eq))]
141pub struct ReportGuarantee {
142 pub report: WorkReport,
144 pub slot: Slot,
148 pub signatures: GuaranteeSignatures,
152}
153
154pub type GuaranteeSignatures = BoundedVec<ValSignature, ConstU32<{ VALS_PER_CORE as u32 }>>;
155
156#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
157pub struct ValSignature {
158 pub val_index: ValIndex,
159 pub signature: super::ed25519::Signature,
160}
161
162impl ConstEncodedLen for ValSignature {}
163
164#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
166pub struct WorkPackageSpec {
167 pub hash: WorkPackageHash,
169 pub len: u32,
171 pub erasure_root: ErasureRoot,
173 pub exports_root: SegmentTreeRoot,
175 pub exports_count: u16,
177}
178
179impl WorkPackageSpec {
180 pub fn wp_srl(&self) -> (WorkPackageHash, SegmentTreeRoot) {
181 (self.hash, self.exports_root)
182 }
183}
184
185#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
188pub struct WorkReport {
189 pub package_spec: WorkPackageSpec,
191 pub context: RefineContext,
193 #[codec(compact)]
195 pub core_index: CoreIndex,
196 pub authorizer_hash: AuthorizerHash,
199 pub auth_output: AuthTrace,
201 pub sr_lookup: VecMap<WorkPackageHash, SegmentTreeRoot>,
203 pub results: BoundedVec<WorkDigest, MaxWorkItems>,
205 #[codec(compact)]
207 pub auth_gas_used: UnsignedGas,
208}
209
210impl WorkReport {
211 pub fn hash(&self) -> WorkReportHash {
213 hash_encoded(self).into()
214 }
215
216 pub fn gas(&self) -> UnsignedGas {
218 self.results.iter().map(|r| r.accumulate_gas).sum()
219 }
220
221 pub fn deps(&self) -> impl Iterator<Item = &WorkPackageHash> {
224 self.sr_lookup.keys().chain(self.context.prerequisites.iter())
225 }
226
227 pub fn dep_count(&self) -> usize {
230 self.sr_lookup.len() + self.context.prerequisites.len()
231 }
232
233 pub fn check_size(&self) -> bool {
235 let total: usize =
236 self.results.iter().filter_map(|r| Some(r.result.as_ref().ok()?.len())).sum();
237 total + self.auth_output.len() <= MAX_REPORT_ELECTIVE_DATA
238 }
239}
240
241impl MaxEncodedLen for WorkReport {
242 fn max_encoded_len() -> usize {
243 MAX_REPORT_ELECTIVE_DATA + 1024
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use jam_types::{MAX_PREIMAGE_BLOB_LEN, MAX_PREIMAGE_LEN};
251
252 #[test]
253 fn max_preimage_blob_len_is_correct() {
254 let preimage =
255 Preimage { requester: ServiceId::MAX, blob: vec![123_u8; MAX_PREIMAGE_BLOB_LEN] };
256 let encoded_len = preimage.encode().len();
257 assert_eq!(MAX_PREIMAGE_LEN, encoded_len);
258 }
259
260 #[test]
261 fn preimage_encoded_len_works() {
262 for blob_len in (0..MAX_PREIMAGE_BLOB_LEN).step_by(997) {
263 let preimage = Preimage { requester: ServiceId::MAX, blob: vec![123_u8; blob_len] };
264 let expected_len = Preimage::encoded_len(blob_len);
265 let actual_len = preimage.encode().len();
266 assert_eq!(expected_len, actual_len);
267 }
268 }
269}