Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm): split-up build #3289

Merged
merged 48 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
112f7a2
added wasm-bindgen-test dependency
TalDerei Oct 30, 2023
46e26ca
make modules visible to wasm-pack test environment
TalDerei Oct 30, 2023
17b3773
unit test for spend tx
TalDerei Nov 5, 2023
1b67e52
progress on split-up build process
TalDerei Nov 7, 2023
2963ad9
update Cargo.toml
TalDerei Nov 7, 2023
ce1df71
modularize actions into seperate functions
TalDerei Nov 7, 2023
ff413a6
added protobuf serialization attributes for Action
TalDerei Nov 8, 2023
b96b988
added build_unauth method to ActionPlan enum
TalDerei Nov 9, 2023
525585e
add build_unauth_with_actions method to TransactionPlan
TalDerei Nov 9, 2023
a691a3a
added authorize_with_auth method to TransactionPlan that accepts a Tr…
TalDerei Nov 9, 2023
34603b0
simplify build method as all-in-one method
TalDerei Nov 9, 2023
89ef566
simpify build_concurrent method with all-in-one method + tokio
TalDerei Nov 9, 2023
e42f358
remove UnauthTransaction type
TalDerei Nov 9, 2023
f74a0c5
new WasmBuilder type and expose action_builder and build_parallel was…
TalDerei Nov 9, 2023
ac3ca0b
add error logging to action_builder
TalDerei Nov 9, 2023
acb51da
modified test suite and storage
TalDerei Nov 13, 2023
2536dd3
refactored build_action method into WasmPlanner
TalDerei Nov 13, 2023
1f2bfc7
moved build_parallel method to tx
TalDerei Nov 13, 2023
dd95cff
minor cleanup
TalDerei Nov 13, 2023
0065b94
clippy
TalDerei Nov 13, 2023
32cce2a
fmt
TalDerei Nov 13, 2023
79f9228
use result pattern and remove unwraps
TalDerei Nov 13, 2023
bbd705e
remove unneccesary imports in cargo toml file
TalDerei Nov 13, 2023
bc96a09
added docustring comments to relevant functions
TalDerei Nov 13, 2023
4efe98e
fixed failing compilation associated with build and concurrent build …
TalDerei Nov 13, 2023
eb2093b
minor changes to handle failing CI
TalDerei Nov 13, 2023
85bf0bc
minor clippy warnings
TalDerei Nov 13, 2023
9852340
more fmt
TalDerei Nov 13, 2023
0d23650
tx: simplify EffectHash computation
hdevalence Nov 15, 2023
8646218
add stub value_blinding method as suggested in PR review
hdevalence Nov 15, 2023
ed26009
minor fmt nits
TalDerei Nov 15, 2023
9ced228
handling blinding factors using value_blinding method
TalDerei Nov 16, 2023
38f2824
unified prebuild actions and added other action variants
TalDerei Nov 17, 2023
db93cae
moved to prebuilt, immutable actions paradigm
TalDerei Nov 17, 2023
ee99610
fix up errors in rebase
hdevalence Nov 17, 2023
9543f9e
fix up cargo fmt probably introduced by rebasing
hdevalence Nov 17, 2023
bf403a5
fix expect invariant description
hdevalence Nov 17, 2023
0683cb0
fill in comment on build_unauth
hdevalence Nov 17, 2023
376e2d3
rename to apply_auth_data
hdevalence Nov 17, 2023
727cdf4
remove FVK from build_unauth_with_actions, fix errors
hdevalence Nov 17, 2023
0bf75b1
clean up build, build_concurrent
hdevalence Nov 17, 2023
4f27967
rm some warnings
hdevalence Nov 17, 2023
40173ac
fill in ActionPlan::build_unauth
hdevalence Nov 17, 2023
d8e9d45
cargo fix
hdevalence Nov 17, 2023
ea99c72
fix warnings
hdevalence Nov 17, 2023
017f926
disable test
hdevalence Nov 17, 2023
486da44
fix failing test per changes to bech32 prefix used for address
TalDerei Nov 17, 2023
37fdcfb
minor formating
TalDerei Nov 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
/*
Comment on lines +169 to +171
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As follow-up, we should add a TransactionPlan::sort_actions(&mut self) and change the Planner to call it just before returning the final TransactionPlan, then re-enable this test (which implicitly depended on action ordering)

// 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??
hdevalence marked this conversation as resolved.
Show resolved Hide resolved
/// 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 @@ -59,6 +59,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
Loading