jam-std-common 0.1.16

Common datatypes and utilities for the JAM nodes and tooling
Documentation
use crate::{
	hash_encoded, header::OffendersMark, AvailabilityAssurance, CoreCount, EpochIndex, ErasureRoot,
	MaxTicketsPerBlock, TicketEnvelope, MAX_REPORT_ELECTIVE_DATA, VALS_PER_CORE,
};
use bounded_collections::ConstU32;
use jam_types::{
	AuthOutput, AuthorizerHash, BoundedVec, CoreIndex, ExtrinsicHash, FixedVec, MaxWorkItems,
	RefineContext, SegmentTreeRoot, ServiceId, Slot, UnsignedGas, ValIndex, ValSuperMajority,
	VecMap, WorkPackageHash, WorkReportHash, WorkResult,
};
use scale::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};

pub const MAX_VERDICTS_COUNT: usize = 16;
pub const MAX_OFFENSES_COUNT: usize = 16;

/// A bunch of preimages.
pub type PreimagesXt = Vec<Preimage>;
/// A collection of ticket envelopes.
pub type TicketsXt = BoundedVec<TicketEnvelope, MaxTicketsPerBlock>;
/// A bunch of assurances.
pub type AssurancesXt = BoundedVec<AvailabilityAssurance, jam_types::ValCount>;
/// A bunch of guarantees.
pub type GuaranteesXt = BoundedVec<ReportGuarantee, CoreCount>;
/// The disputes extrinsic.
#[derive(Clone, Encode, Decode, Debug, Default)]
pub struct DisputesXt {
	/// Disputed work reports together with their judgements.
	pub verdicts: BoundedVec<Verdict, ConstU32<{ MAX_VERDICTS_COUNT as u32 }>>,
	/// Validators who guaranteed a work report subsequently determined to be invalid.
	pub culprits: BoundedVec<CulpritProof, ConstU32<{ MAX_OFFENSES_COUNT as u32 }>>,
	/// Validators who audited a work report in conflict with the final dispute resolution.
	pub faults: BoundedVec<FaultProof, ConstU32<{ MAX_OFFENSES_COUNT as u32 }>>,
}

impl DisputesXt {
	pub fn offenders_mark(&self) -> OffendersMark {
		let offenders: Vec<_> = self
			.culprits
			.iter()
			.map(|v| v.key)
			.chain(self.faults.iter().map(|v| v.key))
			.collect();
		offenders.try_into().expect("OffendersMark bounds equal culprits + faults")
	}
	pub fn implies_offenders_mark(&self, offenders_mark: &OffendersMark) -> bool {
		self.culprits
			.iter()
			.map(|v| v.key)
			.chain(self.faults.iter().map(|v| v.key))
			.zip(offenders_mark.iter())
			.all(|(a, b)| a == *b)
	}
}

/// Judgements coming from a supermajority of either the active validators
/// set (κ) or the previous epoch's validator set (λ).
pub type VerdictVotes = FixedVec<Judgement, ValSuperMajority>;

/// Collection of judgements for a given target work report.
#[derive(Clone, Encode, Decode, Debug)]
pub struct Verdict {
	/// Target work report.
	pub target: WorkReportHash,
	/// Epoch index of the prior state or one less depending on the key set
	/// used to sign the votes.
	pub age: EpochIndex,
	/// Verdict votes collection.
	pub votes: VerdictVotes,
}

#[derive(Clone, Encode, Decode, Debug)]
pub struct Judgement {
	pub vote: bool,
	pub index: ValIndex,
	pub signature: super::ed25519::Signature,
}

#[derive(Clone, Encode, Decode, Debug)]
pub struct CulpritProof {
	pub report_hash: WorkReportHash,
	pub key: super::ed25519::Public,
	pub signature: super::ed25519::Signature,
}

#[derive(Clone, Encode, Decode, Debug)]
pub struct FaultProof {
	pub report_hash: WorkReportHash,
	pub vote: bool,
	pub key: super::ed25519::Public,
	pub signature: super::ed25519::Signature,
}

#[derive(Clone, Encode, Decode, Debug, Default)]
pub struct Extrinsic {
	pub tickets: TicketsXt,
	pub preimages: PreimagesXt,
	pub guarantees: GuaranteesXt,
	pub assurances: AssurancesXt,
	pub disputes: DisputesXt,
}

impl Extrinsic {
	pub fn guarantees_prehashed(&self) -> Vec<(WorkReportHash, Slot, &GuaranteeSignatures)> {
		self.guarantees
			.iter()
			.map(|r| (r.report.hash(), r.slot, &r.signatures))
			.collect()
	}

	pub fn hash(&self) -> ExtrinsicHash {
		let tickets_hash = hash_encoded(&self.tickets);
		let disputes_hash = hash_encoded(&self.disputes);
		let preimages_hash = hash_encoded(&self.preimages);
		let assurances_hash = hash_encoded(&self.assurances);
		let guarantees_hash = hash_encoded(&self.guarantees_prehashed());
		let top = (tickets_hash, preimages_hash, guarantees_hash, assurances_hash, disputes_hash);
		hash_encoded(&top).into()
	}
}

#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Preimage {
	pub requester: ServiceId,
	pub blob: Vec<u8>,
}
impl Preimage {
	pub fn new(requester: ServiceId, blob: impl Into<Vec<u8>>) -> Self {
		Self { requester, blob: blob.into() }
	}
}

/// A Work Report along with a guarantee of its correctness from staked sources.
#[derive(Clone, Encode, Decode, Debug, MaxEncodedLen)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct ReportGuarantee {
	/// The Work Report which is being attested.
	pub report: WorkReport,
	/// The slot following production of the report. The validator set and hence the meaning of the
	/// validator indices in `signatures` is inferred from this. The indices must be a subset of
	/// those assigned to `report.core_index` during the slot.
	pub slot: Slot,
	/// The signatures from the guarantors whose message is the hash of the `report`.
	/// The order of the signatures is the same order as the validators appear in
	/// the epochal validator set.
	pub signatures: GuaranteeSignatures,
}

pub type GuaranteeSignatures = BoundedVec<ValSignature, ConstU32<{ VALS_PER_CORE as u32 }>>;

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
pub struct ValSignature {
	pub val_index: ValIndex,
	pub signature: super::ed25519::Signature,
}

impl ConstEncodedLen for ValSignature {}

/// Secure reference to a Work Package.
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
pub struct WorkPackageSpec {
	/// The hash of the Work Package.
	pub hash: WorkPackageHash,
	/// The length in bytes of Work Bundle.
	pub len: u32,
	/// The erasure root of the Work Bundle and export-segment pieces.
	pub erasure_root: ErasureRoot,
	/// The segment root of the Work Package.
	pub exports_root: SegmentTreeRoot,
	/// The number of segments exported by the Work Package.
	pub exports_count: u16,
}

impl WorkPackageSpec {
	pub fn wp_srl(&self) -> (WorkPackageHash, SegmentTreeRoot) {
		(self.hash, self.exports_root)
	}
}

/// Execution report of a Work Package, mainly comprising the Results from the Refinement
/// of its Work Items.
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub struct WorkReport {
	/// The specification of the underlying Work Package.
	pub package_spec: WorkPackageSpec,
	/// The context of the underlying Work Package.
	pub context: RefineContext,
	/// The Core index under which the Work Package was Refined to generate the Report.
	pub core_index: CoreIndex,
	/// The authorizer under which this Work Package got executed. For the Work Package to be
	/// validly reported, this must appear in the Core's authorizer queue at the time of reporting.
	pub authorizer_hash: AuthorizerHash,
	/// The output of the authorizer under which this Work Package got executed.
	pub auth_output: AuthOutput,
	/// The segment-root lookup dictionary.
	pub sr_lookup: VecMap<WorkPackageHash, SegmentTreeRoot>,
	/// The results of the evaluation of the Items in the underlying Work Package.
	pub results: BoundedVec<WorkResult, MaxWorkItems>,
	/// The amount of gas actually used by the IsAuthorized call.
	#[codec(compact)]
	pub auth_gas_used: UnsignedGas,
}

impl WorkReport {
	/// Report hash.
	pub fn hash(&self) -> WorkReportHash {
		hash_encoded(self).into()
	}

	/// Report total gas requirement.
	pub fn gas(&self) -> UnsignedGas {
		self.results.iter().map(|r| r.accumulate_gas).sum()
	}

	/// Report dependencies derived from both context prerequisites and the segments root
	/// lookup dictionary.
	pub fn deps(&self) -> impl Iterator<Item = &WorkPackageHash> {
		self.sr_lookup.keys().chain(self.context.prerequisites.iter())
	}

	/// Count report dependencies derived from both context prerequisites and the segments root
	/// lookup dictionary.
	pub fn dep_count(&self) -> usize {
		self.sr_lookup.len() + self.context.prerequisites.len()
	}

	/// Determine if the report size is within the limit.
	pub fn check_size(&self) -> bool {
		let total: usize =
			self.results.iter().filter_map(|r| Some(r.result.as_ref().ok()?.len())).sum();
		total + self.auth_output.len() <= MAX_REPORT_ELECTIVE_DATA
	}
}

impl MaxEncodedLen for WorkReport {
	fn max_encoded_len() -> usize {
		MAX_REPORT_ELECTIVE_DATA + 1024
	}
}