Skip to content

Commit

Permalink
Merge pull request #5915 from oasisprotocol/kostko/feature/rt-consens…
Browse files Browse the repository at this point in the history
…us-roothash-state-accessor

runtime: Add accessor for runtime state
  • Loading branch information
kostko authored Oct 21, 2024
2 parents 3509d52 + e45eac6 commit a950040
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
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

0 comments on commit a950040

Please sign in to comment.