From 5797a9070e505a98d1595a720ae34b5d003704dc Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Mon, 8 Jan 2024 12:40:14 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=93=9D=20implement=20cometstub=20?= =?UTF-8?q?crate=20(work-in-progress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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! /!\ /!\ ------------------------------------------------------- /!\ --- Cargo.lock | 23 +++++ Cargo.toml | 1 + crates/test/cometstub/Cargo.toml | 43 ++++++++ crates/test/cometstub/src/lib.rs | 168 +++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 crates/test/cometstub/Cargo.toml create mode 100644 crates/test/cometstub/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 501ee450dd..609409a021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5091,6 +5091,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "penumbra-cometstub" +version = "0.64.1" +dependencies = [ + "anyhow", + "cnidarium", + "cnidarium-component", + "ibc-proto", + "ibc-types", + "penumbra-app", + "penumbra-chain", + "penumbra-compact-block", + "penumbra-keys", + "penumbra-sct", + "penumbra-shielded-pool", + "penumbra-txhash", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "tendermint", + "tendermint-light-client-verifier", + "tokio", +] + [[package]] name = "penumbra-community-pool" version = "0.64.1" diff --git a/Cargo.toml b/Cargo.toml index e6ac21c3c2..773f503d07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "crates/bin/pclientd", "crates/bin/pcli", "crates/wasm", + "crates/test/cometstub", "crates/test/tct-property-test", "crates/misc/measure", "crates/misc/tct-visualize", diff --git a/crates/test/cometstub/Cargo.toml b/crates/test/cometstub/Cargo.toml new file mode 100644 index 0000000000..67b9d4680f --- /dev/null +++ b/crates/test/cometstub/Cargo.toml @@ -0,0 +1,43 @@ +[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 = [ + # "cnidarium", + # "cnidarium-component", + # "penumbra-proto/cnidarium", + # "penumbra-chain/component", +] +std = ["ibc-types/std"] + +[dependencies] +# Workspace dependencies + +# Penumbra dependencies +ibc-proto = { version = "0.40.0", default-features = false } +ibc-types = { version = "0.11.0", default-features = false } + +# Crates.io deps +anyhow = "1" +tendermint = "0.34.0" +# tendermint-proto = "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-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"] } diff --git a/crates/test/cometstub/src/lib.rs b/crates/test/cometstub/src/lib.rs new file mode 100644 index 0000000000..bf12b90c69 --- /dev/null +++ b/crates/test/cometstub/src/lib.rs @@ -0,0 +1,168 @@ +use tendermint::{ + abci::{request::BeginBlock, types::CommitInfo}, + account, + block::{Header, Height, Round}, + chain, + validator::Set, + AppHash, Hash, Time, +}; +use tendermint_light_client_verifier::{ + options::Options, + types::{TrustedBlockState, UntrustedBlockState}, + Verdict, Verifier, +}; + +pub fn begin_block() -> BeginBlock { + BeginBlock { + hash: Hash::None, + header: header(), + last_commit_info: CommitInfo { + round: Round::default(), + votes: vec![], + }, + byzantine_validators: vec![], + } +} + +fn header() -> Header { + use tendermint::block::header::Version; + Header { + version: Version { block: 0, app: 0 }, + chain_id: chain::Id::try_from("test").unwrap(), + height: Height::default(), + time: Time::unix_epoch(), + last_block_id: None, + last_commit_hash: None, + data_hash: None, + validators_hash: 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, + ]), + } +} + +fn validators() -> Set { + Set::new(vec![], None) +} + +fn app_hash() -> AppHash { + AppHash::try_from(vec![1, 2, 3]).unwrap() + // AppHash::try_from is infallible, see: https://github.com/informalsystems/tendermint-rs/issues/1243 +} + +pub struct TestVerifier; +impl Verifier for TestVerifier { + fn verify_update_header( + &self, + _untrusted: UntrustedBlockState<'_>, + _trusted: TrustedBlockState<'_>, + _options: &Options, + _now: Time, + ) -> Verdict { + todo!("::verify_update_header") + } + fn verify_misbehaviour_header( + &self, + _untrusted: UntrustedBlockState<'_>, + _trusted: TrustedBlockState<'_>, + _options: &Options, + _now: Time, + ) -> Verdict { + todo!("::verify_misbehaviour_header") + } +} + +#[cfg(test)] +mod tests { + // use tendermint_light_client_verifier:: + use cnidarium::{ArcStateDeltaExt, StateDelta, TempStorage}; + use cnidarium_component::{ActionHandler as _, Component}; + use penumbra_app::{MockClient, TempStorageExt}; + use penumbra_chain::component::StateWriteExt; + use penumbra_compact_block::component::CompactBlockManager; + use penumbra_keys::{test_keys, PayloadKey}; + use penumbra_sct::component::SourceContext; + use penumbra_shielded_pool::{component::ShieldedPool, SpendPlan}; + use penumbra_txhash::{AuthorizingData, EffectHash, TransactionContext}; + use rand_core::SeedableRng; + use std::{ops::Deref, sync::Arc}; + use tendermint::abci; + + #[test] + fn begin_block_works() { + let _ = super::begin_block(); + // next, parse this block via a light client + } + + // XXX(kate): copied from `crates/core/app/src/tests/spend.rs` + #[tokio::test] + async fn spend_happy_path() -> anyhow::Result<()> { + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(1312); + + let storage = TempStorage::new().await?.apply_default_genesis().await?; + let mut state = Arc::new(StateDelta::new(storage.latest_snapshot())); + + let height = 1; + + // Precondition: This test uses the default genesis which has existing notes for the test keys. + let mut client = MockClient::new(test_keys::FULL_VIEWING_KEY.clone()); + let sk = test_keys::SPEND_KEY.clone(); + client.sync_to(0, state.deref()).await?; + let note = client.notes.values().next().unwrap().clone(); + let note_commitment = note.commit(); + let proof = client.sct.witness(note_commitment).unwrap(); + let root = client.sct.root(); + let tct_position = proof.position(); + + // 1. Simulate BeginBlock + let mut state_tx = state.try_begin_transaction().unwrap(); + state_tx.put_block_height(height); + state_tx.put_epoch_by_height( + height, + penumbra_chain::Epoch { + index: 0, + start_height: 0, + }, + ); + state_tx.apply(); + + // 2. Create a Spend action + let spend_plan = SpendPlan::new(&mut rng, note, tct_position); + let dummy_effect_hash = [0u8; 64]; + let rsk = sk.spend_auth_key().randomize(&spend_plan.randomizer); + let auth_sig = rsk.sign(&mut rng, dummy_effect_hash.as_ref()); + let spend = spend_plan.spend(&test_keys::FULL_VIEWING_KEY, auth_sig, proof, root); + let transaction_context = TransactionContext { + anchor: root, + effect_hash: EffectHash(dummy_effect_hash), + }; + + // 3. Simulate execution of the Spend action + spend.check_stateless(transaction_context).await?; + spend.check_stateful(state.clone()).await?; + let mut state_tx = state.try_begin_transaction().unwrap(); + state_tx.put_mock_source(1u8); + spend.execute(&mut state_tx).await?; + state_tx.apply(); + + // 4. Execute EndBlock + + let end_block = abci::request::EndBlock { + height: height.try_into().unwrap(), + }; + ShieldedPool::end_block(&mut state, &end_block).await; + + let mut state_tx = state.try_begin_transaction().unwrap(); + // ... and for the App, call `finish_block` to correctly write out the SCT with the data we'll use next. + state_tx.finish_block(false).await.unwrap(); + + state_tx.apply(); + + Ok(()) + } +}