From a4464f12034daddcac9ded77a5aa42f2a085b122 Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Wed, 28 Feb 2024 17:02:23 -0500 Subject: [PATCH 1/8] =?UTF-8?q?app:=20=F0=9F=8E=B4=20add=20small=20doc=20c?= =?UTF-8?q?omment=20to=20`App::new`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/core/app/src/app/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core/app/src/app/mod.rs b/crates/core/app/src/app/mod.rs index 94771dbd47..e8c8bc74a8 100644 --- a/crates/core/app/src/app/mod.rs +++ b/crates/core/app/src/app/mod.rs @@ -56,6 +56,7 @@ pub struct App { } impl App { + /// Constructs a new application, using the provided [`Snapshot`]. pub async fn new(snapshot: Snapshot) -> Result { tracing::debug!("initializing App instance"); From 85dc813df9cca49bbf50249607719de69cf899d0 Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Wed, 28 Feb 2024 17:03:08 -0500 Subject: [PATCH 2/8] =?UTF-8?q?mock-consensus:=20=F0=9F=8C=B1=20define=20g?= =?UTF-8?q?enesis=20validator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #3816. this provides extension facilities to the mock consensus test node builder, which allows penumbra-app tests to define a single validator, and subsequently retrieve it from the chain state. see `mock_consensus_can_define_a_genesis_validator`. when run with `--no-capture` enabled, these logs will be visible: ``` 2024-02-28T23:00:43.751036Z DEBUG penumbra_stake::component::stake: adding validator identity to consensus set index, validator: penumbravalid172v76yyqwngcln2dxrs8ht0sjgswer3569yyhezgsz6aj97ecvqqyf3h9h at crates/core/component/stake/src/component/stake.rs:533 in penumbra_stake::component::stake::staking [...] 2024-02-28T23:00:43.776880Z INFO penumbra_app::server::consensus: genesis state is a full configuration at crates/core/app/src/server/consensus.rs:145 2024-02-28T23:00:43.780436Z DEBUG penumbra_app::app: finished committing state, jmt_root: RootHash("46dc0e9561f17eee61a2c13f517036d4d0a4c77c60362cb6cc165083675dcaf7") at crates/core/app/src/app/mod.rs:592 ``` logging facilities are provided so that helper warnings should be given to users that forget to call `with_penumbra_single_validator`, or provide an `AppState` object whose validator list would be overwritten. the `serde_json` dependency is removed from the mock consensus library, it is no longer used. a warning is added to the mock consensus library to note to future contributors that other penumbra dependencies should be avoided in that library. a new `http::Extensions` field is added to the builder. it is not used internally, but provides extension traits a place to hold additional state. * #3588 * #3816 --- Cargo.lock | 4 +- crates/core/app/Cargo.toml | 1 + crates/core/app/tests/common/mod.rs | 147 ++++++++++++++++-- crates/core/app/tests/mock_consensus.rs | 29 ++++ crates/test/mock-consensus/Cargo.toml | 7 +- crates/test/mock-consensus/src/builder.rs | 44 ++++-- .../mock-consensus/src/builder/init_chain.rs | 3 + crates/test/mock-consensus/src/lib.rs | 13 +- 8 files changed, 219 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f89d61475..f7fc7f009b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4713,6 +4713,7 @@ dependencies = [ "penumbra-transaction", "penumbra-txhash", "prost", + "rand 0.8.5", "rand_chacha 0.3.1", "rand_core 0.6.4", "regex", @@ -5270,7 +5271,8 @@ version = "0.68.0" dependencies = [ "anyhow", "bytes", - "serde_json", + "decaf377-rdsa", + "http", "tap", "tendermint", "tower", diff --git a/crates/core/app/Cargo.toml b/crates/core/app/Cargo.toml index 911d5a1476..b1379a44be 100644 --- a/crates/core/app/Cargo.toml +++ b/crates/core/app/Cargo.toml @@ -81,6 +81,7 @@ tracing = {workspace = true} ed25519-consensus = {workspace = true} penumbra-mock-consensus = {workspace = true} penumbra-mock-client = {workspace = true} +rand = {workspace = true} rand_core = {workspace = true} rand_chacha = {workspace = true} tap = {workspace = true} diff --git a/crates/core/app/tests/common/mod.rs b/crates/core/app/tests/common/mod.rs index f400c349fb..d7904e38c6 100644 --- a/crates/core/app/tests/common/mod.rs +++ b/crates/core/app/tests/common/mod.rs @@ -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 { @@ -47,7 +51,7 @@ pub async fn start_test_node(storage: &TempStorage) -> anyhow::Result Result; + /// Creates a single validator with a randomly generated key. + /// + /// This will set the builder's identity key. + fn with_penumbra_single_validator(self) -> Self; } impl BuilderExt for penumbra_mock_consensus::builder::Builder { type Error = anyhow::Error; fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result { - // 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 + .extensions + .get::>() + .ok_or_else(|| { + anyhow::anyhow!("`with_penumbra_auto_app_state` could not find validators") + })? + .clone() + .tap(|v| { + for pb::Validator { + name, + enabled, + sequence_number, + .. + } in v + { + // ...log the name of each... + trace!(%name, %enabled, %sequence_number, "injecting validator into app state"); + } + // ...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. + if !overwritten.is_empty() { + 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)) + } + + fn with_penumbra_single_validator(self) -> Self { + use { + decaf377_rdsa::VerificationKey, + penumbra_keys::keys::{SpendKey, SpendKeyBytes}, + penumbra_proto::{ + core::keys::v1::{GovernanceKey, IdentityKey}, + penumbra::core::component::stake::v1::Validator, + }, + rand::Rng, + rand_core::OsRng, + }; + + // Generate a spend authoration key. + let bytes = { + 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() + }; + + // Generate a validator entry using the generated key. + let validator = Validator { + identity_key: Some(IdentityKey { + ik: bytes.to_vec().clone(), + }), + governance_key: Some(GovernanceKey { + // 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. + gk: bytes.to_vec().clone(), + }), + consensus_key: { + let signing_key = ed25519_consensus::SigningKey::new(OsRng); + signing_key.as_bytes().as_slice().to_vec() + }, + enabled: true, + sequence_number: 0, + name: String::default(), + website: String::default(), + description: String::default(), + funding_streams: Vec::default(), + }; + let validators = vec![validator]; + + // Add the generated identity key and the validator information to the builder. + self.identity_key(bytes).extension(validators) + } +} + +pub trait TestNodeExt { + fn penumbra_identity_key(&self) -> penumbra_stake::IdentityKey; +} + +impl TestNodeExt for TestNode { + fn penumbra_identity_key(&self) -> penumbra_stake::IdentityKey { + self.identity_key() + .try_into() + .map(penumbra_stake::IdentityKey) + .expect("test node should have a valid identity key") } } diff --git a/crates/core/app/tests/mock_consensus.rs b/crates/core/app/tests/mock_consensus.rs index c68b8e3397..ccb37a6fd9 100644 --- a/crates/core/app/tests/mock_consensus.rs +++ b/crates/core/app/tests/mock_consensus.rs @@ -6,6 +6,7 @@ mod common; use { + self::common::TestNodeExt as _, anyhow::anyhow, cnidarium::TempStorage, penumbra_keys::test_keys, @@ -14,6 +15,7 @@ use { penumbra_proto::DomainType, penumbra_sct::component::{clock::EpochRead, tree::SctRead as _}, penumbra_shielded_pool::{OutputPlan, SpendPlan}, + penumbra_stake::component::validator_handler::ValidatorDataRead as _, penumbra_transaction::{ memo::MemoPlaintext, plan::MemoPlan, TransactionParameters, TransactionPlan, }, @@ -37,6 +39,33 @@ 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.penumbra_identity_key(); + match storage + .latest_snapshot() + .get_validator_state(&identity_key) + .tap(|_| info!(?identity_key, "getting validator state")) + .await? + .ok_or_else(|| anyhow!("genesis validator state was not found"))? + { + penumbra_stake::validator::State::Active => info!("genesis validator is active"), + other => panic!("unexpected genesis validator state, found: {other}"), + } + + // 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] diff --git a/crates/test/mock-consensus/Cargo.toml b/crates/test/mock-consensus/Cargo.toml index 5bbdd37521..773d211b10 100644 --- a/crates/test/mock-consensus/Cargo.toml +++ b/crates/test/mock-consensus/Cargo.toml @@ -7,10 +7,15 @@ repository.workspace = true homepage.workspace = true license.workspace = true +# NB: to avoid circular dependencies: do not add other `penumbra-*` crates +# as dependencies, below. we should provide interfaces in terms of generic +# types, and provide penumbra-specific helper functions via extension traits. + [dependencies] anyhow = { workspace = true } bytes = { workspace = true } -serde_json = { workspace = true } +decaf377-rdsa = { workspace = true } +http = { workspace = true } tap = { workspace = true } tendermint = { workspace = true } tower = { workspace = true, features = ["full"] } diff --git a/crates/test/mock-consensus/src/builder.rs b/crates/test/mock-consensus/src/builder.rs index 7d90e11c1f..53fe4e31c8 100644 --- a/crates/test/mock-consensus/src/builder.rs +++ b/crates/test/mock-consensus/src/builder.rs @@ -5,12 +5,21 @@ /// Most importantly, defines [`Builder::init_chain()`]. mod init_chain; -use {crate::TestNode, bytes::Bytes}; +use { + crate::TestNode, + bytes::Bytes, + decaf377_rdsa::{SpendAuth, VerificationKeyBytes}, + http::Extensions, + tap::TapOptional, + tracing::warn, +}; /// A buider, used to prepare and instantiate a new [`TestNode`]. #[derive(Default)] pub struct Builder { - app_state: Option, + pub app_state: Option, + pub identity_key: Option>, + pub extensions: Extensions, } impl TestNode<()> { @@ -21,17 +30,32 @@ 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 - } - /// Sets the `app_state_bytes` to send the ABCI application upon chain initialization. pub fn app_state(self, app_state: impl Into) -> Self { let app_state = Some(app_state.into()); Self { app_state, ..self } } + + /// Sets the test node's identity key. + pub fn identity_key(self, identity_key: impl Into>) -> Self { + let identity_key = Some(identity_key.into()); + Self { + identity_key, + ..self + } + } + + /// Adds an extension to this builder. + /// + /// This is not a part of "regular" use of this builder, but may be used to store additional + /// state to facilitate the implementation of extension traits around this builder. + pub fn extension(mut self, value: T) -> Self + where + T: Send + Sync + 'static, + { + self.extensions + .insert(value) + .tap_some(|_| warn!("builder overwrote an extension value, this is probably a bug!")); + self + } } diff --git a/crates/test/mock-consensus/src/builder/init_chain.rs b/crates/test/mock-consensus/src/builder/init_chain.rs index b60a7793fd..f7ec0bddec 100644 --- a/crates/test/mock-consensus/src/builder/init_chain.rs +++ b/crates/test/mock-consensus/src/builder/init_chain.rs @@ -32,6 +32,8 @@ impl Builder { let Self { app_state: Some(app_state), + identity_key: Some(identity_key), + extensions: _, } = self else { bail!("builder was not fully initialized") @@ -62,6 +64,7 @@ impl Builder { consensus, height: block::Height::from(0_u8), last_app_hash: app_hash.as_bytes().to_owned(), + identity_key: identity_key.try_into()?, }) } diff --git a/crates/test/mock-consensus/src/lib.rs b/crates/test/mock-consensus/src/lib.rs index 1bc9750200..9fc4a138e2 100644 --- a/crates/test/mock-consensus/src/lib.rs +++ b/crates/test/mock-consensus/src/lib.rs @@ -9,6 +9,11 @@ pub mod builder; mod abci; +use { + decaf377_rdsa::{SpendAuth, VerificationKey}, + tendermint::block::Height, +}; + /// A test node. /// /// Construct a new test node by calling [`TestNode::builder()`]. Use [`TestNode::block()`] to @@ -21,7 +26,8 @@ mod abci; pub struct TestNode { consensus: C, last_app_hash: Vec, - height: tendermint::block::Height, + height: Height, + identity_key: VerificationKey, } impl TestNode { @@ -38,4 +44,9 @@ impl TestNode { // - https://doc.rust-lang.org/std/fmt/#formatting-traits format!("{:02X?}", self.last_app_hash) } + + /// Returns this test node's identity key. + pub fn identity_key(&self) -> VerificationKey { + self.identity_key + } } From 3e8691de251b3460b35d64db9c2bfaa170bfdfae Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Fri, 1 Mar 2024 13:38:01 -0500 Subject: [PATCH 3/8] =?UTF-8?q?mock-consensus:=20=F0=9F=94=8D=20rework=20v?= =?UTF-8?q?alidator=20generation=20(review)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for context, see: - https://github.com/penumbra-zone/penumbra/pull/3902#issuecomment-1972239643 - https://github.com/penumbra-zone/penumbra/pull/3902#issuecomment-1972258397 Co-authored-by: Henry de Valence --- Cargo.lock | 2 + crates/core/app/tests/common/mod.rs | 141 +----------------- .../app/tests/common/test_node_builder_ext.rs | 120 +++++++++++++++ crates/core/app/tests/mock_consensus.rs | 32 ++-- crates/test/mock-consensus/Cargo.toml | 2 + crates/test/mock-consensus/src/builder.rs | 30 +--- .../mock-consensus/src/builder/init_chain.rs | 4 +- crates/test/mock-consensus/src/keyring.rs | 24 +++ crates/test/mock-consensus/src/lib.rs | 14 +- 9 files changed, 184 insertions(+), 185 deletions(-) create mode 100644 crates/core/app/tests/common/test_node_builder_ext.rs create mode 100644 crates/test/mock-consensus/src/keyring.rs diff --git a/Cargo.lock b/Cargo.lock index f7fc7f009b..bfba8debe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5272,7 +5272,9 @@ dependencies = [ "anyhow", "bytes", "decaf377-rdsa", + "ed25519-consensus", "http", + "rand_core 0.6.4", "tap", "tendermint", "tower", diff --git a/crates/core/app/tests/common/mod.rs b/crates/core/app/tests/common/mod.rs index d7904e38c6..f47e16d436 100644 --- a/crates/core/app/tests/common/mod.rs +++ b/crates/core/app/tests/common/mod.rs @@ -3,6 +3,8 @@ // NB: Allow dead code, these are in fact shared by files in `tests/`. #![allow(dead_code)] +pub use self::test_node_builder_ext::BuilderExt; + use { async_trait::async_trait, cnidarium::TempStorage, @@ -13,10 +15,13 @@ use { penumbra_genesis::AppState, penumbra_mock_consensus::TestNode, std::ops::Deref, - tap::Tap, - tracing::{trace, warn}, }; +/// Penumbra-specific extensions to the mock consensus builder. +/// +/// See [`BuilderExt`]. +mod test_node_builder_ext; + // Installs a tracing subscriber to log events until the returned guard is dropped. pub fn set_tracing_subscriber() -> tracing::subscriber::DefaultGuard { use tracing_subscriber::filter::EnvFilter; @@ -51,7 +56,7 @@ pub async fn start_test_node(storage: &TempStorage) -> anyhow::Result Result; - /// Creates a single validator with a randomly generated key. - /// - /// This will set the builder's identity key. - fn with_penumbra_single_validator(self) -> Self; -} - -impl BuilderExt for penumbra_mock_consensus::builder::Builder { - type Error = anyhow::Error; - fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result { - use penumbra_proto::penumbra::core::component::stake::v1 as pb; - - // Take the list of genesis validators from the builder... - let validators = self - .extensions - .get::>() - .ok_or_else(|| { - anyhow::anyhow!("`with_penumbra_auto_app_state` could not find validators") - })? - .clone() - .tap(|v| { - for pb::Validator { - name, - enabled, - sequence_number, - .. - } in v - { - // ...log the name of each... - trace!(%name, %enabled, %sequence_number, "injecting validator into app state"); - } - // ...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. - if !overwritten.is_empty() { - 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)) - } - - fn with_penumbra_single_validator(self) -> Self { - use { - decaf377_rdsa::VerificationKey, - penumbra_keys::keys::{SpendKey, SpendKeyBytes}, - penumbra_proto::{ - core::keys::v1::{GovernanceKey, IdentityKey}, - penumbra::core::component::stake::v1::Validator, - }, - rand::Rng, - rand_core::OsRng, - }; - - // Generate a spend authoration key. - let bytes = { - 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() - }; - - // Generate a validator entry using the generated key. - let validator = Validator { - identity_key: Some(IdentityKey { - ik: bytes.to_vec().clone(), - }), - governance_key: Some(GovernanceKey { - // 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. - gk: bytes.to_vec().clone(), - }), - consensus_key: { - let signing_key = ed25519_consensus::SigningKey::new(OsRng); - signing_key.as_bytes().as_slice().to_vec() - }, - enabled: true, - sequence_number: 0, - name: String::default(), - website: String::default(), - description: String::default(), - funding_streams: Vec::default(), - }; - let validators = vec![validator]; - - // Add the generated identity key and the validator information to the builder. - self.identity_key(bytes).extension(validators) - } -} - -pub trait TestNodeExt { - fn penumbra_identity_key(&self) -> penumbra_stake::IdentityKey; -} - -impl TestNodeExt for TestNode { - fn penumbra_identity_key(&self) -> penumbra_stake::IdentityKey { - self.identity_key() - .try_into() - .map(penumbra_stake::IdentityKey) - .expect("test node should have a valid identity key") - } -} diff --git a/crates/core/app/tests/common/test_node_builder_ext.rs b/crates/core/app/tests/common/test_node_builder_ext.rs new file mode 100644 index 0000000000..0644e771ec --- /dev/null +++ b/crates/core/app/tests/common/test_node_builder_ext.rs @@ -0,0 +1,120 @@ +use { + penumbra_genesis::AppState, + penumbra_mock_consensus::{builder::Builder, keyring::Keys}, + penumbra_proto::{ + core::keys::v1::{GovernanceKey, IdentityKey}, + penumbra::core::component::stake::v1::Validator as PenumbraValidator, + }, + tap::Tap, +}; + +/// 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; +} + +impl BuilderExt for Builder { + type Error = anyhow::Error; + fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result { + // Generate a penumbra validator using the test node's consensus keys (if they exist). + // Eventually, we may wish to generate and inject additional definitions, but only a single + // validator is supported for now. + let app_state = match self + .keys + .as_ref() + .map(generate_penumbra_validator) + .inspect(log_validator) + .map(std::iter::once) + { + Some(validator) => app_state_with_validators(app_state, validator)?, + None => app_state, + }; + + // 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)) + } +} + +/// Injects the given collection of [`Validator`s][PenumbraValidator] into the app state. +fn app_state_with_validators( + app_state: AppState, + validators: V, +) -> Result +where + V: IntoIterator, +{ + use AppState::{Checkpoint, Content}; + match app_state { + Checkpoint(_) => anyhow::bail!("checkpoint app state isn't supported"), + Content(mut content) => { + // Inject the builder's validators into the staking component's genesis state. + std::mem::replace( + &mut content.stake_content.validators, + validators.into_iter().collect(), + ) + .tap(|overwritten| { + // Log a warning if this overwrote any validators already in the app state. + if !overwritten.is_empty() { + tracing::warn!( + ?overwritten, + "`with_penumbra_auto_app_state` overwrote validators in the given AppState" + ) + } + }); + Ok(Content(content)) + } + } +} + +/// Generates a [`Validator`][PenumbraValidator] given a set of consensus [`Keys`]. +fn generate_penumbra_validator( + Keys { + consensus_verification_key, + .. + }: &Keys, +) -> PenumbraValidator { + /// A temporary stub for validator keys. + /// + /// 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. + const BYTES: [u8; 32] = [0; 32]; + + PenumbraValidator { + identity_key: Some(IdentityKey { + ik: BYTES.to_vec().clone(), + }), + governance_key: Some(GovernanceKey { + gk: BYTES.to_vec().clone(), + }), + consensus_key: consensus_verification_key.as_bytes().to_vec(), + enabled: true, + sequence_number: 0, + name: String::default(), + website: String::default(), + description: String::default(), + funding_streams: Vec::default(), + } +} + +fn log_validator( + PenumbraValidator { + name, + enabled, + sequence_number, + .. + }: &PenumbraValidator, +) { + tracing::trace!( + %name, + %enabled, + %sequence_number, + "injecting validator into app state" + ) +} diff --git a/crates/core/app/tests/mock_consensus.rs b/crates/core/app/tests/mock_consensus.rs index ccb37a6fd9..5a2c3490b2 100644 --- a/crates/core/app/tests/mock_consensus.rs +++ b/crates/core/app/tests/mock_consensus.rs @@ -6,7 +6,6 @@ mod common; use { - self::common::TestNodeExt as _, anyhow::anyhow, cnidarium::TempStorage, penumbra_keys::test_keys, @@ -45,18 +44,27 @@ 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 _test_node = common::start_test_node(&storage).await?; - let identity_key = test_node.penumbra_identity_key(); - match storage - .latest_snapshot() - .get_validator_state(&identity_key) - .tap(|_| info!(?identity_key, "getting validator state")) - .await? - .ok_or_else(|| anyhow!("genesis validator state was not found"))? - { - penumbra_stake::validator::State::Active => info!("genesis validator is active"), - other => panic!("unexpected genesis validator state, found: {other}"), + let snapshot = storage.latest_snapshot(); + let validators = snapshot + .validator_definitions() + .tap(|_| info!("getting validator definitions")) + .await?; + match validators.as_slice() { + [v] => { + let identity_key = v.identity_key; + let status = snapshot + .get_validator_state(&identity_key) + .await? + .ok_or_else(|| anyhow!("could not find validator status"))?; + assert_eq!( + status, + penumbra_stake::validator::State::Active, + "validator should be active" + ); + } + unexpected => panic!("there should be one validator, got: {unexpected:?}"), } // Free our temporary storage. diff --git a/crates/test/mock-consensus/Cargo.toml b/crates/test/mock-consensus/Cargo.toml index 773d211b10..ba3e27ce40 100644 --- a/crates/test/mock-consensus/Cargo.toml +++ b/crates/test/mock-consensus/Cargo.toml @@ -15,7 +15,9 @@ license.workspace = true anyhow = { workspace = true } bytes = { workspace = true } decaf377-rdsa = { workspace = true } +ed25519-consensus = { workspace = true } http = { workspace = true } +rand_core = { workspace = true } tap = { workspace = true } tendermint = { workspace = true } tower = { workspace = true, features = ["full"] } diff --git a/crates/test/mock-consensus/src/builder.rs b/crates/test/mock-consensus/src/builder.rs index 53fe4e31c8..29a22cedbb 100644 --- a/crates/test/mock-consensus/src/builder.rs +++ b/crates/test/mock-consensus/src/builder.rs @@ -6,20 +6,15 @@ mod init_chain; use { - crate::TestNode, + crate::{keyring::Keys, TestNode}, bytes::Bytes, - decaf377_rdsa::{SpendAuth, VerificationKeyBytes}, - http::Extensions, - tap::TapOptional, - tracing::warn, }; /// A buider, used to prepare and instantiate a new [`TestNode`]. #[derive(Default)] pub struct Builder { pub app_state: Option, - pub identity_key: Option>, - pub extensions: Extensions, + pub keys: Option, } impl TestNode<()> { @@ -36,26 +31,11 @@ impl Builder { Self { app_state, ..self } } - /// Sets the test node's identity key. - pub fn identity_key(self, identity_key: impl Into>) -> Self { - let identity_key = Some(identity_key.into()); + /// Generates a single set of validator keys. + pub fn single_validator(self) -> Self { Self { - identity_key, + keys: Some(Keys::generate()), ..self } } - - /// Adds an extension to this builder. - /// - /// This is not a part of "regular" use of this builder, but may be used to store additional - /// state to facilitate the implementation of extension traits around this builder. - pub fn extension(mut self, value: T) -> Self - where - T: Send + Sync + 'static, - { - self.extensions - .insert(value) - .tap_some(|_| warn!("builder overwrote an extension value, this is probably a bug!")); - self - } } diff --git a/crates/test/mock-consensus/src/builder/init_chain.rs b/crates/test/mock-consensus/src/builder/init_chain.rs index f7ec0bddec..5d5e02ee3d 100644 --- a/crates/test/mock-consensus/src/builder/init_chain.rs +++ b/crates/test/mock-consensus/src/builder/init_chain.rs @@ -32,8 +32,7 @@ impl Builder { let Self { app_state: Some(app_state), - identity_key: Some(identity_key), - extensions: _, + keys: _, } = self else { bail!("builder was not fully initialized") @@ -64,7 +63,6 @@ impl Builder { consensus, height: block::Height::from(0_u8), last_app_hash: app_hash.as_bytes().to_owned(), - identity_key: identity_key.try_into()?, }) } diff --git a/crates/test/mock-consensus/src/keyring.rs b/crates/test/mock-consensus/src/keyring.rs new file mode 100644 index 0000000000..e3fb6b4e9d --- /dev/null +++ b/crates/test/mock-consensus/src/keyring.rs @@ -0,0 +1,24 @@ +pub struct Keys { + pub consensus_signing_key: ed25519_consensus::SigningKey, + pub consensus_verification_key: ed25519_consensus::VerificationKey, +} + +impl Keys { + pub fn generate() -> Self { + Self::generate_with(rand_core::OsRng) + } + + /// Generates a set of keys using the provided random number generator. + pub fn generate_with(rng: R) -> Self + where + R: rand_core::RngCore + rand_core::CryptoRng, + { + let consensus_signing_key = ed25519_consensus::SigningKey::new(rng); + let consensus_verification_key = consensus_signing_key.verification_key(); + + Self { + consensus_signing_key, + consensus_verification_key, + } + } +} diff --git a/crates/test/mock-consensus/src/lib.rs b/crates/test/mock-consensus/src/lib.rs index 9fc4a138e2..9ef65dcb2f 100644 --- a/crates/test/mock-consensus/src/lib.rs +++ b/crates/test/mock-consensus/src/lib.rs @@ -6,14 +6,10 @@ pub mod block; pub mod builder; +pub mod keyring; mod abci; -use { - decaf377_rdsa::{SpendAuth, VerificationKey}, - tendermint::block::Height, -}; - /// A test node. /// /// Construct a new test node by calling [`TestNode::builder()`]. Use [`TestNode::block()`] to @@ -26,8 +22,7 @@ use { pub struct TestNode { consensus: C, last_app_hash: Vec, - height: Height, - identity_key: VerificationKey, + height: tendermint::block::Height, } impl TestNode { @@ -44,9 +39,4 @@ impl TestNode { // - https://doc.rust-lang.org/std/fmt/#formatting-traits format!("{:02X?}", self.last_app_hash) } - - /// Returns this test node's identity key. - pub fn identity_key(&self) -> VerificationKey { - self.identity_key - } } From 3bd72e8b35dfa8fb560471800e036d847c345a6c Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Fri, 1 Mar 2024 16:13:24 -0500 Subject: [PATCH 4/8] =?UTF-8?q?mock-consensus:=20=F0=9F=91=8B=20remove=20f?= =?UTF-8?q?rivolous=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 -- crates/test/mock-consensus/Cargo.toml | 2 -- 2 files changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfba8debe4..327b07cbca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5271,9 +5271,7 @@ version = "0.68.0" dependencies = [ "anyhow", "bytes", - "decaf377-rdsa", "ed25519-consensus", - "http", "rand_core 0.6.4", "tap", "tendermint", diff --git a/crates/test/mock-consensus/Cargo.toml b/crates/test/mock-consensus/Cargo.toml index ba3e27ce40..4cec69ae7e 100644 --- a/crates/test/mock-consensus/Cargo.toml +++ b/crates/test/mock-consensus/Cargo.toml @@ -14,9 +14,7 @@ license.workspace = true [dependencies] anyhow = { workspace = true } bytes = { workspace = true } -decaf377-rdsa = { workspace = true } ed25519-consensus = { workspace = true } -http = { workspace = true } rand_core = { workspace = true } tap = { workspace = true } tendermint = { workspace = true } From 91bc6e72c89533247efb771b0fc2f05af4b48850 Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Thu, 7 Mar 2024 09:43:39 -0500 Subject: [PATCH 5/8] =?UTF-8?q?tests:=20=F0=9F=93=B4=20pass=20`Verificatio?= =?UTF-8?q?nKey`=20directly=20(review)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/penumbra-zone/penumbra/pull/3902#discussion_r1513620850 --- crates/core/app/tests/common/test_node_builder_ext.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/core/app/tests/common/test_node_builder_ext.rs b/crates/core/app/tests/common/test_node_builder_ext.rs index 0644e771ec..741693168d 100644 --- a/crates/core/app/tests/common/test_node_builder_ext.rs +++ b/crates/core/app/tests/common/test_node_builder_ext.rs @@ -1,6 +1,6 @@ use { penumbra_genesis::AppState, - penumbra_mock_consensus::{builder::Builder, keyring::Keys}, + penumbra_mock_consensus::builder::Builder, penumbra_proto::{ core::keys::v1::{GovernanceKey, IdentityKey}, penumbra::core::component::stake::v1::Validator as PenumbraValidator, @@ -27,7 +27,7 @@ impl BuilderExt for Builder { let app_state = match self .keys .as_ref() - .map(generate_penumbra_validator) + .map(|keys| generate_penumbra_validator(keys.consensus_verification_key)) .inspect(log_validator) .map(std::iter::once) { @@ -75,10 +75,7 @@ where /// Generates a [`Validator`][PenumbraValidator] given a set of consensus [`Keys`]. fn generate_penumbra_validator( - Keys { - consensus_verification_key, - .. - }: &Keys, + consensus_verification_key: ed25519_consensus::VerificationKey, ) -> PenumbraValidator { /// A temporary stub for validator keys. /// From 718fecff073986ab58a5de02ecc4f3676bef2124 Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Thu, 7 Mar 2024 10:00:22 -0500 Subject: [PATCH 6/8] tests: add note about invalid identity key https://github.com/penumbra-zone/penumbra/pull/3902/files#r1513611082 > This isn't a valid key, which may cause problems. > > We should instead generate a random signing key, get its verification > key, use that for the identity key, and throw the signing key away > after. a stub is intentionally used here until we have test coverage exercising this. this /will/ cause problems, but i explicitly want to know when! i want to see how far we get before we run into this being an issue. --- .../core/app/tests/common/test_node_builder_ext.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/core/app/tests/common/test_node_builder_ext.rs b/crates/core/app/tests/common/test_node_builder_ext.rs index 741693168d..d09849bf0f 100644 --- a/crates/core/app/tests/common/test_node_builder_ext.rs +++ b/crates/core/app/tests/common/test_node_builder_ext.rs @@ -79,16 +79,23 @@ fn generate_penumbra_validator( ) -> PenumbraValidator { /// A temporary stub for validator keys. /// + /// An invalid key is intentionally provided here, until we have test coverage exercising the + /// use of these keys. Once we need it we will: + /// - generate a random signing key + /// - get its verification key + /// - use that for the identity key + /// - throw the signing key away + /// /// 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. - const BYTES: [u8; 32] = [0; 32]; + const INVALID_KEY_BYTES: [u8; 32] = [0; 32]; PenumbraValidator { identity_key: Some(IdentityKey { - ik: BYTES.to_vec().clone(), + ik: INVALID_KEY_BYTES.to_vec().clone(), }), governance_key: Some(GovernanceKey { - gk: BYTES.to_vec().clone(), + gk: INVALID_KEY_BYTES.to_vec().clone(), }), consensus_key: consensus_verification_key.as_bytes().to_vec(), enabled: true, From e666286978905dccf685381da57ac59438ff67ca Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Thu, 7 Mar 2024 11:24:27 -0500 Subject: [PATCH 7/8] =?UTF-8?q?mock-consensus:=20=F0=9F=8E=B9=20replace=20?= =?UTF-8?q?`Keys`=20with=20a=20`Keyring`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this addresses a few assorted threads from review: https://github.com/penumbra-zone/penumbra/pull/3902/files#r1513620298 https://github.com/penumbra-zone/penumbra/pull/3902/files#r1513615058 https://github.com/penumbra-zone/penumbra/pull/3902/files#r1513613617 https://github.com/penumbra-zone/penumbra/pull/3902/files#r1513617136 see also, #3934. this replaces the `Keys` structure, and defines a new `Keyring` type. this is added to the builder and used by the `with_penumbra_auto_app_state` extension, but it not yet introduced to `TestNode`. --- .../app/tests/common/test_node_builder_ext.rs | 60 +++++----- crates/test/mock-consensus/src/builder.rs | 6 +- .../mock-consensus/src/builder/init_chain.rs | 2 +- crates/test/mock-consensus/src/keyring.rs | 113 +++++++++++++++--- 4 files changed, 128 insertions(+), 53 deletions(-) diff --git a/crates/core/app/tests/common/test_node_builder_ext.rs b/crates/core/app/tests/common/test_node_builder_ext.rs index d09849bf0f..2ff8e7d7cd 100644 --- a/crates/core/app/tests/common/test_node_builder_ext.rs +++ b/crates/core/app/tests/common/test_node_builder_ext.rs @@ -5,7 +5,6 @@ use { core::keys::v1::{GovernanceKey, IdentityKey}, penumbra::core::component::stake::v1::Validator as PenumbraValidator, }, - tap::Tap, }; /// Penumbra-specific extensions to the mock consensus builder. @@ -21,18 +20,21 @@ pub trait BuilderExt: Sized { impl BuilderExt for Builder { type Error = anyhow::Error; fn with_penumbra_auto_app_state(self, app_state: AppState) -> Result { - // Generate a penumbra validator using the test node's consensus keys (if they exist). - // Eventually, we may wish to generate and inject additional definitions, but only a single - // validator is supported for now. - let app_state = match self - .keys - .as_ref() - .map(|keys| generate_penumbra_validator(keys.consensus_verification_key)) - .inspect(log_validator) - .map(std::iter::once) - { - Some(validator) => app_state_with_validators(app_state, validator)?, - None => app_state, + let Self { keyring, .. } = &self; + + let app_state = if keyring.is_empty() { + // If there are no consensus keys to inject, pass along the provided app state. + app_state + } else { + // Otherwise, generate a penumbra validator for each entry in the keyring... + let validators = keyring + .verification_keys() + .cloned() + .map(generate_penumbra_validator) + .inspect(log_validator) + .collect::>(); + // ...and then inject these validators into the app state. + inject_penumbra_validators(app_state, validators)? }; // Serialize the app state into bytes, and add it to the builder. @@ -43,31 +45,23 @@ impl BuilderExt for Builder { } /// Injects the given collection of [`Validator`s][PenumbraValidator] into the app state. -fn app_state_with_validators( +fn inject_penumbra_validators( app_state: AppState, - validators: V, -) -> Result -where - V: IntoIterator, -{ + validators: Vec, +) -> Result { use AppState::{Checkpoint, Content}; match app_state { Checkpoint(_) => anyhow::bail!("checkpoint app state isn't supported"), Content(mut content) => { - // Inject the builder's validators into the staking component's genesis state. - std::mem::replace( - &mut content.stake_content.validators, - validators.into_iter().collect(), - ) - .tap(|overwritten| { - // Log a warning if this overwrote any validators already in the app state. - if !overwritten.is_empty() { - tracing::warn!( - ?overwritten, - "`with_penumbra_auto_app_state` overwrote validators in the given AppState" - ) - } - }); + // Inject the builder's validators into the staking component's genesis state... + let overwritten = std::mem::replace(&mut content.stake_content.validators, validators); + // ...and log a warning if this overwrote any validators already in the app state. + if !overwritten.is_empty() { + tracing::warn!( + ?overwritten, + "`with_penumbra_auto_app_state` overwrote validators in the given AppState" + ) + } Ok(Content(content)) } } diff --git a/crates/test/mock-consensus/src/builder.rs b/crates/test/mock-consensus/src/builder.rs index 29a22cedbb..eeb444d3ee 100644 --- a/crates/test/mock-consensus/src/builder.rs +++ b/crates/test/mock-consensus/src/builder.rs @@ -6,7 +6,7 @@ mod init_chain; use { - crate::{keyring::Keys, TestNode}, + crate::{keyring::Keyring, TestNode}, bytes::Bytes, }; @@ -14,7 +14,7 @@ use { #[derive(Default)] pub struct Builder { pub app_state: Option, - pub keys: Option, + pub keyring: Keyring, } impl TestNode<()> { @@ -34,7 +34,7 @@ impl Builder { /// Generates a single set of validator keys. pub fn single_validator(self) -> Self { Self { - keys: Some(Keys::generate()), + keyring: Keyring::new_with_size(1), ..self } } diff --git a/crates/test/mock-consensus/src/builder/init_chain.rs b/crates/test/mock-consensus/src/builder/init_chain.rs index 5d5e02ee3d..1a9c280852 100644 --- a/crates/test/mock-consensus/src/builder/init_chain.rs +++ b/crates/test/mock-consensus/src/builder/init_chain.rs @@ -32,7 +32,7 @@ impl Builder { let Self { app_state: Some(app_state), - keys: _, + keyring: _, } = self else { bail!("builder was not fully initialized") diff --git a/crates/test/mock-consensus/src/keyring.rs b/crates/test/mock-consensus/src/keyring.rs index e3fb6b4e9d..9839c1d613 100644 --- a/crates/test/mock-consensus/src/keyring.rs +++ b/crates/test/mock-consensus/src/keyring.rs @@ -1,24 +1,105 @@ -pub struct Keys { - pub consensus_signing_key: ed25519_consensus::SigningKey, - pub consensus_verification_key: ed25519_consensus::VerificationKey, -} +//! Provides a [`Keyring`] for managing consensus keys. + +use { + ed25519_consensus::{SigningKey, VerificationKey}, + rand_core::{CryptoRng, OsRng, RngCore}, + std::collections::btree_map::{self, BTreeMap}, +}; + +/// A keyring of [`VerificationKey`] and [`SigningKey`] consensus keys. +#[derive(Clone, Debug, Default)] +pub struct Keyring(BTreeMap); + +/// An entry in a [`Keyring`]. +pub type Entry = (VerificationKey, SigningKey); + +impl Keyring { + /// Creates a new [`Keyring`]. + pub fn new() -> Self { + Self::default() + } + + /// Creates a new [`Keyring`] and fills with with `n` random entries. + pub fn new_with_size(n: usize) -> Self { + let gen = || Self::generate_key(OsRng); + std::iter::repeat_with(gen).take(n).collect() + } + + /// Returns the consensus signing key corresponding to the given verification key. + pub fn get(&self, verification_key: &VerificationKey) -> Option<&SigningKey> { + self.0.get(verification_key) + } + + /// Returns `true` if the keyring contains no elements. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Gets an iterator over the consensus verification keys in the keyring. + pub fn verification_keys(&self) -> impl Iterator { + self.0.keys() + } + + /// Gets an iterator over the consensus signing keys in the keyring. + pub fn signing_keys(&self) -> impl Iterator { + self.0.values() + } + + /// Generates a new key using the default [`OsRng`], and inserts it into the keyring. + /// + /// This returns the verification key associated with this new entry. + pub fn add_key(&mut self) -> VerificationKey { + self.add_key_with(OsRng) + } -impl Keys { - pub fn generate() -> Self { - Self::generate_with(rand_core::OsRng) + /// Generates a new key with the provided CSPRNG, and inserts it into the keyring. + /// + /// This returns the verification key associated with this new entry. + pub fn add_key_with(&mut self, rng: R) -> VerificationKey + where + R: RngCore + CryptoRng, + { + let (vk, sk) = Self::generate_key(rng); + self.0.insert(vk, sk); + vk } - /// Generates a set of keys using the provided random number generator. - pub fn generate_with(rng: R) -> Self + /// Generates a new consensus key. + pub fn generate_key(rng: R) -> Entry where - R: rand_core::RngCore + rand_core::CryptoRng, + R: RngCore + CryptoRng, { - let consensus_signing_key = ed25519_consensus::SigningKey::new(rng); - let consensus_verification_key = consensus_signing_key.verification_key(); + let sk = ed25519_consensus::SigningKey::new(rng); + let vk = sk.verification_key(); + tracing::trace!(verification_key = ?vk, "generated consensus key"); + (vk, sk) + } +} - Self { - consensus_signing_key, - consensus_verification_key, - } +type KeyringIter<'a> = btree_map::Iter<'a, VerificationKey, SigningKey>; +impl<'a> IntoIterator for &'a Keyring { + type Item = (&'a VerificationKey, &'a SigningKey); + type IntoIter = KeyringIter<'a>; + fn into_iter(self) -> KeyringIter<'a> { + self.0.iter() + } +} + +type KeyringIntoIter = btree_map::IntoIter; +impl IntoIterator for Keyring { + type Item = (VerificationKey, SigningKey); + type IntoIter = KeyringIntoIter; + fn into_iter(self) -> KeyringIntoIter { + self.0.into_iter() + } +} + +impl FromIterator for Keyring { + fn from_iter(iter: I) -> Keyring + where + I: IntoIterator, + { + let k = iter.into_iter().collect(); + Self(k) } } From 52ba4766de9ef3811ee7aa6e62fadf1a023e261b Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Thu, 7 Mar 2024 11:28:49 -0500 Subject: [PATCH 8/8] =?UTF-8?q?tests:=20=F0=9F=93=99=20fix=20stale=20comme?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/core/app/tests/common/test_node_builder_ext.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/app/tests/common/test_node_builder_ext.rs b/crates/core/app/tests/common/test_node_builder_ext.rs index 2ff8e7d7cd..b527c37928 100644 --- a/crates/core/app/tests/common/test_node_builder_ext.rs +++ b/crates/core/app/tests/common/test_node_builder_ext.rs @@ -67,9 +67,9 @@ fn inject_penumbra_validators( } } -/// Generates a [`Validator`][PenumbraValidator] given a set of consensus [`Keys`]. +/// Generates a [`Validator`][PenumbraValidator] given a consensus verification key. fn generate_penumbra_validator( - consensus_verification_key: ed25519_consensus::VerificationKey, + consensus_key: ed25519_consensus::VerificationKey, ) -> PenumbraValidator { /// A temporary stub for validator keys. /// @@ -91,7 +91,7 @@ fn generate_penumbra_validator( governance_key: Some(GovernanceKey { gk: INVALID_KEY_BYTES.to_vec().clone(), }), - consensus_key: consensus_verification_key.as_bytes().to_vec(), + consensus_key: consensus_key.as_bytes().to_vec(), enabled: true, sequence_number: 0, name: String::default(),