-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 📝 implement cometstub crate (work-in-progress)
this commit introduces a new library, in `crates/test/`. this library contains a mock implementation of cometbft, for use in cargo integration tests. * this does NOT add the `penumbra-cometstub` crate to the list of crates included in the rust documentation in `deployments/scripts/rust-docs`. the `penumbra-tct-property-test` crate was also not included in that list at the time of writing. /!\ ------------------------------------------------------- /!\ /!\ NOTE: this is a rolling work-in-progress. /!\ /!\ this branch will be force-pushed frequently until it is /!\ /!\ ready for review. proceed accordingly! /!\ /!\ ------------------------------------------------------- /!\ NOTE 24-01-02: i have brought over a test from `core/app/src/tests/spend.rs`. currently, it panics because proving keys could not be loaded. printlns and panics have been left to point out how far we can get through that test before failing. track down the core of this issue next week. use this command to see it break: ``` cargo watch -Bc -x 'test -p penumbra-cometstub spend_happy_path' ```
- Loading branch information
Showing
11 changed files
with
479 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
[package] | ||
name = "penumbra-cometstub" | ||
version = "0.64.1" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[features] | ||
default = ["component", "std"] | ||
component = [ | ||
# TODO(kate): are these feature flags needed? maybe. | ||
# "cnidarium", | ||
# "cnidarium-component", | ||
# "penumbra-proto/cnidarium", | ||
# "penumbra-chain/component", | ||
] | ||
std = ["ibc-types/std"] | ||
|
||
[dependencies] | ||
anyhow = "1" | ||
# ed25519 = "2.2.3" | ||
ed25519-consensus = "2.1.0" | ||
ibc-proto = { version = "0.40.0", default-features = false } | ||
ibc-types = { version = "0.11.0", default-features = false } | ||
rand_core = "0.6.3" | ||
sha2 = "0.10.8" | ||
tendermint = "0.34.0" | ||
tendermint-light-client-verifier = "0.34.0" | ||
|
||
[dev-dependencies] | ||
cnidarium = { path = "../../cnidarium" } | ||
cnidarium-component = { path = "../../cnidarium-component" } | ||
penumbra-app = { path = "../../core/app" } | ||
penumbra-chain = { path = "../../core/component/chain", features = ["component"] } | ||
penumbra-compact-block = { path = "../../core/component/compact-block" } | ||
penumbra-keys = { path = "../../core/keys" } | ||
penumbra-proof-params = { path = "../../crypto/proof-params", features = [ | ||
"bundled-proving-keys", | ||
"download-proving-keys", | ||
] } | ||
penumbra-sct = { path = "../../core/component/sct" } | ||
penumbra-shielded-pool = { path = "../../core/component/shielded-pool", features = ["component"] } | ||
penumbra-txhash = { path = "../../core/txhash" } | ||
rand_chacha = "0.3" | ||
rand_core = "0.6" | ||
tokio = { version = "1.21.1", features = ["full", "tracing"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//! ABCI- and ABCI++-related facilities. | ||
//! | ||
//! See the [ABCI++ specification][abci-spec] for more information. See ["Methods][abci-methods] | ||
//! for more information on ABCI methods. | ||
//! | ||
//! [abci-spec]: https://github.com/cometbft/cometbft/blob/main/spec/abci/README.md | ||
//! [abci-methods]: https://github.com/cometbft/cometbft/blob/main/spec/abci/abci++_methods.md | ||
// | ||
// TODO(kate): `tendermint::abci::request` types, stub these out as needed. | ||
// - apply_snapshot_chunk::ApplySnapshotChunk, | ||
// - begin_block::BeginBlock, | ||
// - check_tx::{CheckTx, CheckTxKind}, | ||
// - deliver_tx::DeliverTx, | ||
// - echo::Echo, | ||
// - end_block::EndBlock, | ||
// - extend_vote::ExtendVote, | ||
// - finalize_block::FinalizeBlock, | ||
// - info::Info, | ||
// - init_chain::InitChain, | ||
// - load_snapshot_chunk::LoadSnapshotChunk, | ||
// - offer_snapshot::OfferSnapshot, | ||
// - prepare_proposal::PrepareProposal, | ||
// - process_proposal::ProcessProposal, | ||
// - query::Query, | ||
// - set_option::SetOption, | ||
// - verify_vote_extension::VerifyVoteExtension, | ||
|
||
use tendermint::{ | ||
abci::{request::BeginBlock, types}, | ||
block::Round, | ||
Hash, | ||
}; | ||
|
||
#[allow(dead_code)] // XXX(kate) | ||
pub(crate) fn begin_block() -> BeginBlock { | ||
BeginBlock { | ||
hash: Hash::None, | ||
header: crate::header::header(), | ||
last_commit_info: types::CommitInfo { | ||
round: Round::default(), | ||
votes: vec![], | ||
}, | ||
byzantine_validators: vec![], | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
//! Facilities for generating tendermint [`Header`]s | ||
use tendermint::{ | ||
account, | ||
block::{self, Header}, | ||
chain, | ||
validator::Set, | ||
AppHash, Hash, Time, | ||
}; | ||
|
||
pub(crate) fn header() -> Header { | ||
let validators_hash = Set::new(vec![], None).hash(); | ||
Header { | ||
version: block::header::Version { block: 0, app: 0 }, | ||
chain_id: chain::Id::try_from("test").unwrap(), | ||
height: block::Height::default(), | ||
time: Time::unix_epoch(), | ||
last_block_id: None, | ||
last_commit_hash: None, | ||
data_hash: None, | ||
validators_hash, | ||
next_validators_hash: validators_hash, | ||
consensus_hash: Hash::None, | ||
app_hash: app_hash(), | ||
last_results_hash: None, | ||
evidence_hash: None, | ||
proposer_address: account::Id::new([ | ||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
]), | ||
} | ||
} | ||
|
||
// TODO(kate): informalsystems/tendermint-rs#1243 | ||
fn app_hash() -> AppHash { | ||
AppHash::try_from(vec![1, 2, 3]).expect("AppHash::try_from is infallible") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
//! `penumbra-cometstub` is an in-memory consensus engine for integration tests. | ||
// | ||
// TODO(kate): | ||
// - `tests/ibc_handshake.rs` contains a starter test case to build out. | ||
|
||
mod abci; | ||
mod header; | ||
mod state; | ||
|
||
use { | ||
rand_core::OsRng, | ||
tendermint::{block, chain, vote, AppHash, Hash, Time}, | ||
}; | ||
|
||
/// An in-memory consensus engine for integration tests. | ||
/// | ||
/// See the crate-level documentation for more information. | ||
pub struct Engine { | ||
/// Inner consensus state. | ||
#[allow(dead_code)] // XXX(kate) | ||
state: State, | ||
} | ||
|
||
/// Consensus state used by the [`Engine`] to generate [`Block`s][tendermint::Block]. | ||
#[allow(dead_code)] // XXX(kate) | ||
struct State { | ||
/// The chain identifier. | ||
chain_id: chain::Id, | ||
/// The initial [`block::Height`]. | ||
initial_height: block::Height, | ||
/// Metadata regarding the last block generated. | ||
last_block: Option<LastBlock>, | ||
/// The set of validators. | ||
validators: Validators, | ||
/// The latest app hash. | ||
app_hash: AppHash, | ||
/// The merkle root of the result of executing the previous block. | ||
last_results_hash: Option<Hash>, | ||
// TODO(kate): handle consensus parameters later. | ||
// consensus_params: (), | ||
// last_height_consensus_params_changed: block::Height, | ||
} | ||
|
||
/// Consensus [`State`] metadata regarding the last block generated. | ||
#[allow(dead_code)] // XXX(kate) | ||
struct LastBlock { | ||
height: block::Height, | ||
id: block::Id, | ||
time: Time, | ||
} | ||
|
||
/// Consensus [`State`] fields regarding for the validator set. | ||
#[allow(dead_code)] // XXX(kate) | ||
pub struct Validators { | ||
current: Vec<Validator>, | ||
// TODO(kate): we may want these fields too. | ||
// - next: Vec<Validator>, | ||
// - last: Vec<Validator>, | ||
// - last_height_validators_changed: block::Height, | ||
} | ||
|
||
/// A validator address with voting power. | ||
/// | ||
/// This is a [`tendermint::abci::types::Validator`], but with signing keys held in-memory. | ||
/// | ||
/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#validator) | ||
#[allow(dead_code)] // XXX(kate) | ||
pub struct Validator { | ||
/// The validator's address (the first 20 bytes of `SHA256(public_key)`). | ||
pub address: [u8; 20], | ||
/// The voting power of the validator. | ||
pub power: vote::Power, | ||
/// The validator's (private) signing key. | ||
signing_key: ed25519_consensus::SigningKey, | ||
} | ||
|
||
// === impl Engine === | ||
|
||
impl Engine { | ||
/// Returns a new [`Engine`]. | ||
pub fn new() -> Self { | ||
Self { | ||
state: State::new(), | ||
} | ||
} | ||
} | ||
|
||
// === impl State === | ||
|
||
impl State { | ||
/// Returns a new [`State`]. | ||
// | ||
// TODO(kate): what do we need at the start? avoid recreating a whole genesis file. | ||
// use a single validator key, no arbitrary genesis height, simple. | ||
pub fn new() -> Self { | ||
let app_hash: AppHash = b"placeholder-app-hash" | ||
.to_vec() | ||
.try_into() | ||
.expect("infallible"); | ||
|
||
let validators = { | ||
/// A hard-coded voting power assignment for our mock validator. | ||
// TODO(kate): upstream a constant `Power` constructor, if possible. | ||
const POWER: u32 = 100; | ||
let v = Validator::new(POWER.into()); | ||
Validators { current: vec![v] } | ||
}; | ||
|
||
State { | ||
chain_id: chain::Id::try_from("penumbra-cometstub").unwrap(), | ||
initial_height: 0_u32.into(), | ||
last_block: None, | ||
validators, | ||
app_hash, | ||
last_results_hash: None, | ||
} | ||
} | ||
} | ||
|
||
// === impl Validators === | ||
|
||
impl Validators { | ||
/// Returns a validator containing a single [`Validator`]. | ||
pub fn single() -> Self { | ||
let power = 100_u32.into(); | ||
Self { | ||
current: vec![ | ||
Validator::new(power), | ||
], | ||
} | ||
} | ||
|
||
pub fn new(validators: Vec<Validator>) -> Self { | ||
Self { | ||
current: validators, | ||
} | ||
} | ||
} | ||
|
||
impl FromIterator<Validator> for Validators { | ||
fn from_iter<T: IntoIterator<Item = Validator>>(iter: T) -> Self { | ||
let current = iter.into_iter().collect(); | ||
Self { current } | ||
} | ||
} | ||
|
||
// === impl Validator === | ||
|
||
impl Validator { | ||
/// Returns a [`Validator`] with the designated voting power. | ||
/// | ||
/// NB: This will generate a [`ed25519_consensus::SigningKey`] for this validator. | ||
pub fn new(power: vote::Power) -> Self { | ||
let signing_key = ed25519_consensus::SigningKey::new(OsRng); | ||
let address = Self::address_from_public_key(&signing_key); | ||
Validator { | ||
address, | ||
power, | ||
signing_key, | ||
} | ||
} | ||
|
||
// TODO(kate): tendermint-rs says addresses are the first 20 bytes of the sha256 hash of the | ||
// public key. track down where this is described in the spec. | ||
fn address_from_public_key(public_key: impl AsRef<[u8]>) -> [u8; 20] { | ||
use sha2::{Digest as _, Sha256}; | ||
let mut hasher = Sha256::new(); | ||
hasher.update(public_key); | ||
let hash = hasher.finalize(); | ||
hash[..20].try_into().expect("") | ||
} | ||
} | ||
|
||
// TODO(kate): `Into`, implementation for converting this into a tendermint::..Validator, and | ||
// for getting its key as a tendermint::..SigningKey. write those when needed. | ||
|
||
// === unit tests === | ||
|
||
#[cfg(test)] | ||
mod validator_tests { | ||
use super::*; | ||
#[test] | ||
fn single_validator_can_be_created() { | ||
let _ = Validators::single(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Oops, something went wrong.