Skip to content

Commit

Permalink
mock-consensus: 💰 execute a spend via mock consensus (#3857)
Browse files Browse the repository at this point in the history
~f/ixes #3788.~

this introduces a new `mock_consensus_can_send_a_spend_action` test
which, as the name implies, shows that the mock consensus harness can
send and execute a `Spend` action via our consensus service.

a `MockClient::with_sync_to_storage` helper (bikesheds are welcome here)
is introduced, to facilitate syncing to the latest snapshot of the
temporary storage.

* #3588
* #3788
  • Loading branch information
cratelyn authored Feb 22, 2024
1 parent c34c4ec commit 1381ca8
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 1 deletion.
84 changes: 84 additions & 0 deletions crates/core/app/tests/mock_consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
mod common;

use {
anyhow::anyhow,
cnidarium::TempStorage,
penumbra_keys::test_keys,
penumbra_mock_client::MockClient,
penumbra_sct::component::clock::EpochRead,
tracing::{error_span, Instrument},
};
Expand Down Expand Up @@ -65,3 +68,84 @@ async fn mock_consensus_can_send_a_sequence_of_empty_blocks() -> anyhow::Result<

Ok(())
}

#[tokio::test]
async fn mock_consensus_can_send_a_spend_action() -> 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 mut test_node = common::start_test_node(&storage).await?;
let mut rng = <rand_chacha::ChaChaRng as rand_core::SeedableRng>::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())
.with_sync_to_storage(&storage)
.await?;

// Take one of the test account's notes...
let note = notes
.values()
.cloned()
.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)
};

// Next, craft a transaction, containing this `Spend`.
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,
..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(),
};
<Transaction as penumbra_proto::DomainType>::encode_to_vec(&transaction)
};

// 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())
.with_sync_to_storage(&storage)
.await?;

// Free our temporary storage.
drop(storage);
drop(guard);

Ok(())
}
14 changes: 13 additions & 1 deletion crates/test/mock-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use cnidarium::StateRead;
use penumbra_compact_block::{component::StateReadExt as _, CompactBlock, StatePayload};
use penumbra_dex::swap::SwapPlaintext;
use penumbra_keys::FullViewingKey;
use penumbra_sct::component::tree::SctRead;
use penumbra_sct::component::{clock::EpochRead, tree::SctRead};
use penumbra_shielded_pool::{note, Note};
use penumbra_tct as tct;
use std::collections::BTreeMap;
Expand All @@ -27,6 +27,18 @@ impl MockClient {
}
}

pub async fn with_sync_to_storage(
mut self,
storage: impl AsRef<cnidarium::Storage>,
) -> anyhow::Result<Self> {
let latest = storage.as_ref().latest_snapshot();
let height = latest.get_block_height().await?;
let state = cnidarium::StateDelta::new(latest);
self.sync_to(height, state).await?;

Ok(self)
}

pub async fn sync_to<R: StateRead>(
&mut self,
target_height: u64,
Expand Down

0 comments on commit 1381ca8

Please sign in to comment.