Skip to content

Commit

Permalink
feat: 📝 implement cometstub crate (work-in-progress)
Browse files Browse the repository at this point in the history
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!                  /!\
/!\ ------------------------------------------------------- /!\
  • Loading branch information
cratelyn committed Jan 12, 2024
1 parent 5adb302 commit 5797a90
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
43 changes: 43 additions & 0 deletions crates/test/cometstub/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
168 changes: 168 additions & 0 deletions crates/test/cometstub/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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!("<BarkBark as Verifier>::verify_update_header")
}
fn verify_misbehaviour_header(
&self,
_untrusted: UntrustedBlockState<'_>,
_trusted: TrustedBlockState<'_>,
_options: &Options,
_now: Time,
) -> Verdict {
todo!("<BarkBark as Verifier>::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(())
}
}

0 comments on commit 5797a90

Please sign in to comment.