From ad7d109d3a0b8199266bef2bb674a8e2337d53fa Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Mon, 26 Feb 2024 11:29:14 -0500 Subject: [PATCH] =?UTF-8?q?mock-consensus:=20=E2=AD=90=20use=20transaction?= =?UTF-8?q?=20plan=20in=20tests=20(#3866)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #3588 * #3788 * #3857 * #3880 * #3881 --- crates/core/app/tests/mock_consensus.rs | 86 ++++++++++------------- crates/core/transaction/src/plan.rs | 1 + crates/core/transaction/src/plan/build.rs | 21 ++++++ crates/core/transaction/src/plan/spend.rs | 2 + crates/test/mock-client/src/lib.rs | 2 +- crates/test/mock-consensus/src/lib.rs | 2 +- 6 files changed, 62 insertions(+), 52 deletions(-) create mode 100644 crates/core/transaction/src/plan/spend.rs diff --git a/crates/core/app/tests/mock_consensus.rs b/crates/core/app/tests/mock_consensus.rs index e4b12d1a08..3b7c682c68 100644 --- a/crates/core/app/tests/mock_consensus.rs +++ b/crates/core/app/tests/mock_consensus.rs @@ -10,7 +10,12 @@ use { cnidarium::TempStorage, penumbra_keys::test_keys, penumbra_mock_client::MockClient, + penumbra_proto::DomainType, penumbra_sct::component::clock::EpochRead, + penumbra_shielded_pool::SpendPlan, + penumbra_transaction::TransactionPlan, + std::ops::Deref, + tap::Tap, tracing::{error_span, Instrument}, }; @@ -78,71 +83,52 @@ async fn mock_consensus_can_send_a_spend_action() -> anyhow::Result<()> { let mut rng = ::seed_from_u64(0xBEEF); // Sync the mock client, using the test account's full viewing key, to the latest snapshot. - let MockClient { notes, sct, .. } = MockClient::new(test_keys::FULL_VIEWING_KEY.clone()) + let (viewing_key, spend_key) = (&test_keys::FULL_VIEWING_KEY, &test_keys::SPEND_KEY); + let client = MockClient::new(viewing_key.deref().clone()) .with_sync_to_storage(&storage) .await?; // Take one of the test account's notes... - let note = notes - .values() - .cloned() + let (commitment, note) = client + .notes + .iter() .next() - .ok_or_else(|| anyhow!("mock client had no note"))?; - let asset_id = note.asset_id(); - let proof = sct - .witness(note.commit()) - .ok_or_else(|| anyhow!("index is not witnessed"))?; - - // ...and use it to craft a `Spend`. - let (spend, spend_key) = { - use {decaf377_rdsa::SigningKey, penumbra_shielded_pool::SpendPlan}; - let spend_plan = SpendPlan::new(&mut rng, note, proof.position()); - let auth_sig = test_keys::SPEND_KEY - .spend_auth_key() - .randomize(&spend_plan.randomizer) - .sign(&mut rng, [0u8; 64].as_ref()); - let spend = spend_plan.spend(&test_keys::FULL_VIEWING_KEY, auth_sig, proof, sct.root()); - let key = SigningKey::from(spend_plan.value_blinding); - (spend, key) - }; + .ok_or_else(|| anyhow!("mock client had no note"))? + .tap(|(commitment, note)| { + tracing::info!(?commitment, ?note, "mock client note commitment") + }); - // Next, craft a transaction, containing this `Spend`. + // Build a transaction spending this note. let tx = { - use { - penumbra_asset::Value, - penumbra_fee::Fee, - penumbra_num::Amount, - penumbra_transaction::{Action, Transaction, TransactionBody, TransactionParameters}, - penumbra_txhash::AuthorizingData, - }; - let transaction_parameters = TransactionParameters { - expiry_height: 0, - chain_id: "i-wonder-if-this-is-load-bearing".to_owned(), - fee: Fee(Value { - amount: Amount::zero(), - asset_id, - }), - }; - let transaction_body = TransactionBody { - actions: vec![Action::Spend(spend)], - transaction_parameters, + let position = client + .sct + .witness(*commitment) + .ok_or_else(|| anyhow!("commitment is not witnessed"))? + .position(); + let spend = SpendPlan::new(&mut rng, note.clone(), position); + let plan = TransactionPlan { + actions: vec![spend.into()], ..Default::default() }; - let binding_sig = spend_key.sign(rng, transaction_body.auth_hash().as_bytes()); - let transaction = Transaction { - transaction_body, - binding_sig, - anchor: sct.root(), - }; - ::encode_to_vec(&transaction) + let witness = plan.witness_data(&client.sct)?; + let auth = plan.authorize(rand_core::OsRng, spend_key)?; + plan.build_concurrent(viewing_key, &witness, &auth).await? }; // Execute the transaction, and sync another mock client up to the latest snapshot. - test_node.block().with_data(vec![tx]).execute().await?; - MockClient::new(test_keys::FULL_VIEWING_KEY.clone()) + test_node + .block() + .with_data(vec![tx.encode_to_vec()]) // TODO(kate): add a `with_tx` extension method + .execute() + .await?; + + // Sync to the latest storage snapshot once more. + let client = MockClient::new(test_keys::FULL_VIEWING_KEY.clone()) .with_sync_to_storage(&storage) .await?; + client.notes.get(&commitment).unwrap(); + // Free our temporary storage. drop(storage); drop(guard); diff --git a/crates/core/transaction/src/plan.rs b/crates/core/transaction/src/plan.rs index 0e57ae7b90..a383447a40 100644 --- a/crates/core/transaction/src/plan.rs +++ b/crates/core/transaction/src/plan.rs @@ -27,6 +27,7 @@ mod build; mod clue; mod detection_data; mod memo; +mod spend; pub use action::ActionPlan; pub use clue::CluePlan; diff --git a/crates/core/transaction/src/plan/build.rs b/crates/core/transaction/src/plan/build.rs index 76d638ed7d..1980f92e15 100644 --- a/crates/core/transaction/src/plan/build.rs +++ b/crates/core/transaction/src/plan/build.rs @@ -190,4 +190,25 @@ impl TransactionPlan { // 4. Return the completed transaction. Ok(tx) } + + /// Returns a [`WitnessData`], which may be used to build this transaction. + pub fn witness_data(&self, sct: &penumbra_tct::Tree) -> Result { + let anchor = sct.root(); + + let witness_note = |spend: &penumbra_shielded_pool::SpendPlan| { + let commitment = spend.note.commit(); + sct.witness(commitment) + .ok_or_else(|| anyhow::anyhow!("commitment should exist in tree")) + .map(|proof| (commitment, proof)) + }; + let state_commitment_proofs = self + .spend_plans() + .map(witness_note) + .collect::>()?; + + Ok(WitnessData { + anchor, + state_commitment_proofs, + }) + } } diff --git a/crates/core/transaction/src/plan/spend.rs b/crates/core/transaction/src/plan/spend.rs new file mode 100644 index 0000000000..f5565df95f --- /dev/null +++ b/crates/core/transaction/src/plan/spend.rs @@ -0,0 +1,2 @@ +// use penumbra_tct::Tree; +// use {super::*, penumbra_proto::DomainType, penumbra_shielded_pool::SpendPlan}; diff --git a/crates/test/mock-client/src/lib.rs b/crates/test/mock-client/src/lib.rs index 141dd08e10..b938346b49 100644 --- a/crates/test/mock-client/src/lib.rs +++ b/crates/test/mock-client/src/lib.rs @@ -10,7 +10,7 @@ use std::collections::BTreeMap; /// A bare-bones mock client for use exercising the state machine. pub struct MockClient { latest_height: u64, - fvk: FullViewingKey, + pub fvk: FullViewingKey, pub notes: BTreeMap, swaps: BTreeMap, pub sct: penumbra_tct::Tree, diff --git a/crates/test/mock-consensus/src/lib.rs b/crates/test/mock-consensus/src/lib.rs index e58c930237..4fd88998cb 100644 --- a/crates/test/mock-consensus/src/lib.rs +++ b/crates/test/mock-consensus/src/lib.rs @@ -4,10 +4,10 @@ // // see penumbra-zone/penumbra#3588. +pub mod block; pub mod builder; mod abci; -mod block; /// A test node. ///