Skip to main content

jam_std_common/
simple.rs

1use crate::{BytesCursor, Immutable, ImmutableWrap, max_merkle_proof_len};
2
3use super::{
4	Extrinsic, Header,
5	extrinsic::{AssurancesXt, DisputesXt, GuaranteesXt, TicketsXt},
6};
7use codec::{Compact, Decode, Encode, MaxEncodedLen};
8use jam_types::{
9	HeaderHash, MAX_PREIMAGE_LEN, MAX_SEGMENT_SLICE_LEN, SEGMENT_LEN, Slot, VALS_PER_CORE,
10	WorkPackage, WorkPackageHash, max_export_segments, max_exports, max_imports, opaque,
11	segment_len, slot_period_sec,
12};
13use std::{sync::Arc, time::Duration};
14
15/// Duration of a time slot, as a Rust-native `Duration`.
16pub fn slot_duration() -> Duration {
17	Duration::from_secs(slot_period_sec())
18}
19
20/// Maximum size of the hashes in the proof of one imported segment, without the length prefix:
21/// one hash for each level of the segment tree of the exporting package, which has at most
22/// `max_exports()` leaves.
23fn max_import_proof_hashes_size() -> u32 {
24	32 * max_merkle_proof_len(max_exports())
25}
26
27/// (W_F) Number of octets one imported segment counts in the work-bundle size: the segment and its
28/// justification.
29pub fn max_import_footprint_in_bundle() -> u32 {
30	SEGMENT_LEN as u32 + max_import_proof_hashes_size()
31}
32
33/// Maximum encoded size of the proof of one imported segment, with its one-octet length prefix.
34pub fn max_import_proof_size() -> usize {
35	max_import_proof_hashes_size() as usize + 1
36}
37
38/// Availability store data expiration in slots (28 days)
39pub fn availability_data_expiration() -> Slot {
40	28 * 24 * 60 * 60 / slot_period_sec() as Slot
41}
42
43/// The conservative maximum size of a Work Package Bundle.
44pub fn max_bundle_size() -> usize {
45	// Note that WorkPackage::max_encoded_len() includes max_input() so we don't need to explicitly
46	// include it here. max_input() also covers the imported segments and their justifications, so
47	// only the one-octet length prefix of each justification is added. This assumes a somewhat
48	// naive WorkPackage::max_encoded_len implementation, that doesn't eg account for the fact that
49	// if there are many import specs, some of the max_input() allowance will be used up by the
50	// imported segments. If WorkPackage::max_encoded_len is made less conservative, this function
51	// may need to be updated.
52	WorkPackage::max_encoded_len() + max_imports() as usize
53}
54
55/// Conservative upper bound on the encoded size of a `Block`.
56///
57/// Sum of `MaxEncodedLen` of each bounded `Extrinsic` field plus a fixed cap on the total
58/// preimage data per block (`MAX_PREIMAGE_LEN`).
59pub fn max_block_size() -> usize {
60	Header::max_encoded_len() +
61		TicketsXt::max_encoded_len() +
62		AssurancesXt::max_encoded_len() +
63		GuaranteesXt::max_encoded_len() +
64		DisputesXt::max_encoded_len() +
65		MAX_PREIMAGE_LEN +
66		Compact::<u32>::max_encoded_len() // Length prefix for Vec<Preimage>
67}
68
69/// Maximum basic size of a Work Package that we accept.
70/// This should be at least ImportSpec::encoded_fixed_size() * max_imports()
71pub const SANE_MAX_PACKAGE_SIZE: usize = 200 * 1024;
72
73/// The min. no. of signatures required for a guarantee to be valid.
74///
75/// Equals 2/3 of guarantors assigned to a core.
76pub const GUARANTEE_MIN_SIGNATURES: usize = (VALS_PER_CORE * 2).div_ceil(3);
77
78// Entropy required by some protocol's operations.
79opaque! { pub struct Entropy(pub [u8; 32]); }
80
81pub type ImmutableHeader = Immutable<Header, HeaderHash>;
82
83#[derive(Clone, Debug, Encode, Decode)]
84pub struct Block {
85	pub header: Arc<ImmutableHeader>,
86	pub extrinsic: Arc<Extrinsic>,
87}
88
89/// Type to represent the index of a tranche.
90pub type TrancheIndex = u8;
91
92/// Maximum size in bytes for a work package exported segments
93pub fn max_segment_vec_bytes() -> u32 {
94	segment_len() as u32 * max_export_segments()
95}
96
97/// One validator's slices of a work package's segments: one slice per segment, all of the
98/// same length.
99///
100/// Erasure-coding `n` segments for `v` validators yields a `v x n` matrix of slices; this
101/// type is one row. Segment `j` is reconstructed from the `j`-th slice of any `D(v)` rows:
102///
103/// ```text
104///                  seg 0   seg 1   ...   seg n-1
105/// validator 0      slice   slice         slice     <- one SegmentSliceVec
106/// ...
107/// validator v-1    slice   slice         slice
108/// ```
109///
110/// The row is stored as `data`, the concatenation of the validator's slices in segment
111/// order; `slice_len` (`SEGMENT_LEN / D(v)`, a property of the erasure coding rather than a
112/// protocol constant) gives the slice boundaries within it. The slice count is bound by the
113/// maximum number of segments a work package may export.
114///
115/// Every constructor takes the slice length, so `slice_len` is always in
116/// `[1, MAX_SEGMENT_SLICE_LEN]`.
117#[derive(Clone, Debug, Eq, PartialEq)]
118pub struct SegmentSliceVec {
119	data: Vec<u8>,
120	slice_len: usize,
121}
122
123impl SegmentSliceVec {
124	/// Get the raw byte buffer: all slices concatenated in segment order.
125	pub fn raw(&self) -> &[u8] {
126		&self.data
127	}
128
129	/// Get the underlying `Vec`.
130	pub fn into_inner(self) -> Vec<u8> {
131		self.data
132	}
133
134	/// Returns the length of each contained slice.
135	pub fn slice_len(&self) -> usize {
136		self.slice_len
137	}
138
139	/// Creates a vec of `slice_len`-sized slices from serialized bytes.
140	/// Returns `None` if the size is incorrect.
141	pub fn from_bytes(data: Vec<u8>, slice_len: usize) -> Option<Self> {
142		if slice_len == 0 || slice_len > MAX_SEGMENT_SLICE_LEN {
143			return None
144		}
145		if !data.len().is_multiple_of(slice_len) {
146			return None
147		}
148		if data.len() / slice_len > max_export_segments() as usize {
149			return None
150		}
151		Some(Self { data, slice_len })
152	}
153
154	/// Preallocates a vec for the given number of `slice_len`-sized slices.
155	pub fn with_capacity(capacity: usize, slice_len: usize) -> Self {
156		assert!(slice_len > 0 && slice_len <= MAX_SEGMENT_SLICE_LEN);
157		Self { data: Vec::with_capacity(capacity * slice_len), slice_len }
158	}
159
160	/// Returns the number of slices.
161	pub fn len(&self) -> usize {
162		self.data.len() / self.slice_len
163	}
164
165	/// Check if the vec is empty.
166	pub fn is_empty(&self) -> bool {
167		self.data.is_empty()
168	}
169
170	/// Appends a slice to this vec. Returns an error if the slice size is incorrect or the
171	/// vec overflows.
172	pub fn push(&mut self, slice: &[u8]) -> Result<(), &'static str> {
173		if slice.len() != self.slice_len {
174			return Err("Slice length does not match segment slice length")
175		}
176		if self.len() == max_export_segments() as usize {
177			return Err("Exceeded maximum segment slice vector size")
178		}
179		self.data.extend_from_slice(slice);
180		Ok(())
181	}
182
183	/// Adds a new empty slice to the vec and returns a reference to it. Produces an error if the
184	/// vec overflows.
185	pub fn push_default(&mut self) -> Result<&mut [u8], &'static str> {
186		if self.len() == max_export_segments() as usize {
187			return Err("Exceeded maximum segment slice vector size")
188		}
189		let len = self.data.len();
190		self.data.resize(len + self.slice_len, 0u8);
191		Ok(&mut self.data[len..])
192	}
193}
194
195impl std::ops::Index<usize> for SegmentSliceVec {
196	type Output = [u8];
197	fn index(&self, index: usize) -> &Self::Output {
198		&self.data[index * self.slice_len..(index + 1) * self.slice_len]
199	}
200}
201
202pub type ImmutableWorkPackage =
203	Immutable<jam_types::WrappedWorkPackage<ImmutableWrap>, WorkPackageHash>;
204
205impl From<&jam_types::WorkPackage> for ImmutableWorkPackage {
206	fn from(package: &jam_types::WorkPackage) -> Self {
207		// Convert by encoding and then decoding. This is simple and ensures all Bytes instances
208		// reference the same buffer. This should only be used by test code so efficiency does not
209		// particularly matter.
210		BytesCursor::from(package.encode())
211			.decode_all()
212			.expect("Should be able to decode result of T::encode()")
213	}
214}
215
216impl From<jam_types::WorkPackage> for ImmutableWorkPackage {
217	fn from(package: jam_types::WorkPackage) -> Self {
218		(&package).into()
219	}
220}