Skip to content

Commit

Permalink
mock-consensus: 🌱 define genesis validator
Browse files Browse the repository at this point in the history
  • Loading branch information
cratelyn committed Feb 28, 2024
1 parent c078784 commit d17c790
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 21 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 63 additions & 15 deletions crates/core/app/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
// NB: Allow dead code, these are in fact shared by files in `tests/`.
#![allow(dead_code)]

use async_trait::async_trait;
use cnidarium::TempStorage;
use penumbra_app::{
app::App,
server::consensus::{Consensus, ConsensusService},
use {
async_trait::async_trait,
cnidarium::TempStorage,
penumbra_app::{
app::App,
server::consensus::{Consensus, ConsensusService},
},
penumbra_genesis::AppState,
penumbra_mock_consensus::TestNode,
std::ops::Deref,
tap::Tap,
tracing::{trace, warn},
};
use penumbra_genesis::AppState;
use penumbra_mock_consensus::TestNode;
use std::ops::Deref;

// Installs a tracing subscriber to log events until the returned guard is dropped.
pub fn set_tracing_subscriber() -> tracing::subscriber::DefaultGuard {
Expand Down Expand Up @@ -83,19 +87,63 @@ impl TempStorageExt for TempStorage {

/// Penumbra-specific extensions to the mock consensus builder.
pub trait BuilderExt: Sized {
/// The error thrown by [`with_penumbra_auto_app_state`]
type Error;
/// Add the provided Penumbra [`AppState`] to the builder.
///
/// This will inject any configured validators into the state before serializing it into bytes.
fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result<Self, Self::Error>;
}

impl BuilderExt for penumbra_mock_consensus::builder::Builder {
type Error = anyhow::Error;
fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result<Self, Self::Error> {
// what to do here?
// - read out list of abci/comet validators from the builder,
// - define a penumbra validator for each one
// - inject that into the penumbra app state
// - serialize to json and then call `with_app_state_bytes`
let app_state = serde_json::to_vec(&app_state)?;
Ok(self.app_state(app_state))
use penumbra_proto::penumbra::core::component::stake::v1 as pb;

// Take the list of genesis validators from the builder...
let validators = self
.validators
.iter()
.inspect(
|pb::Validator {
name,
enabled,
sequence_number,
..
}| {
// ...log the name of each...
trace!(%name, %enabled, %sequence_number, "injecting validator into app state")
},
)
.cloned()
.collect::<Vec<_>>()
.tap(|v| {
// ...or print a warning if there are not any validators.
if v.is_empty() {
warn!("`with_penumbra_auto_app_state` was called but builder has no validators")
}
});

// Add the validators to the app state.
let app_state: AppState = match app_state {
AppState::Checkpoint(_) => anyhow::bail!("checkpoint app state isn't supported"),
AppState::Content(mut content) => {
// Inject the builder's validators into the staking component's genesis state.
std::mem::replace(
&mut content.stake_content.validators,
validators
)
.tap(|overwritten| {
// Log a warning if this overwrote any validators already in the app state.
warn!(?overwritten, "`with_penumbra_auto_app_state` overwrote validators in the given AppState")
});
AppState::Content(content)
}
};

// Serialize the app state into bytes, and add it to the builder.
serde_json::to_vec(&app_state)
.map_err(Self::Error::from)
.map(|s| self.app_state(s))
}
}
21 changes: 21 additions & 0 deletions crates/core/app/tests/mock_consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// Note: these should eventually replace the existing test cases. mock consensus tests are placed
// here while the engine is still in development. See #3588.

use penumbra_ibc::component::ClientStateReadExt;
use penumbra_stake::component::validator_handler::ValidatorDataRead;

mod common;

use {
Expand Down Expand Up @@ -37,6 +40,24 @@ async fn mock_consensus_can_send_an_init_chain_request() -> anyhow::Result<()> {
Ok(())
}

/// Exercises that the mock consensus engine can provide a single genesis validator.
#[tokio::test]
async fn mock_consensus_can_define_a_genesis_validator() -> anyhow::Result<()> {
// Install a test logger, acquire some temporary storage, and start the test node.
let guard = common::set_tracing_subscriber();
let storage = TempStorage::new().await?;
let test_node = common::start_test_node(&storage).await?;

let identity_key = test_node.identity_key;
storage.latest_snapshot().get_validator_status(identity_key);

// Free our temporary storage.
drop(storage);
drop(guard);

Ok(())
}

/// Exercises that a series of empty blocks, with no validator set present, can be successfully
/// executed by the consensus service.
#[tokio::test]
Expand Down
6 changes: 6 additions & 0 deletions crates/test/mock-consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ license.workspace = true
[dependencies]
anyhow = { workspace = true }
bytes = { workspace = true }
decaf377-rdsa = { workspace = true }
ed25519-consensus = { workspace = true }
penumbra-proto = { workspace = true }
penumbra-keys = { workspace = true }
rand = {workspace = true}
rand_core = {workspace = true}
serde_json = { workspace = true }
tap = { workspace = true }
tendermint = { workspace = true }
Expand Down
58 changes: 52 additions & 6 deletions crates/test/mock-consensus/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
/// Most importantly, defines [`Builder::init_chain()`].
mod init_chain;

use {crate::TestNode, bytes::Bytes};
use {
crate::TestNode, bytes::Bytes, penumbra_proto::penumbra::core::component::stake::v1 as pb,
rand_core::OsRng,
};

/// A buider, used to prepare and instantiate a new [`TestNode`].
#[derive(Default)]
pub struct Builder {
app_state: Option<Bytes>,
pub app_state: Option<Bytes>,
pub validators: Vec<pb::Validator>,
}

impl TestNode<()> {
Expand All @@ -21,12 +25,54 @@ impl TestNode<()> {
}

impl Builder {
// TODO: add other convenience methods for validator config?

/// Creates a single validator with a randomly generated key.
pub fn single_validator(self) -> Self {
// this does not do anything yet
self
use penumbra_proto::core::keys::v1::{GovernanceKey, IdentityKey};

// Generate a spend authoration key.
let bytes = {
use {
decaf377_rdsa::VerificationKey,
penumbra_keys::keys::{SpendKey, SpendKeyBytes},
rand::Rng,
};
let spend_key = SpendKey::from(SpendKeyBytes(OsRng.gen()));
let spend_auth_key = spend_key.spend_auth_key();
let verification_key = VerificationKey::from(spend_auth_key);
verification_key.to_bytes().to_vec()
};

// Generate an identity key for the validator.
let identity_key = Some(IdentityKey { ik: bytes.clone() });

// Generate a governance key for the validator.
//
// NB: for now, we will use the same key for governance. See the documentation of
// `GovernanceKey` for more information about cold storage of validator keys.
let governance_key = Some(GovernanceKey { gk: bytes.clone() });

// Generate a consensus pubkey for the validator.
let consensus_key = {
let signing_key = ed25519_consensus::SigningKey::new(OsRng);
signing_key.as_bytes().as_slice().to_vec()
};

let validator = pb::Validator {
identity_key,
consensus_key,
enabled: true,
sequence_number: 0,
governance_key,
name: String::default(),
website: String::default(),
description: String::default(),
funding_streams: Vec::default(),
};

Self {
validators: vec![validator],
..self
}
}

/// Sets the `app_state_bytes` to send the ABCI application upon chain initialization.
Expand Down
1 change: 1 addition & 0 deletions crates/test/mock-consensus/src/builder/init_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl Builder {

let Self {
app_state: Some(app_state),
validators: _,
} = self
else {
bail!("builder was not fully initialized")
Expand Down

0 comments on commit d17c790

Please sign in to comment.