jam-std-common 0.1.26

Common datatypes and utilities for the JAM nodes and tooling
Documentation
use crate::{hash_raw_concat, Entropy, Mmr, WorkReport};
use bounded_collections::ConstU32;
use codec::{Decode, Encode};
use jam_types::{
	deposit_per_account, deposit_per_byte, deposit_per_item, opaque, AuthQueue, AuthWindow,
	AuthorizerHash, Balance, BoundedVec, CodeHash, CoreCount, EpochPeriod, FixedVec, Hash,
	HeaderHash, MmrPeakHash, RecentBlockCount, SegmentTreeRoot, ServiceId, ServiceInfo, Slot,
	StateRootHash, UnsignedGas, VecMap, VecSet, WorkPackageHash, WorkReportHash,
};

opaque! {
	pub struct StorageKey(pub [u8; 31]);
}

#[derive(Debug, Encode, Decode, Copy, Clone, Eq, PartialEq)]
pub enum SystemKey {
	Reserved0 = 0,
	AuthPools = 1,
	AuthQueues = 2,
	RecentBlocks = 3,
	Safrole = 4,
	Disputes = 5,
	Entropy = 6,
	Designates = 7,
	Validators = 8,
	PrevValidators = 9,
	Availability = 10,
	CurrentTime = 11,
	Privileges = 12,
	Statistics = 13,
	ReadyQueue = 14,
	Accumulated = 15,
	AccumulationOutput = 16,
}

impl From<SystemKey> for StorageKey {
	fn from(value: SystemKey) -> Self {
		let k = value.encode();
		let mut r = [0; StorageKey::LEN];
		r[..k.len()].copy_from_slice(&k[..]);
		r.into()
	}
}

impl IntoStorageKey for SystemKey {}

impl IntoStorageKey for StorageKey {}

#[derive(Clone)]
pub enum ServiceKey<'a> {
	Info { id: ServiceId },
	Value { id: ServiceId, key: &'a [u8] },
	Request { id: ServiceId, len: u32, hash: [u8; 32] },
	Preimage { id: ServiceId, hash: [u8; 32] },
}

pub trait IntoStorageKey: Into<StorageKey> {
	fn service_id(&self) -> Option<ServiceId> {
		None
	}
}

impl IntoStorageKey for ServiceKey<'_> {
	fn service_id(&self) -> Option<ServiceId> {
		Some(match self {
			Self::Info { id, .. } |
			Self::Value { id, .. } |
			Self::Request { id, .. } |
			Self::Preimage { id, .. } => *id,
		})
	}
}

impl<'a> From<ServiceKey<'a>> for StorageKey {
	fn from(k: ServiceKey<'a>) -> Self {
		use ServiceKey::*;
		let id = match k {
			Info { id } | Value { id, .. } | Request { id, .. } | Preimage { id, .. } => id,
		}
		.to_le_bytes();
		let mut r = [0; StorageKey::LEN];
		let hash = match k {
			Info { .. } => {
				r[0] = 255;
				r[1] = id[0];
				r[3] = id[1];
				r[5] = id[2];
				r[7] = id[3];
				return r.into();
			},
			Value { key, .. } => hash_raw_concat([&[0xff, 0xff, 0xff, 0xff], key]),
			Preimage { hash, .. } => hash_raw_concat([&[0xfe, 0xff, 0xff, 0xff], &hash[..]]),
			Request { len, hash, .. } => hash_raw_concat([&len.to_le_bytes(), &hash[..]]),
		};
		r[..8].copy_from_slice(&[id[0], hash[0], id[1], hash[1], id[2], hash[2], id[3], hash[3]]);
		r[8..].copy_from_slice(&hash[4..27]);
		r.into()
	}
}

/// Current service struct version.
const SERVICE_VERSION: u8 = 0;

#[derive(Debug, Clone)]
pub struct Service {
	/// The hash of the code of the service.
	pub code_hash: CodeHash,
	/// The existing balance of the service.
	pub balance: Balance,
	/// The minimum amount of gas which must be provided to this service's `accumulate` for each
	/// work item it must process.
	pub min_item_gas: UnsignedGas,
	/// The minimum amount of gas which must be provided to this service's `accumulate` for each
	/// incoming transfer it must process.
	pub min_memo_gas: UnsignedGas,
	/// The total number of bytes used for data electively held for this service on-chain.
	pub bytes: u64,
	/// Offset of storage footprint only above which a minimum deposit is needed.
	pub deposit_offset: Balance,
	/// The total number of items of data electively held for this service on-chain.
	pub items: u32,
	/// Creation time slot.
	pub creation_slot: Slot,
	/// Most recent accumulation time slot.
	pub last_accumulation_slot: Slot,
	/// Parent service identifier.
	pub parent_service: ServiceId,
}

impl From<Service> for ServiceInfo {
	fn from(service: Service) -> Self {
		ServiceInfo {
			code_hash: service.code_hash,
			balance: service.balance,
			min_item_gas: service.min_item_gas,
			min_memo_gas: service.min_memo_gas,
			bytes: service.bytes,
			items: service.items,
			threshold: service.threshold(),
			deposit_offset: service.deposit_offset,
			creation_slot: service.creation_slot,
			last_accumulation_slot: service.last_accumulation_slot,
			parent_service: service.parent_service,
		}
	}
}

impl Encode for Service {
	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
		dest.push_byte(SERVICE_VERSION);
		self.code_hash.encode_to(dest);
		self.balance.encode_to(dest);
		self.min_item_gas.encode_to(dest);
		self.min_memo_gas.encode_to(dest);
		self.bytes.encode_to(dest);
		self.deposit_offset.encode_to(dest);
		self.items.encode_to(dest);
		self.creation_slot.encode_to(dest);
		self.last_accumulation_slot.encode_to(dest);
		self.parent_service.encode_to(dest);
	}
}

impl Decode for Service {
	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
		if u8::decode(input)? != SERVICE_VERSION {
			return Err("Invalid service version".into())
		}
		Ok(Self {
			code_hash: CodeHash::decode(input)?,
			balance: Balance::decode(input)?,
			min_item_gas: UnsignedGas::decode(input)?,
			min_memo_gas: UnsignedGas::decode(input)?,
			bytes: u64::decode(input)?,
			deposit_offset: Balance::decode(input)?,
			items: u32::decode(input)?,
			creation_slot: Slot::decode(input)?,
			last_accumulation_slot: Slot::decode(input)?,
			parent_service: ServiceId::decode(input)?,
		})
	}
}

impl Service {
	pub fn threshold(&self) -> Balance {
		Self::deposit_required(self.items, self.bytes, self.deposit_offset)
	}
	pub fn free(&self) -> Balance {
		self.balance.saturating_sub(self.threshold())
	}
	pub fn deposit_required(items: u32, bytes: u64, deposit_offset: Balance) -> Balance {
		let base_deposit =
			items as u64 * deposit_per_item() + bytes * deposit_per_byte() + deposit_per_account();
		base_deposit.saturating_sub(deposit_offset)
	}
}

pub type AuthPool = BoundedVec<AuthorizerHash, AuthWindow>;
pub type AuthPools = FixedVec<AuthPool, CoreCount>;
pub type AuthQueues = FixedVec<AuthQueue, CoreCount>;

#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone)]
pub struct AvailabilityAssignment {
	/// Work report.
	pub report: WorkReport,
	/// Timeslot when `report` has been reported.
	pub report_slot: Slot,
}

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct Privileges {
	/// The service index with the ability to alter the privileges as well as bestow services with
	/// storage deposit credits.
	pub bless: ServiceId,
	/// The service indices with the ability to assign authorizors to cores.
	pub assign: FixedVec<ServiceId, CoreCount>,
	/// The service index with the ability to set the staging validator keys.
	pub designate: ServiceId,
	/// The service index with the ability to register service ids in the protected range.
	pub register: ServiceId,
	/// The services which always accumulate, together with their free gas.
	pub always_acc: VecMap<ServiceId, UnsignedGas>,
}

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct Disputes {
	/// The allow-set of work-reports, these are known to be good.
	pub good: VecSet<WorkReportHash>,
	/// The corrupt-set of work-reports, these are known to be bad.
	pub bad: VecSet<WorkReportHash>,
	/// The ban-set of work-reports, these have been determined of uncertain validity.
	pub wonky: VecSet<WorkReportHash>,
	/// The offenders' Ed25519 keys, these are known to have incorrectly judged a work-report's
	/// validity.
	pub offenders: VecSet<super::ed25519::Public>,
}

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct ValActivityRecord {
	// The number of blocks produced by the validator.
	pub blocks: u32,
	// The number of tickets introduced by the validator.
	pub tickets: u32,
	// The number of preimages introduced by the validator.
	pub preimages: u32,
	// The total number of octets across all preimages introduced by the validator.
	pub preimages_size: u32,
	// The number of reports guaranteed by the validator.
	pub guarantees: u32,
	// The number of availability assurances made by the validator.
	pub assurances: u32,
}

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct ServiceActivityRecord {
	/// Number of preimages provided to this service.
	#[codec(compact)]
	pub provided_count: u16,
	/// Total size of preimages provided to this service.
	#[codec(compact)]
	pub provided_size: u32,
	/// Number of work-items refined by service for reported work.
	#[codec(compact)]
	pub refinement_count: u32,
	/// Amount of gas used for refinement by service for reported work.
	#[codec(compact)]
	pub refinement_gas_used: UnsignedGas,
	/// Number of segments imported from the DL by service for reported work.
	#[codec(compact)]
	pub imports: u32,
	/// Total number of extrinsics used by service for reported work.
	#[codec(compact)]
	pub extrinsic_count: u32,
	/// Total size of extrinsics used by service for reported work.
	#[codec(compact)]
	pub extrinsic_size: u32,
	/// Number of segments exported into the DL by service for reported work.
	#[codec(compact)]
	pub exports: u32,
	/// Number of work-items accumulated by service.
	#[codec(compact)]
	pub accumulate_count: u32,
	/// Amount of gas used for accumulation by service.
	#[codec(compact)]
	pub accumulate_gas_used: UnsignedGas,
}

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct CoreActivityRecord {
	// Assurances coming in from general validators (but ultimately provided by guarantors)
	/// Amount of bytes which are placed into either Audits or Segments DA.
	/// This includes the work-bundle (including all extrinsics and imports) as well as all
	/// (exported) segments.
	#[codec(compact)]
	pub da_load: u32,
	/// Number of validators which formed super-majority for assurance.
	#[codec(compact)]
	pub popularity: u16,
	/// Number of segments imported from DA made by core for reported work.
	#[codec(compact)]
	pub imports: u16,
	/// Total number of extrinsics used by core for reported work.
	#[codec(compact)]
	pub extrinsic_count: u16,
	/// Total size of extrinsics used by core for reported work.
	#[codec(compact)]
	pub extrinsic_size: u32,
	/// Number of segments exported into DA made by core for reported work.
	#[codec(compact)]
	pub exports: u16,
	/// The work-bundle size. This is the size of data being placed into Audits DA by the core.
	#[codec(compact)]
	pub bundle_size: u32,
	// Reports coming in from guarantors
	/// Total gas consumed by core for reported work. Includes all refinement and authorizations.
	#[codec(compact)]
	pub gas_used: UnsignedGas,
}

pub type ValidatorsStats = FixedVec<ValActivityRecord, jam_types::ValCount>;
pub type CoresStats = FixedVec<CoreActivityRecord, CoreCount>;
pub type ServicesStats = VecMap<ServiceId, ServiceActivityRecord>;

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, Default)]
pub struct Statistics {
	pub vals_curr: ValidatorsStats,
	pub vals_last: ValidatorsStats,
	pub cores: CoresStats,
	pub services: ServicesStats,
}

impl Statistics {
	/// Reset the two accumulators.
	pub fn reset(&mut self) {
		self.cores = Default::default();
		self.services = Default::default();
	}
}

/// Entries relative to epoch `N`
/// `[0]`: epoch `N` entropy accumulator. Updated on each block with fresh entropy.
/// `[i]`, for 1 ≤ i ≤ 3: accumulator snapshot at the begin of epoch  `N-i+1`.
/// If `N-i+1 < 0` then the corresponding entry is set to some fixed default value.
pub type EntropyBuffer = FixedVec<Entropy, ConstU32<4>>;

/// Block information.
#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone)]
pub struct BlockInfo {
	/// Header hash.
	pub hash: HeaderHash,
	/// Accumulation MMR root.
	pub beefy_root: MmrPeakHash,
	/// Posterior state root.
	pub state_root: StateRootHash,
	/// Hash of each work report made into the block which is no more than the
	/// number of cores.
	pub reported: VecMap<WorkPackageHash, SegmentTreeRoot>,
}

/// Recent blocks history
pub type RecentBlocksHistory = BoundedVec<BlockInfo, RecentBlockCount>;

/// Imported blocks information
#[derive(Clone, Encode, Decode, Default, Debug)]
pub struct RecentBlocks {
	pub history: RecentBlocksHistory,
	pub mmr: Mmr,
}

pub type AvailabilityAssignments = FixedVec<Option<AvailabilityAssignment>, CoreCount>;

#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub struct ReadyRecord {
	pub report: WorkReport,
	pub deps: VecSet<WorkPackageHash>,
}
pub type ReadyQueue = FixedVec<Vec<ReadyRecord>, EpochPeriod>;
pub type Accumulated = FixedVec<Vec<WorkPackageHash>, EpochPeriod>;

pub type AccumulationOutput = Vec<(ServiceId, Hash)>;