use crate::{
AvailabilityStatement, Block, BoundedString, EpochIndex, ErasureRoot, PeerAddr, PeerDetails,
PeerId, ReportGuarantee, TicketId, WorkReport,
};
use bounded_collections::{BoundedVec, ConstU32, TryCollect};
use codec::{Decode, Encode, MaxEncodedLen};
use jam_types::{
CoreCount, CoreIndex, FixedVec, Hash, HeaderHash, ImportSpec, MaxDependencies,
MaxImportSegments, MaxImports, MaxWorkItems, ProtocolParameters, SegmentTreeRoot, ServiceId,
Slot, TicketAttempt, TicketsAttemptsNumber, UnsignedGas, ValIndex, WorkItem, WorkPackageHash,
WorkReportHash, JAM_COMMON_ERA, VALS_PER_CORE,
};
use std::{
ops::AddAssign,
time::{Duration, SystemTime, UNIX_EPOCH},
};
pub const PROTOCOL_VERSION: u8 = 0;
pub type Reason = BoundedString<128>;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, MaxEncodedLen)]
pub struct Timestamp(pub u64);
impl Timestamp {
pub fn now() -> Self {
Self::from(SystemTime::now())
}
}
impl std::fmt::Debug for Timestamp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl From<SystemTime> for Timestamp {
fn from(time: SystemTime) -> Self {
let jam_ce = UNIX_EPOCH + Duration::from_secs(JAM_COMMON_ERA);
let since_jam_ce = time.duration_since(jam_ce).unwrap_or(Duration::ZERO);
Self(since_jam_ce.as_micros() as u64)
}
}
impl From<Timestamp> for SystemTime {
fn from(timestamp: Timestamp) -> Self {
let jam_ce = UNIX_EPOCH + Duration::from_secs(JAM_COMMON_ERA);
jam_ce + Duration::from_micros(timestamp.0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, MaxEncodedLen)]
pub struct EventId(pub u64);
impl std::fmt::Debug for EventId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for EventId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum ConnectionSide {
Local = 0,
Remote = 1,
}
pub type ShardIndex = u16;
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct BlockOutline {
pub size: u32,
pub hash: HeaderHash,
pub num_tickets: u32,
pub num_preimages: u32,
pub preimages_size: u32,
pub num_guarantees: u32,
pub num_assurances: u32,
pub num_dispute_verdicts: u32,
}
impl From<(&Block, HeaderHash)> for BlockOutline {
fn from((block, hash): (&Block, HeaderHash)) -> Self {
Self {
size: block.encoded_size() as u32,
hash,
num_tickets: block.extrinsic.tickets.len() as u32,
num_preimages: block.extrinsic.preimages.len() as u32,
preimages_size: block.extrinsic.preimages.iter().map(|p| p.blob.len() as u32).sum(),
num_guarantees: block.extrinsic.guarantees.len() as u32,
num_assurances: block.extrinsic.assurances.len() as u32,
num_dispute_verdicts: block.extrinsic.disputes.verdicts.len() as u32,
}
}
}
impl From<&Block> for BlockOutline {
fn from(block: &Block) -> Self {
(block, block.header.hash()).into()
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum BlockRequestDirection {
AscendingExclusive = 0,
DescendingInclusive = 1,
}
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct ExecCost {
pub gas: UnsignedGas,
pub ns: u64,
}
impl AddAssign for ExecCost {
fn add_assign(&mut self, rhs: Self) {
self.gas += rhs.gas;
self.ns += rhs.ns;
}
}
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct IsAuthorizedCost {
pub total: ExecCost,
pub load_ns: u64,
pub host_call: ExecCost,
}
impl AddAssign for IsAuthorizedCost {
fn add_assign(&mut self, rhs: Self) {
self.total += rhs.total;
self.host_call += rhs.host_call;
}
}
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct RefineHostCallCost {
pub lookup: ExecCost,
pub vm: ExecCost,
pub mem: ExecCost,
pub invoke: ExecCost,
pub other: ExecCost,
}
impl AddAssign for RefineHostCallCost {
fn add_assign(&mut self, rhs: Self) {
self.lookup += rhs.lookup;
self.vm += rhs.vm;
self.mem += rhs.mem;
self.invoke += rhs.invoke;
self.other += rhs.other;
}
}
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct RefineCost {
pub total: ExecCost,
pub load_ns: u64,
pub host_call: RefineHostCallCost,
}
impl AddAssign for RefineCost {
fn add_assign(&mut self, rhs: Self) {
self.total += rhs.total;
self.host_call += rhs.host_call;
}
}
pub type RefineCosts = BoundedVec<RefineCost, MaxWorkItems>;
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct AccumulateHostCallCost {
pub state: ExecCost,
pub lookup: ExecCost,
pub preimage: ExecCost,
pub service: ExecCost,
pub transfer: ExecCost,
pub transfer_dest_gas: UnsignedGas,
pub other: ExecCost,
}
impl AddAssign for AccumulateHostCallCost {
fn add_assign(&mut self, rhs: Self) {
self.state += rhs.state;
self.lookup += rhs.lookup;
self.preimage += rhs.preimage;
self.service += rhs.service;
self.transfer += rhs.transfer;
self.transfer_dest_gas += rhs.transfer_dest_gas;
self.other += rhs.other;
}
}
#[derive(Clone, Debug, Default, Encode, Decode, MaxEncodedLen)]
pub struct AccumulateCost {
pub num_calls: u32,
pub num_transfers: u32,
pub num_items: u32,
pub total: ExecCost,
pub load_ns: u64,
pub host_call: AccumulateHostCallCost,
}
impl AddAssign for AccumulateCost {
fn add_assign(&mut self, rhs: Self) {
self.num_calls += rhs.num_calls;
self.num_transfers += rhs.num_transfers;
self.num_items += rhs.num_items;
self.total += rhs.total;
self.host_call += rhs.host_call;
}
}
pub type AccumulateCosts = BoundedVec<(ServiceId, AccumulateCost), ConstU32<500>>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ImportSegmentId {
Import(u16),
ProofPage(u16),
}
impl ImportSegmentId {
pub fn import(self) -> Option<u16> {
match self {
Self::Import(index) => Some(index),
Self::ProofPage(_) => None,
}
}
}
impl Encode for ImportSegmentId {
fn size_hint(&self) -> usize {
2
}
fn encode_to<O: codec::Output + ?Sized>(&self, output: &mut O) {
let index = match *self {
Self::Import(index) => {
debug_assert!(index < (1 << 15));
index
},
Self::ProofPage(index) => {
debug_assert!(index < (1 << 15));
index + (1 << 15)
},
};
index.encode_to(output);
}
}
impl Decode for ImportSegmentId {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let index = u16::decode(input)?;
if (index & (1 << 15)) == 0 {
Ok(Self::Import(index))
} else {
Ok(Self::ProofPage(index & !(1 << 15)))
}
}
fn encoded_fixed_size() -> Option<usize> {
u16::encoded_fixed_size()
}
}
impl MaxEncodedLen for ImportSegmentId {
fn max_encoded_len() -> usize {
u16::max_encoded_len()
}
}
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct WorkItemOutline {
pub service: ServiceId,
pub payload_size: u32,
pub refine_gas_limit: UnsignedGas,
pub accumulate_gas_limit: UnsignedGas,
pub extrinsic_size: u32,
pub imports: BoundedVec<ImportSpec, MaxImports>,
pub num_exports: u16,
}
impl From<&WorkItem> for WorkItemOutline {
fn from(item: &WorkItem) -> Self {
Self {
service: item.service,
payload_size: item.payload.len() as u32,
refine_gas_limit: item.refine_gas_limit,
accumulate_gas_limit: item.accumulate_gas_limit,
extrinsic_size: item.extrinsic_size(),
imports: item.import_segments.clone(),
num_exports: item.export_count,
}
}
}
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct WorkPackageOutline {
pub size: u32,
pub hash: WorkPackageHash,
pub anchor: HeaderHash,
pub lookup_anchor_slot: Slot,
pub prerequisites: BoundedVec<WorkPackageHash, MaxDependencies>,
pub items: BoundedVec<WorkItemOutline, MaxWorkItems>,
}
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct WorkReportOutline {
pub hash: WorkReportHash,
pub bundle_size: u32,
pub erasure_root: ErasureRoot,
pub exports_root: SegmentTreeRoot,
}
impl From<&WorkReport> for WorkReportOutline {
fn from(report: &WorkReport) -> Self {
Self {
hash: report.hash(),
bundle_size: report.package_spec.len,
erasure_root: report.package_spec.erasure_root,
exports_root: report.package_spec.exports_root,
}
}
}
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct GuaranteeOutline {
pub report_hash: WorkReportHash,
pub slot: Slot,
pub guarantors: BoundedVec<ValIndex, ConstU32<{ VALS_PER_CORE as u32 }>>,
}
impl From<&ReportGuarantee> for GuaranteeOutline {
fn from(guarantee: &ReportGuarantee) -> Self {
Self {
report_hash: guarantee.report.hash(),
slot: guarantee.slot,
guarantors: guarantee
.signatures
.iter()
.map(|signature| signature.val_index)
.try_collect()
.expect("Output and input bounds are the same"),
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum GuaranteeDiscardReason {
PackageReportedOnChain = 0,
Superseded = 1,
CannotReportOnChain = 2,
TooMany = 3,
Other = 4,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum ReconstructionKind {
NonTrivial,
Trivial,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum AnnouncedPreimageForgetReason {
ProvidedOnChain = 0,
NotRequestedOnChain = 1,
FailedToAcquire = 2,
TooMany = 3,
BadLength = 4,
Other = 5,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
pub enum PreimageDiscardReason {
ProvidedOnChain = 0,
NotRequestedOnChain = 1,
TooMany = 2,
Other = 3,
}
pub const NODE_USES_PVM_RECOMPILER: u32 = 1 << 0;
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct NodeInfo {
pub params: ProtocolParameters,
pub genesis: HeaderHash,
pub details: PeerDetails,
pub flags: u32,
pub impl_name: BoundedString<32>,
pub impl_version: BoundedString<32>,
pub gp_version: BoundedString<16>,
pub note: BoundedString<512>,
}
macro_rules! event_struct {
($(#[$attr:meta])* $name:ident { $($(#[$field_attr:meta])* $field_name:ident: $field_type:ty,)+ }) => {
$(#[$attr])*
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct $name {
$($(#[$field_attr])* pub $field_name: $field_type,)+
}
};
}
macro_rules! events {
($($(#[$attr:meta])* $name:ident = $discriminator:literal $fields:tt)+) => {
pub mod event {
use super::*;
$(event_struct! { $(#[$attr])* $name $fields })+
}
#[repr(u8)]
#[derive(Clone, Encode, Decode, MaxEncodedLen)]
pub enum Event {
$($name(event::$name) = $discriminator,)+
}
impl std::fmt::Debug for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(Self::$name(event) => event.fmt(f),)+
}
}
}
};
}
events! {
Dropped = 0 {
last_timestamp: Timestamp,
num: u64,
}
Status = 10 {
num_peers: u32,
num_val_peers: u32,
num_sync_peers: u32,
num_guarantees: FixedVec<u8, CoreCount>,
num_shards: u32,
shards_size: u64,
num_preimages: u32,
preimages_size: u32,
}
BestBlockChanged = 11 {
slot: Slot,
hash: HeaderHash,
}
FinalizedBlockChanged = 12 {
slot: Slot,
hash: HeaderHash,
}
SyncStatusChanged = 13 {
synced: bool,
}
ConnectionRefused = 20 {
from: PeerAddr,
}
ConnectingIn = 21 {
from: PeerAddr,
}
ConnectInFailed = 22 {
connecting_id: EventId,
reason: Reason,
}
ConnectedIn = 23 {
connecting_id: EventId,
peer_id: PeerId,
}
ConnectingOut = 24 {
to: PeerDetails,
}
ConnectOutFailed = 25 {
connecting_id: EventId,
reason: Reason,
}
ConnectedOut = 26 {
connecting_id: EventId,
}
Disconnected = 27 {
peer: PeerId,
terminator: Option<ConnectionSide>,
reason: Reason,
}
PeerMisbehaved = 28 {
peer: PeerId,
reason: Reason,
}
Authoring = 40 {
slot: Slot,
parent: HeaderHash,
}
AuthoringFailed = 41 {
authoring_id: EventId,
reason: Reason,
}
Authored = 42 {
authoring_id: EventId,
outline: BlockOutline,
}
Importing = 43 {
slot: Slot,
outline: BlockOutline,
}
BlockVerificationFailed = 44 {
importing_id: EventId,
reason: Reason,
}
BlockVerified = 45 {
importing_id: EventId,
}
BlockExecutionFailed = 46 {
authoring_or_importing_id: EventId,
reason: Reason,
}
BlockExecuted = 47 {
authoring_or_importing_id: EventId,
accumulate_costs: AccumulateCosts,
}
BlockAnnouncementStreamOpened = 60 {
peer: PeerId,
opener: ConnectionSide,
}
BlockAnnouncementStreamClosed = 61 {
peer: PeerId,
closer: ConnectionSide,
reason: Reason,
}
BlockAnnounced = 62 {
peer: PeerId,
announcer: ConnectionSide,
slot: Slot,
hash: HeaderHash,
}
SendingBlockRequest = 63 {
recipient: PeerId,
hash: HeaderHash,
direction: BlockRequestDirection,
max_blocks: u32,
}
ReceivingBlockRequest = 64 {
sender: PeerId,
}
BlockRequestFailed = 65 {
request_id: EventId,
reason: Reason,
}
BlockRequestSent = 66 {
request_id: EventId,
}
BlockRequestReceived = 67 {
request_id: EventId,
hash: HeaderHash,
direction: BlockRequestDirection,
max_blocks: u32,
}
BlockTransferred = 68 {
request_id: EventId,
slot: Slot,
outline: BlockOutline,
last: bool,
}
GeneratingTickets = 80 {
epoch: EpochIndex,
}
TicketGenerationFailed = 81 {
generating_id: EventId,
reason: Reason,
}
TicketsGenerated = 82 {
generating_id: EventId,
ids: BoundedVec<TicketId, TicketsAttemptsNumber>,
}
TicketTransferFailed = 83 {
peer: PeerId,
sender: ConnectionSide,
from_proxy: bool,
reason: Reason,
}
TicketTransferred = 84 {
peer: PeerId,
sender: ConnectionSide,
from_proxy: bool,
epoch: EpochIndex,
attempt: TicketAttempt,
id: TicketId,
}
WorkPackageSubmission = 90 {
builder: PeerId,
bundle: bool,
}
WorkPackageBeingShared = 91 {
primary: PeerId,
}
WorkPackageFailed = 92 {
submission_or_share_id: EventId,
reason: Reason,
}
DuplicateWorkPackage = 93 {
submission_or_share_id: EventId,
core: CoreIndex,
hash: WorkPackageHash,
}
WorkPackageReceived = 94 {
submission_or_share_id: EventId,
core: CoreIndex,
outline: WorkPackageOutline,
}
Authorized = 95 {
submission_or_share_id: EventId,
cost: IsAuthorizedCost,
}
ExtrinsicDataReceived = 96 {
submission_or_share_id: EventId,
}
ImportsReceived = 97 {
submission_or_share_id: EventId,
}
SharingWorkPackage = 98 {
submission_id: EventId,
secondary: PeerId,
}
WorkPackageSharingFailed = 99 {
submission_id: EventId,
secondary: PeerId,
reason: Reason,
}
BundleSent = 100 {
submission_id: EventId,
secondary: PeerId,
}
Refined = 101 {
submission_or_share_id: EventId,
costs: RefineCosts,
}
WorkReportBuilt = 102 {
submission_or_share_id: EventId,
outline: WorkReportOutline,
}
WorkReportSignatureSent = 103 {
share_id: EventId,
}
WorkReportSignatureReceived = 104 {
submission_id: EventId,
secondary: PeerId,
}
GuaranteeBuilt = 105 {
submission_id: EventId,
outline: GuaranteeOutline,
}
SendingGuarantee = 106 {
built_id: EventId,
recipient: PeerId,
}
GuaranteeSendFailed = 107 {
sending_id: EventId,
reason: Reason,
}
GuaranteeSent = 108 {
sending_id: EventId,
}
GuaranteesDistributed = 109 {
submission_id: EventId,
}
ReceivingGuarantee = 110 {
sender: PeerId,
}
GuaranteeReceiveFailed = 111 {
receiving_id: EventId,
reason: Reason,
}
GuaranteeReceived = 112 {
receiving_id: EventId,
outline: GuaranteeOutline,
}
GuaranteeDiscarded = 113 {
outline: GuaranteeOutline,
reason: GuaranteeDiscardReason,
}
SendingShardRequest = 120 {
guarantor: PeerId,
erasure_root: ErasureRoot,
shard: ShardIndex,
}
ReceivingShardRequest = 121 {
assurer: PeerId,
}
ShardRequestFailed = 122 {
request_id: EventId,
reason: Reason,
}
ShardRequestSent = 123 {
request_id: EventId,
}
ShardRequestReceived = 124 {
request_id: EventId,
erasure_root: ErasureRoot,
shard: ShardIndex,
}
ShardsTransferred = 125 {
request_id: EventId,
}
DistributingAssurance = 126 {
statement: AvailabilityStatement,
}
AssuranceSendFailed = 127 {
distributing_id: EventId,
recipient: PeerId,
reason: Reason,
}
AssuranceSent = 128 {
distributing_id: EventId,
recipient: PeerId,
}
AssuranceDistributed = 129 {
distributing_id: EventId,
}
AssuranceReceiveFailed = 130 {
sender: PeerId,
reason: Reason,
}
AssuranceReceived = 131 {
sender: PeerId,
anchor: HeaderHash,
}
SendingBundleShardRequest = 140 {
audit_id: EventId,
assurer: PeerId,
shard: ShardIndex,
}
ReceivingBundleShardRequest = 141 {
auditor: PeerId,
}
BundleShardRequestFailed = 142 {
request_id: EventId,
reason: Reason,
}
BundleShardRequestSent = 143 {
request_id: EventId,
}
BundleShardRequestReceived = 144 {
request_id: EventId,
erasure_root: ErasureRoot,
shard: ShardIndex,
}
BundleShardTransferred = 145 {
request_id: EventId,
}
ReconstructingBundle = 146 {
audit_id: EventId,
kind: ReconstructionKind,
}
BundleReconstructed = 147 {
audit_id: EventId,
}
SendingBundleRequest = 148 {
audit_id: EventId,
guarantor: PeerId,
}
ReceivingBundleRequest = 149 {
auditor: PeerId,
}
BundleRequestFailed = 150 {
request_id: EventId,
reason: Reason,
}
BundleRequestSent = 151 {
request_id: EventId,
}
BundleRequestReceived = 152 {
request_id: EventId,
erasure_root: ErasureRoot,
}
BundleTransferred = 153 {
request_id: EventId,
}
WorkPackageHashMapped = 160 {
submission_id: EventId,
work_package_hash: WorkPackageHash,
segments_root: SegmentTreeRoot,
}
SegmentsRootMapped = 161 {
submission_id: EventId,
segments_root: SegmentTreeRoot,
erasure_root: ErasureRoot,
}
SendingSegmentShardRequest = 162 {
submission_id: EventId,
assurer: PeerId,
proofs: bool,
shards: BoundedVec<(ImportSegmentId, ShardIndex), MaxImportSegments>,
}
ReceivingSegmentShardRequest = 163 {
sender: PeerId,
proofs: bool,
}
SegmentShardRequestFailed = 164 {
request_id: EventId,
reason: Reason,
}
SegmentShardRequestSent = 165 {
request_id: EventId,
}
SegmentShardRequestReceived = 166 {
request_id: EventId,
num: u16,
}
SegmentShardsTransferred = 167 {
request_id: EventId,
}
ReconstructingSegments = 168 {
submission_id: EventId,
segments: BoundedVec<ImportSegmentId, MaxImportSegments>,
kind: ReconstructionKind,
}
SegmentReconstructionFailed = 169 {
reconstructing_id: EventId,
reason: Reason,
}
SegmentsReconstructed = 170 {
reconstructing_id: EventId,
}
SegmentVerificationFailed = 171 {
submission_id: EventId,
segments: BoundedVec<u16, MaxImports>,
reason: Reason,
}
SegmentsVerified = 172 {
submission_id: EventId,
segments: BoundedVec<u16, MaxImports>,
}
SendingSegmentRequest = 173 {
submission_id: EventId,
prev_guarantor: PeerId,
segments: BoundedVec<u16, MaxImportSegments>,
}
ReceivingSegmentRequest = 174 {
guarantor: PeerId,
}
SegmentRequestFailed = 175 {
request_id: EventId,
reason: Reason,
}
SegmentRequestSent = 176 {
request_id: EventId,
}
SegmentRequestReceived = 177 {
request_id: EventId,
num: u16,
}
SegmentsTransferred = 178 {
request_id: EventId,
}
PreimageAnnouncementFailed = 190 {
peer: PeerId,
announcer: ConnectionSide,
reason: Reason,
}
PreimageAnnounced = 191 {
peer: PeerId,
announcer: ConnectionSide,
service: ServiceId,
hash: Hash,
length: u32,
}
AnnouncedPreimageForgotten = 192 {
service: ServiceId,
hash: Hash,
length: u32,
reason: AnnouncedPreimageForgetReason,
}
SendingPreimageRequest = 193 {
recipient: PeerId,
hash: Hash,
}
ReceivingPreimageRequest = 194 {
sender: PeerId,
}
PreimageRequestFailed = 195 {
request_id: EventId,
reason: Reason,
}
PreimageRequestSent = 196 {
request_id: EventId,
}
PreimageRequestReceived = 197 {
request_id: EventId,
hash: Hash,
}
PreimageTransferred = 198 {
request_id: EventId,
length: u32,
}
PreimageDiscarded = 199 {
hash: Hash,
length: u32,
reason: PreimageDiscardReason,
}
}
impl Event {
pub fn next_id(&self, id: EventId) -> EventId {
let inc = match self {
Self::Dropped(dropped) => dropped.num,
_ => 1,
};
EventId(id.0 + inc)
}
}
#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen)]
pub struct TimestampedEvent {
pub timestamp: Timestamp,
pub event: Event,
}