Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: Add accessor for runtime state #5915

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5915.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
runtime: Add accessor for runtime state
64 changes: 63 additions & 1 deletion runtime/src/consensus/roothash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
crypto::{hash::Hash, signature::PublicKey},
namespace::Namespace,
},
consensus::state::StateError,
consensus::{registry::Runtime, scheduler::Committee, state::StateError},
};

// Modules.
Expand Down Expand Up @@ -111,6 +111,68 @@ impl MessageEvent {
}
}

/// Per-runtime state.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
#[cbor(allow_unknown)]
pub struct RuntimeState {
/// Latest per-epoch runtime descriptor.
pub runtime: Runtime,
/// Flag indicating whether the runtime is currently suspended.
#[cbor(optional)]
pub suspended: bool,

// Runtime's first block.
pub genesis_block: Block,

/// Runtime's most recently finalized block.
pub last_block: Block,
/// Height at which the runtime's most recent block was finalized.
pub last_block_height: i64,

/// Runtime round which was normally processed by the runtime. This is also the round that
/// contains the message results for the last processed runtime messages.
pub last_normal_round: u64,
/// Consensus block height corresponding to `last_normal_round`.
pub last_normal_height: i64,

/// Committee the executor pool is collecting commitments for.
#[cbor(optional)]
pub commitee: Option<Committee>,
// NOTE: Commitment pool deserialization is currently not supported.
/// Consensus height at which the round is scheduled for forced finalization.
#[cbor(optional)]
pub next_timeout: i64,

/// Liveness statistics for the current epoch.
pub liveness_stats: Option<LivenessStatistics>,
}

/// Per-epoch liveness statistics for nodes.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct LivenessStatistics {
/// Total number of rounds in the last epoch, excluding any rounds generated by the roothash
/// service itself.
pub total_rounds: u64,

/// A list of counters, specified in committee order (e.g. counter at index i has the value for
/// node i in the committee).
pub good_rounds: Vec<u64>,

/// A list that records the number of finalized rounds when a node acted as a proposed with the
/// highest rank.
///
/// The list is ordered according to the committee arrangement (i.e., the counter at index i
/// holds the value for the node at index i in the committee).
pub finalized_proposals: Vec<u64>,

/// A list that records the number of failed rounds when a node/ acted as a proposer with the
/// highest rank.
///
/// The list is ordered according to the committee arrangement (i.e., the counter at index i
/// holds the value for the node at index i in the committee).
pub missed_proposals: Vec<u64>,
}

/// Information about how a particular round was executed by the consensus layer.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct RoundResults {
Expand Down
2 changes: 2 additions & 0 deletions runtime/src/consensus/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum Role {
}

/// A node participating in a committee.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct CommitteeNode {
/// The node's role in a committee.
pub role: Role,
Expand All @@ -44,6 +45,7 @@ pub enum CommitteeKind {
}

/// A per-runtime (instance) committee.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct Committee {
/// The functionality a committee exists to provide.
pub kind: CommitteeKind,
Expand Down
45 changes: 44 additions & 1 deletion runtime/src/consensus/state/roothash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
namespace::Namespace,
},
consensus::{
roothash::{Error, RoundResults, RoundRoots},
roothash::{Error, RoundResults, RoundRoots, RuntimeState},
state::StateError,
},
key_format,
Expand All @@ -29,11 +29,25 @@ impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
}
}

key_format!(RuntimeKeyFmt, 0x20, Hash);
key_format!(StateRootKeyFmt, 0x25, Hash);
key_format!(LastRoundResultsKeyFmt, 0x27, Hash);
key_format!(PastRootsKeyFmt, 0x2a, (Hash, u64));

impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
/// Returns the latest runtime state.
pub fn runtime_state(&self, id: Namespace) -> Result<RuntimeState, Error> {
match self
.mkvs
.get(&RuntimeKeyFmt(Hash::digest_bytes(id.as_ref())).encode())
{
Ok(Some(b)) => cbor::from_slice_non_strict(&b)
.map_err(|err| StateError::Unavailable(anyhow!(err)).into()),
Ok(None) => Err(Error::InvalidRuntime(id)),
Err(err) => Err(StateError::Unavailable(anyhow!(err)).into()),
}
}

/// Returns the state root for a specific runtime.
pub fn state_root(&self, id: Namespace) -> Result<Hash, Error> {
match self
Expand Down Expand Up @@ -132,6 +146,35 @@ mod test {
let runtime_id =
Namespace::from("8000000000000000000000000000000000000000000000000000000000000010");

// Test fetching runtime state.
let runtime_state = state
.runtime_state(runtime_id)
.expect("runtime state query should work");
println!("{:?}", runtime_state);
assert_eq!(runtime_state.runtime.id, runtime_id);
assert_eq!(runtime_state.suspended, false);
assert_eq!(runtime_state.genesis_block.header.round, 1);
assert_eq!(
runtime_state.genesis_block.header.io_root,
Hash::digest_bytes(format!("genesis").as_bytes())
);
assert_eq!(
runtime_state.genesis_block.header.state_root,
Hash::digest_bytes(format!("genesis").as_bytes())
);
assert_eq!(runtime_state.last_block.header.round, 10);
assert_eq!(
runtime_state.last_block.header.io_root,
Hash::digest_bytes(format!("io 10").as_bytes())
);
assert_eq!(
runtime_state.last_block.header.state_root,
Hash::digest_bytes(format!("state 10").as_bytes())
);
assert_eq!(runtime_state.last_block_height, 90);
assert_eq!(runtime_state.last_normal_round, 10);
assert_eq!(runtime_state.last_normal_height, 90);

// Test fetching past round roots.
let past_round_roots = state
.past_round_roots(runtime_id)
Expand Down
Loading