Skip to content

Commit

Permalink
feat(wasm): split-up build (#3289)
Browse files Browse the repository at this point in the history
  • Loading branch information
TalDerei committed Nov 17, 2023
1 parent e7e9633 commit 2347ecb
Show file tree
Hide file tree
Showing 22 changed files with 1,072 additions and 617 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 0 additions & 2 deletions crates/bin/pcli/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use penumbra_proto::{
};
use penumbra_transaction::{plan::TransactionPlan, Id as TransactionId, Transaction};
use penumbra_view::ViewClient;
use rand_core::OsRng;
use std::future::Future;
use tonic::transport::{Channel, ClientTlsConfig};
use tracing::instrument;
Expand All @@ -31,7 +30,6 @@ impl App {
&self.config.full_viewing_key,
self.view.as_mut().expect("view service initialized"),
&mut self.custody,
OsRng,
plan,
);
async move {
Expand Down
4 changes: 4 additions & 0 deletions crates/bin/pcli/tests/network_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,15 @@ fn transaction_send_from_addr_0_to_addr_1() {

let tvp: ProtoTransactionView = serde_json::value::from_value(view_json).unwrap();
let tv: TransactionView = tvp.try_into().unwrap();
// TODO: the first may no longer be a spend because of ordering changes.
// Let's not try to fix this at the moment. Later we can put a "canonical ordering" into the planner.
/*
// There will be a lot of ActionViews in the body... let's just check that one is a Spend.
assert!(matches!(
&tv.body_view.action_views[0],
penumbra_transaction::ActionView::Spend(_)
));
*/

// Inspect the TransactionView and ensure that we can read the memo text.
let mv = tv
Expand Down
36 changes: 12 additions & 24 deletions crates/core/app/src/action_handler/actions/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use penumbra_proto::DomainType;
use penumbra_sct::component::StateReadExt as _;
use penumbra_shielded_pool::component::SupplyWrite;
use penumbra_storage::{StateDelta, StateRead, StateWrite};
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};

use penumbra_transaction::plan::TransactionPlan;
use penumbra_transaction::Transaction;
Expand Down Expand Up @@ -324,31 +323,20 @@ static DAO_FULL_VIEWING_KEY: Lazy<FullViewingKey> = Lazy::new(|| {
FullViewingKey::from_components(ak, nk)
});

/// The seed used for the random number generator used when constructing transactions made by the
/// DAO.
///
/// It is arbitary, but must be deterministic in order to ensure that every node in the network
/// constructs a byte-for-byte identical transaction.
const DAO_TRANSACTION_RNG_SEED: &[u8; 32] = b"Penumbra DAO's tx build rng seed";

async fn build_dao_transaction(transaction_plan: TransactionPlan) -> Result<Transaction> {
let effect_hash = transaction_plan.effect_hash(&DAO_FULL_VIEWING_KEY);
transaction_plan
.build(
&DAO_FULL_VIEWING_KEY,
WitnessData {
anchor: penumbra_tct::Tree::new().root(),
state_commitment_proofs: Default::default(),
},
)?
.authorize(
&mut ChaCha20Rng::from_seed(*DAO_TRANSACTION_RNG_SEED),
&AuthorizationData {
effect_hash,
spend_auths: Default::default(),
delegator_vote_auths: Default::default(),
},
)
transaction_plan.build(
&DAO_FULL_VIEWING_KEY,
&WitnessData {
anchor: penumbra_tct::Tree::new().root(),
state_commitment_proofs: Default::default(),
},
&AuthorizationData {
effect_hash,
spend_auths: Default::default(),
delegator_vote_auths: Default::default(),
},
)
}

#[cfg(test)]
Expand Down
14 changes: 4 additions & 10 deletions crates/core/app/src/action_handler/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,10 @@ mod tests {
})
.collect(),
};
let mut rng = OsRng;
let tx = plan
.build_concurrent(&mut rng, fvk, witness_data)
.build_concurrent(fvk, &witness_data, &auth_data)
.await
.expect("can build transaction")
.authorize(&mut rng, &auth_data)
.expect("can authorize transaction");
.expect("can build transaction");

let context = tx.context();

Expand Down Expand Up @@ -241,13 +238,10 @@ mod tests {
})
.collect(),
};
let mut rng = OsRng;
let mut tx = plan
.build_concurrent(&mut rng, fvk, witness_data)
.build_concurrent(fvk, &witness_data, &auth_data)
.await
.expect("can build transaction")
.authorize(&mut rng, &auth_data)
.expect("can authorize transaction");
.expect("can build transaction");

// Set the anchor to the wrong root.
tx.anchor = wrong_root;
Expand Down
1 change: 1 addition & 0 deletions crates/core/component/dex/src/lp/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ impl PositionRewardClaimPlan {
todo!()
}

// FIXME: why is there an auth sig here??
/// Convenience method to construct the [`PositionRewardClaim`] described by this [`PositionRewardClaimPlan`].
pub fn position_reward_claim(
&self,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/keys/src/symmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl PayloadKind {
/// Represents a symmetric `ChaCha20Poly1305` key.
///
/// Used for encrypting and decrypting notes, swaps, memos, and memo keys.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PayloadKey(Key);

impl PayloadKey {
Expand Down
1 change: 1 addition & 0 deletions crates/core/transaction/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ serde_json = "1"
tracing = "0.1"
tokio = { version = "1.21.1", features = ["full"], optional = true }
clap = { version = "3", features = ["derive"], optional = true }
wasm-bindgen-test = "0.3.37"

[dev-dependencies]
proptest = "1"
Expand Down
4 changes: 3 additions & 1 deletion crates/core/transaction/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use penumbra_asset::balance;
use penumbra_proto::{core::transaction::v1alpha1 as pb, DomainType, TypeUrl};

use crate::{ActionView, IsAction, TransactionPerspective};
use serde::{Deserialize, Serialize};

/// An action performed by a Penumbra transaction.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "pb::Action", into = "pb::Action")]
#[allow(clippy::large_enum_variant)]
pub enum Action {
Output(penumbra_shielded_pool::Output),
Expand Down
138 changes: 26 additions & 112 deletions crates/core/transaction/src/effect_hash.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use blake2b_simd::Params;
use decaf377_fmd::Clue;
use penumbra_chain::EffectHash;
use penumbra_dao::{DaoDeposit, DaoOutput, DaoSpend};
Expand All @@ -11,16 +10,17 @@ use penumbra_governance::{
DelegatorVote, DelegatorVoteBody, Proposal, ProposalDepositClaim, ProposalSubmit,
ProposalWithdraw, ValidatorVote, ValidatorVoteBody, Vote,
};
use penumbra_ibc::IbcRelay;
use penumbra_keys::{FullViewingKey, PayloadKey};
use penumbra_proto::TypeUrl;
use penumbra_proto::{
core::component::dex::v1alpha1 as pbd, core::component::fee::v1alpha1 as pbf,
core::component::governance::v1alpha1 as pbg, core::component::ibc::v1alpha1 as pbi,
core::component::shielded_pool::v1alpha1 as pb_sp, core::component::stake::v1alpha1 as pbs,
core::transaction::v1alpha1 as pbt, crypto::decaf377_fmd::v1alpha1 as pb_fmd, Message,
};
use penumbra_proto::{DomainType, TypeUrl};
use penumbra_shielded_pool::{output, spend, Ics20Withdrawal};
use penumbra_stake::{Delegate, Undelegate, UndelegateClaimBody};
use penumbra_stake::{validator, Delegate, Undelegate, UndelegateClaimBody};

use crate::{
memo::MemoCiphertext, plan::TransactionPlan, transaction::DetectionData, Action, Transaction,
Expand Down Expand Up @@ -139,101 +139,19 @@ impl TransactionPlan {
let num_actions = self.actions.len() as u32;
state.update(&num_actions.to_le_bytes());

// TransactionPlan::build builds the actions sorted by type, so hash the
// actions in the order they'll appear in the final transaction.
for spend in self.spend_plans() {
state.update(spend.spend_body(fvk).effect_hash().as_bytes());
}

// If the memo_key is None, then there is no memo, and we populate the memo key
// field with a dummy key.
let dummy_payload_key: PayloadKey = [0u8; 32].into();
for output in self.output_plans() {
state.update(
output
.output_body(
fvk.outgoing(),
memo_key.as_ref().unwrap_or(&dummy_payload_key),
)
.effect_hash()
.as_bytes(),
);
}
for swap in self.swap_plans() {
state.update(swap.swap_body(fvk).effect_hash().as_bytes());
}
for swap_claim in self.swap_claim_plans() {
state.update(swap_claim.swap_claim_body(fvk).effect_hash().as_bytes());
}
for delegation in self.delegations() {
state.update(delegation.effect_hash().as_bytes());
}
for undelegation in self.undelegations() {
state.update(undelegation.effect_hash().as_bytes());
}
for plan in self.undelegate_claim_plans() {
state.update(plan.undelegate_claim_body().effect_hash().as_bytes());
}
for proposal_submit in self.proposal_submits() {
state.update(proposal_submit.effect_hash().as_bytes());
}
for proposal_withdraw in self.proposal_withdraws() {
state.update(proposal_withdraw.effect_hash().as_bytes());
}
for validator_vote in self.validator_votes() {
state.update(validator_vote.effect_hash().as_bytes());
}
for delegator_vote in self.delegator_vote_plans() {
state.update(
delegator_vote
.delegator_vote_body(fvk)
.effect_hash()
.as_bytes(),
);
}
for proposal_deposit_claim in self.proposal_deposit_claims() {
state.update(proposal_deposit_claim.effect_hash().as_bytes());
}
// These are data payloads, so just hash them directly,
// since they are effecting data.
for payload in self.validator_definitions() {
let effect_hash = Params::default()
.personal(b"PAH:valdefnition")
.hash(&payload.encode_to_vec());
state.update(effect_hash.as_bytes());
}
for payload in self.ibc_actions() {
let effect_hash = Params::default()
.personal(b"PAH:ibc_action")
.hash(&payload.encode_to_vec());
state.update(effect_hash.as_bytes());
}
for dao_spend in self.dao_spends() {
state.update(dao_spend.effect_hash().as_bytes());
}
for dao_output in self.dao_outputs() {
state.update(dao_output.effect_hash().as_bytes());
}
for dao_deposit in self.dao_deposits() {
state.update(dao_deposit.effect_hash().as_bytes());
}
for position_open in self.position_openings() {
state.update(position_open.effect_hash().as_bytes());
}
for position_close in self.position_closings() {
state.update(position_close.effect_hash().as_bytes());
}
for position_withdraw in self.position_withdrawals() {

// Hash the effecting data of each action, in the order it appears in the plan,
// which will be the order it appears in the transaction.
for action_plan in &self.actions {
state.update(
position_withdraw
.position_withdraw()
.effect_hash()
action_plan
.effect_hash(fvk, memo_key.as_ref().unwrap_or(&dummy_payload_key))
.as_bytes(),
);
}
for ics20_withdrawal in self.ics20_withdrawals() {
state.update(ics20_withdrawal.effect_hash().as_bytes());
}

EffectHash(state.finalize().as_array().clone())
}
Expand All @@ -254,22 +172,8 @@ impl EffectingData for Action {
Action::ValidatorVote(vote) => vote.effect_hash(),
Action::SwapClaim(swap_claim) => swap_claim.body.effect_hash(),
Action::Swap(swap) => swap.body.effect_hash(),
// These are data payloads, so just hash them directly,
// since we consider them authorizing data.
Action::ValidatorDefinition(payload) => EffectHash(
Params::default()
.personal(b"PAH:valdefnition")
.hash(&payload.encode_to_vec())
.as_array()
.clone(),
),
Action::IbcRelay(payload) => EffectHash(
Params::default()
.personal(b"PAH:ibc_action")
.hash(&payload.encode_to_vec())
.as_array()
.clone(),
),
Action::ValidatorDefinition(defn) => defn.effect_hash(),
Action::IbcRelay(payload) => payload.effect_hash(),
Action::PositionOpen(p) => p.effect_hash(),
Action::PositionClose(p) => p.effect_hash(),
Action::PositionWithdraw(p) => p.effect_hash(),
Expand Down Expand Up @@ -305,6 +209,20 @@ fn create_personalized_state(personalization: &str) -> blake2b_simd::State {
state
}

impl EffectingData for validator::Definition {
fn effect_hash(&self) -> EffectHash {
let effecting_data: pbs::ValidatorDefinition = self.clone().into();
hash_proto_effecting_data(validator::Definition::TYPE_URL, &effecting_data)
}
}

impl EffectingData for IbcRelay {
fn effect_hash(&self) -> EffectHash {
let effecting_data: pbi::IbcRelay = self.clone().into();
hash_proto_effecting_data(IbcRelay::TYPE_URL, &effecting_data)
}
}

impl EffectingData for Ics20Withdrawal {
fn effect_hash(&self) -> EffectHash {
let effecting_data: pbi::Ics20Withdrawal = self.clone().into();
Expand Down Expand Up @@ -648,11 +566,7 @@ mod tests {
})
.collect(),
};
let transaction = plan
.build(fvk, witness_data)
.unwrap()
.authorize(&mut OsRng, &auth_data)
.unwrap();
let transaction = plan.build(fvk, &witness_data, &auth_data).unwrap();

let transaction_effect_hash = transaction.effect_hash();

Expand Down
9 changes: 8 additions & 1 deletion crates/core/transaction/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use penumbra_governance::{
DelegatorVotePlan, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote,
};
use penumbra_ibc::IbcRelay;
use penumbra_keys::Address;
use penumbra_keys::{Address, PayloadKey};
use penumbra_proto::{core::transaction::v1alpha1 as pb, DomainType, TypeUrl};
use penumbra_shielded_pool::{Ics20Withdrawal, OutputPlan, SpendPlan};
use penumbra_stake::{Delegate, Undelegate, UndelegateClaimPlan};
Expand Down Expand Up @@ -289,6 +289,13 @@ impl TransactionPlan {

self.clue_plans = clue_plans;
}

/// Convenience method to grab the `MemoKey` from the plan.
pub fn memo_key(&self) -> Option<PayloadKey> {
self.memo_plan
.as_ref()
.map(|memo_plan| memo_plan.key.clone())
}
}

impl TypeUrl for TransactionPlan {
Expand Down
Loading

0 comments on commit 2347ecb

Please sign in to comment.