From 673446ef017278973746b5d233f13681cc1ca029 Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Mon, 29 Apr 2024 16:50:02 -0400 Subject: [PATCH] =?UTF-8?q?keys:=20=F0=9F=A5=B0=20`Address`=20defers=20com?= =?UTF-8?q?putation=20of=20diversified=20base=20(#4285)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### 💬 describe your changes fixes #1957. the last remaining item mentioned in #1957 is that currently, while `Address` _caches_ the diversified base point, it eagerly computes it when `Address::from_components()` is called. we can defer this action until the base point is needed, using `OnceLock`. in order to do that, we must revoke the `Copy` derivation. essentially, this branch makes the following change(s) to `Address`: ```diff -#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "pb::Address", into = "pb::Address")] pub struct Address { d: Diversifier, /// cached copy of the diversified base - g_d: decaf377::Element, + g_d: OnceLock, /// extra invariant: the bytes in pk_d should be the canonical encoding of an /// s value (whether or not it is a valid decaf377 encoding) /// this ensures we can use a PaymentAddress to form a note commitment, /// which involves hashing s as a field element. pk_d: ka::Public, /// transmission key s value transmission_key_s: Fq, ck_d: fmd::ClueKey, } ``` out of that, a few other types also lose their `Copy` property: * AddressView * FundingStream * Recipient ..which leads to the addition of explicit `.clone()` calls in various places. largely, that tends to affect test code, but it is a more invasive change than expected, and i want to draw attention to that. it should _not_ be a change in behavior, however. these are the same copies as previously made, but are now performed explicitly. for more, see the `Copy` documentation: https://doc.rust-lang.org/stable/std/marker/trait.Copy.html#whats-the-difference-between-copy-and-clone some extra documentation, a small compile-time check to enshrine thread-safety properties (`Send` and `Sync`), and an extra `From` implementation are added too. #### ➕ changes to facilitate review and to make this easier to reason about, this branch is divided into distinct steps: * keys: 🔖 add more `Address` documentation * keys: 🙂 add compile-time assertions about addresses * keys: 📫 `pb::Address: From<&Address>` * keys: 👯 `Address` is not `Copy` * keys: 🥰 defer diversified base using `OnceLock` ...reviewers are encouraged to review this step-by-step. :mag: **NB:** using `OnceLock` is the core change at play here. ### issue ticket number and link * #1957 ### checklist before requesting a review - [x] if this code contains consensus-breaking changes, i have added the "consensus-breaking" label. otherwise, i declare my belief that there are not consensus-breaking changes, for the following reason: > this does not change the behavior of addresses, it only defers the > computation of the diversified base point. --- crates/bin/pcli/src/command/ceremony.rs | 2 +- crates/bin/pcli/src/command/tx.rs | 6 +- .../bin/pclientd/tests/network_integration.rs | 6 +- .../app/src/action_handler/transaction.rs | 12 ++- ..._can_define_and_delegate_to_a_validator.rs | 19 ++-- .../app_can_disable_community_pool_spends.rs | 13 ++- .../app_can_propose_community_pool_spends.rs | 13 ++- .../app_can_spend_notes_and_detect_outputs.rs | 10 +- .../app_can_undelegate_from_a_validator.rs | 26 +++-- ..._uptime_for_validators_only_once_active.rs | 19 ++-- crates/core/app/tests/swap_and_swap_claim.rs | 4 +- .../core/component/dex/src/swap/plaintext.rs | 6 +- .../governance/src/delegator_vote/proof.rs | 10 +- .../src/component/note_manager.rs | 2 +- .../core/component/shielded-pool/src/note.rs | 6 +- .../shielded-pool/src/output/plan.rs | 2 +- .../shielded-pool/src/spend/proof.rs | 34 +++---- .../component/stake/src/funding_stream.rs | 6 +- crates/core/keys/src/address.rs | 96 ++++++++++++++++--- crates/core/keys/src/address/r1cs.rs | 2 +- crates/core/keys/src/address/view.rs | 46 +++++---- crates/core/transaction/src/memo.rs | 6 +- crates/core/transaction/src/plan.rs | 6 +- crates/view/src/planner.rs | 2 +- crates/view/src/service.rs | 12 +-- crates/wallet/src/plan.rs | 2 +- tools/summonerd/src/participant.rs | 2 +- tools/summonerd/src/queue.rs | 2 +- tools/summonerd/src/server.rs | 2 +- 29 files changed, 252 insertions(+), 122 deletions(-) diff --git a/crates/bin/pcli/src/command/ceremony.rs b/crates/bin/pcli/src/command/ceremony.rs index 0e396f374f..5cd73ca96c 100644 --- a/crates/bin/pcli/src/command/ceremony.rs +++ b/crates/bin/pcli/src/command/ceremony.rs @@ -139,7 +139,7 @@ impl CeremonyCmd { bid, address ); - handle_bid(app, *coordinator_address, index, bid).await?; + handle_bid(app, coordinator_address.clone(), index, bid).await?; println!("connecting to coordinator..."); // After we bid, we need to wait a couple of seconds just for the transaction to be diff --git a/crates/bin/pcli/src/command/tx.rs b/crates/bin/pcli/src/command/tx.rs index ae11ece118..3fd90f5b5b 100644 --- a/crates/bin/pcli/src/command/tx.rs +++ b/crates/bin/pcli/src/command/tx.rs @@ -31,7 +31,7 @@ use penumbra_asset::{asset, asset::Metadata, Value, STAKING_TOKEN_ASSET_ID}; use penumbra_dex::{lp::position, swap_claim::SwapClaimPlan}; use penumbra_fee::Fee; use penumbra_governance::{proposal::ProposalToml, proposal_state::State as ProposalState, Vote}; -use penumbra_keys::keys::AddressIndex; +use penumbra_keys::{keys::AddressIndex, Address}; use penumbra_num::Amount; use penumbra_proto::{ core::component::{ @@ -346,7 +346,7 @@ impl TxCmd { .map(|v| v.parse()) .collect::, _>>()?; let to = to - .parse() + .parse::
() .map_err(|_| anyhow::anyhow!("address is invalid"))?; let return_address = app @@ -364,7 +364,7 @@ impl TxCmd { .set_gas_prices(gas_prices) .set_fee_tier((*fee_tier).into()); for value in values.iter().cloned() { - planner.output(value, to); + planner.output(value, to.clone()); } let plan = planner .memo(memo_plaintext)? diff --git a/crates/bin/pclientd/tests/network_integration.rs b/crates/bin/pclientd/tests/network_integration.rs index 7f02c207ce..6c9c282171 100644 --- a/crates/bin/pclientd/tests/network_integration.rs +++ b/crates/bin/pclientd/tests/network_integration.rs @@ -6,7 +6,7 @@ //! where no tokens have been delegated, and the address with index 0 //! was distributedp 1cube. -use std::process::Command as StdCommand; +use std::{ops::Deref, process::Command as StdCommand}; use anyhow::Context; use assert_cmd::cargo::CommandCargoExt; @@ -120,7 +120,7 @@ async fn transaction_send_flow() -> anyhow::Result<()> { let plan = view_client .transaction_planner(TransactionPlannerRequest { outputs: vec![tpr::Output { - address: Some((*test_keys::ADDRESS_1).into()), + address: Some(test_keys::ADDRESS_1.deref().clone().into()), value: Some( Value { amount: 1_000_000u64.into(), @@ -304,7 +304,7 @@ async fn swap_claim_flow() -> anyhow::Result<()> { amount: Some(num::Amount { lo: 0, hi: 0 }), asset_id: None, }), - claim_address: Some((*test_keys::ADDRESS_1).into()), + claim_address: Some(test_keys::ADDRESS_1.deref().clone().into()), }], ..Default::default() }) diff --git a/crates/core/app/src/action_handler/transaction.rs b/crates/core/app/src/action_handler/transaction.rs index 859ff816c8..9219a8350b 100644 --- a/crates/core/app/src/action_handler/transaction.rs +++ b/crates/core/app/src/action_handler/transaction.rs @@ -110,6 +110,8 @@ impl AppActionHandler for Transaction { #[cfg(test)] mod tests { + use std::ops::Deref; + use anyhow::Result; use penumbra_asset::{Value, STAKING_TOKEN_ASSET_ID}; use penumbra_fee::Fee; @@ -163,10 +165,14 @@ mod tests { actions: vec![ SpendPlan::new(&mut OsRng, note, auth_path.position()).into(), SpendPlan::new(&mut OsRng, note2, auth_path2.position()).into(), - OutputPlan::new(&mut OsRng, value, *test_keys::ADDRESS_1).into(), + OutputPlan::new(&mut OsRng, value, test_keys::ADDRESS_1.deref().clone()).into(), ], detection_data: Some(DetectionDataPlan { - clue_plans: vec![CluePlan::new(&mut OsRng, *test_keys::ADDRESS_1, 1)], + clue_plans: vec![CluePlan::new( + &mut OsRng, + test_keys::ADDRESS_1.deref().clone(), + 1, + )], }), memo: None, }; @@ -228,7 +234,7 @@ mod tests { }, actions: vec![ SpendPlan::new(&mut OsRng, note, auth_path.position()).into(), - OutputPlan::new(&mut OsRng, value, *test_keys::ADDRESS_1).into(), + OutputPlan::new(&mut OsRng, value, test_keys::ADDRESS_1.deref().clone()).into(), ], detection_data: None, memo: None, diff --git a/crates/core/app/tests/app_can_define_and_delegate_to_a_validator.rs b/crates/core/app/tests/app_can_define_and_delegate_to_a_validator.rs index 16b0f6a41f..b783bf2db4 100644 --- a/crates/core/app/tests/app_can_define_and_delegate_to_a_validator.rs +++ b/crates/core/app/tests/app_can_define_and_delegate_to_a_validator.rs @@ -16,6 +16,7 @@ use { GovernanceKey, IdentityKey, }, rand_core::OsRng, + std::ops::Deref, tap::Tap, tracing::{error_span, info, Instrument}, }; @@ -251,13 +252,16 @@ async fn app_can_define_and_delegate_to_a_validator() -> anyhow::Result<()> { let output = OutputPlan::new( &mut rand_core::OsRng, delegate.delegation_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), delegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), @@ -410,14 +414,17 @@ async fn app_can_define_and_delegate_to_a_validator() -> anyhow::Result<()> { let output = OutputPlan::new( &mut rand_core::OsRng, undelegate.unbonded_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), undelegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), diff --git a/crates/core/app/tests/app_can_disable_community_pool_spends.rs b/crates/core/app/tests/app_can_disable_community_pool_spends.rs index 630d1588f9..3648b06bba 100644 --- a/crates/core/app/tests/app_can_disable_community_pool_spends.rs +++ b/crates/core/app/tests/app_can_disable_community_pool_spends.rs @@ -33,7 +33,7 @@ use { }, rand::Rng, rand_core::OsRng, - std::collections::BTreeMap, + std::{collections::BTreeMap, ops::Deref}, tap::{Tap, TapFallible}, tracing::{error_span, info, Instrument}, }; @@ -204,7 +204,7 @@ async fn app_can_disable_community_pool_spends() -> anyhow::Result<()> { CommunityPoolSpend { value }.into(), CommunityPoolOutput { value, - address: *test_keys::ADDRESS_0, + address: test_keys::ADDRESS_0.deref().clone(), } .into(), ], @@ -233,12 +233,17 @@ async fn app_can_disable_community_pool_spends() -> anyhow::Result<()> { actions: vec![ proposal, // Next, create a new output of the exact same amount. - OutputPlan::new(&mut OsRng, proposal_nft_value, *test_keys::ADDRESS_0).into(), + OutputPlan::new( + &mut OsRng, + proposal_nft_value, + test_keys::ADDRESS_0.deref().clone(), + ) + .into(), ], // Now fill out the remaining parts of the transaction needed for verification: memo: Some(MemoPlan::new( &mut OsRng, - MemoPlaintext::blank_memo(*test_keys::ADDRESS_0), + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), )?), detection_data: None, transaction_parameters: TransactionParameters { diff --git a/crates/core/app/tests/app_can_propose_community_pool_spends.rs b/crates/core/app/tests/app_can_propose_community_pool_spends.rs index b9ea0f9aab..fe308b9761 100644 --- a/crates/core/app/tests/app_can_propose_community_pool_spends.rs +++ b/crates/core/app/tests/app_can_propose_community_pool_spends.rs @@ -33,7 +33,7 @@ use { }, rand::Rng, rand_core::OsRng, - std::collections::BTreeMap, + std::{collections::BTreeMap, ops::Deref}, tap::{Tap, TapFallible}, tracing::{error_span, info, Instrument}, }; @@ -198,7 +198,7 @@ async fn app_can_propose_community_pool_spends() -> anyhow::Result<()> { CommunityPoolSpend { value }.into(), CommunityPoolOutput { value, - address: *test_keys::ADDRESS_0, + address: test_keys::ADDRESS_0.deref().clone(), } .into(), ], @@ -227,12 +227,17 @@ async fn app_can_propose_community_pool_spends() -> anyhow::Result<()> { actions: vec![ proposal, // Next, create a new output of the exact same amount. - OutputPlan::new(&mut OsRng, proposal_nft_value, *test_keys::ADDRESS_0).into(), + OutputPlan::new( + &mut OsRng, + proposal_nft_value, + test_keys::ADDRESS_0.deref().clone(), + ) + .into(), ], // Now fill out the remaining parts of the transaction needed for verification: memo: Some(MemoPlan::new( &mut OsRng, - MemoPlaintext::blank_memo(*test_keys::ADDRESS_0), + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), )?), detection_data: None, transaction_parameters: TransactionParameters { diff --git a/crates/core/app/tests/app_can_spend_notes_and_detect_outputs.rs b/crates/core/app/tests/app_can_spend_notes_and_detect_outputs.rs index d4bf8577d1..547525cca6 100644 --- a/crates/core/app/tests/app_can_spend_notes_and_detect_outputs.rs +++ b/crates/core/app/tests/app_can_spend_notes_and_detect_outputs.rs @@ -13,6 +13,7 @@ use { memo::MemoPlaintext, plan::MemoPlan, TransactionParameters, TransactionPlan, }, rand_core::OsRng, + std::ops::Deref, tap::{Tap, TapFallible}, tracing::info, }; @@ -63,12 +64,17 @@ async fn app_can_spend_notes_and_detect_outputs() -> anyhow::Result<()> { ) .into(), // Next, create a new output of the exact same amount. - OutputPlan::new(&mut OsRng, input_note.value(), *test_keys::ADDRESS_1).into(), + OutputPlan::new( + &mut OsRng, + input_note.value(), + test_keys::ADDRESS_1.deref().clone(), + ) + .into(), ], // Now fill out the remaining parts of the transaction needed for verification: memo: Some(MemoPlan::new( &mut OsRng, - MemoPlaintext::blank_memo(*test_keys::ADDRESS_0), + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), )?), detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { diff --git a/crates/core/app/tests/app_can_undelegate_from_a_validator.rs b/crates/core/app/tests/app_can_undelegate_from_a_validator.rs index e5b0cdf786..12365df179 100644 --- a/crates/core/app/tests/app_can_undelegate_from_a_validator.rs +++ b/crates/core/app/tests/app_can_undelegate_from_a_validator.rs @@ -18,6 +18,7 @@ use { memo::MemoPlaintext, plan::MemoPlan, TransactionParameters, TransactionPlan, }, rand_core::OsRng, + std::ops::Deref, tap::Tap, tracing::{error_span, info, Instrument}, }; @@ -132,13 +133,16 @@ async fn app_can_undelegate_from_a_validator() -> anyhow::Result<()> { let output = OutputPlan::new( &mut rand_core::OsRng, delegate.delegation_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), delegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), @@ -230,13 +234,16 @@ async fn app_can_undelegate_from_a_validator() -> anyhow::Result<()> { let output = OutputPlan::new( &mut rand_core::OsRng, undelegate.unbonded_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), undelegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), @@ -317,8 +324,11 @@ async fn app_can_undelegate_from_a_validator() -> anyhow::Result<()> { let mut plan = TransactionPlan { actions: vec![claim.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), diff --git a/crates/core/app/tests/app_tracks_uptime_for_validators_only_once_active.rs b/crates/core/app/tests/app_tracks_uptime_for_validators_only_once_active.rs index 99c05262e6..2c3f228f0c 100644 --- a/crates/core/app/tests/app_tracks_uptime_for_validators_only_once_active.rs +++ b/crates/core/app/tests/app_tracks_uptime_for_validators_only_once_active.rs @@ -16,6 +16,7 @@ use { FundingStreams, GovernanceKey, IdentityKey, Uptime, }, rand_core::OsRng, + std::ops::Deref, tap::Tap, tracing::{error_span, Instrument}, }; @@ -191,13 +192,16 @@ async fn app_tracks_uptime_for_validators_only_once_active() -> anyhow::Result<( let output = OutputPlan::new( &mut rand_core::OsRng, delegate.delegation_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), delegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), @@ -312,14 +316,17 @@ async fn app_tracks_uptime_for_validators_only_once_active() -> anyhow::Result<( let output = OutputPlan::new( &mut rand_core::OsRng, undelegate.unbonded_value(), - *test_keys::ADDRESS_1, + test_keys::ADDRESS_1.deref().clone(), ); let mut plan = TransactionPlan { actions: vec![spend.into(), output.into(), undelegate.into()], // Now fill out the remaining parts of the transaction needed for verification: - memo: MemoPlan::new(&mut OsRng, MemoPlaintext::blank_memo(*test_keys::ADDRESS_0)) - .map(Some)?, + memo: MemoPlan::new( + &mut OsRng, + MemoPlaintext::blank_memo(test_keys::ADDRESS_0.deref().clone()), + ) + .map(Some)?, detection_data: None, // We'll set this automatically below transaction_parameters: TransactionParameters { chain_id: TestNode::<()>::CHAIN_ID.to_string(), diff --git a/crates/core/app/tests/swap_and_swap_claim.rs b/crates/core/app/tests/swap_and_swap_claim.rs index 586e5027f3..aa329da576 100644 --- a/crates/core/app/tests/swap_and_swap_claim.rs +++ b/crates/core/app/tests/swap_and_swap_claim.rs @@ -58,7 +58,7 @@ async fn swap_and_swap_claim() -> anyhow::Result<()> { let delta_1 = Amount::from(100_000u64); let delta_2 = Amount::from(0u64); let fee = Fee::default(); - let claim_address: Address = *test_keys::ADDRESS_0; + let claim_address: Address = test_keys::ADDRESS_0.deref().clone(); let plaintext = SwapPlaintext::new(&mut rng, trading_pair, delta_1, delta_2, fee, claim_address); @@ -295,7 +295,7 @@ async fn swap_with_nonzero_fee() -> anyhow::Result<()> { let delta_1 = Amount::from(100_000u64); let delta_2 = Amount::from(0u64); let fee = Fee::from_staking_token_amount(Amount::from(1u64)); - let claim_address: Address = *test_keys::ADDRESS_0; + let claim_address: Address = test_keys::ADDRESS_0.deref().clone(); let plaintext = SwapPlaintext::new(&mut rng, trading_pair, delta_1, delta_2, fee, claim_address); diff --git a/crates/core/component/dex/src/swap/plaintext.rs b/crates/core/component/dex/src/swap/plaintext.rs index 8bde2f5db6..45e5ce66b5 100644 --- a/crates/core/component/dex/src/swap/plaintext.rs +++ b/crates/core/component/dex/src/swap/plaintext.rs @@ -70,7 +70,7 @@ impl SwapPlaintext { batch_data.pro_rata_outputs((self.delta_1_i, self.delta_2_i)); let output_1_note = Note::from_parts( - self.claim_address, + self.claim_address.clone(), Value { amount: lambda_1_i, asset_id: self.trading_pair.asset_1(), @@ -80,7 +80,7 @@ impl SwapPlaintext { .expect("claim address is valid"); let output_2_note = Note::from_parts( - self.claim_address, + self.claim_address.clone(), Value { amount: lambda_2_i, asset_id: self.trading_pair.asset_2(), @@ -344,7 +344,7 @@ impl From<&SwapPlaintext> for [u8; SWAP_LEN_BYTES] { bytes[80..96].copy_from_slice(&swap.delta_2_i.to_le_bytes()); bytes[96..112].copy_from_slice(&swap.claim_fee.0.amount.to_le_bytes()); bytes[112..144].copy_from_slice(&swap.claim_fee.0.asset_id.to_bytes()); - let pb_address = pb_keys::Address::from(swap.claim_address); + let pb_address = pb_keys::Address::from(swap.claim_address.clone()); bytes[144..224].copy_from_slice(&pb_address.inner); bytes[224..256].copy_from_slice(&swap.rseed.to_bytes()); bytes diff --git a/crates/core/component/governance/src/delegator_vote/proof.rs b/crates/core/component/governance/src/delegator_vote/proof.rs index 201afd1ed2..f4a61fe267 100644 --- a/crates/core/component/governance/src/delegator_vote/proof.rs +++ b/crates/core/component/governance/src/delegator_vote/proof.rs @@ -453,7 +453,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -469,7 +469,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("can insert note commitment into SCT"); } @@ -480,7 +480,7 @@ mod tests { // All proposals should have a position commitment index of zero, so we need to end the epoch // and get the position that corresponds to the first commitment in the new epoch. sct.end_epoch().expect("should be able to end an epoch"); - let first_note_commitment = Note::from_parts(sender, value_to_send, Rseed([u8::MAX; 32])).expect("can create note").commit(); + let first_note_commitment = Note::from_parts(sender.clone(), value_to_send, Rseed([u8::MAX; 32])).expect("can create note").commit(); sct.insert(tct::Witness::Keep, first_note_commitment).expect("can insert note commitment into SCT"); let start_position = sct.witness(first_note_commitment).expect("can witness note commitment").position(); @@ -529,7 +529,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -545,7 +545,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("can insert note commitment into SCT"); } diff --git a/crates/core/component/shielded-pool/src/component/note_manager.rs b/crates/core/component/shielded-pool/src/component/note_manager.rs index dd221fac73..a4d13ef343 100644 --- a/crates/core/component/shielded-pool/src/component/note_manager.rs +++ b/crates/core/component/shielded-pool/src/component/note_manager.rs @@ -52,7 +52,7 @@ pub trait NoteManager: StateWrite { .as_bytes()[0..32] .try_into()?; - let note = Note::from_parts(*address, value, Rseed(rseed_bytes))?; + let note = Note::from_parts(address.clone(), value, Rseed(rseed_bytes))?; self.add_note_payload(note.payload(), source).await; Ok(()) diff --git a/crates/core/component/shielded-pool/src/note.rs b/crates/core/component/shielded-pool/src/note.rs index 930cb089de..c1575443b6 100644 --- a/crates/core/component/shielded-pool/src/note.rs +++ b/crates/core/component/shielded-pool/src/note.rs @@ -137,7 +137,7 @@ impl Note { Ok(Note { value, rseed, - address, + address: address.clone(), transmission_key_s: Fq::from_bytes(address.transmission_key().0) .map_err(|_| Error::InvalidTransmissionKey)?, }) @@ -155,12 +155,12 @@ impl Note { /// random blinding factor. pub fn generate(rng: &mut (impl Rng + CryptoRng), address: &Address, value: Value) -> Self { let rseed = Rseed::generate(rng); - Note::from_parts(*address, value, rseed) + Note::from_parts(address.clone(), value, rseed) .expect("transmission key in address is always valid") } pub fn address(&self) -> Address { - self.address + self.address.clone() } pub fn diversified_generator(&self) -> decaf377::Element { diff --git a/crates/core/component/shielded-pool/src/output/plan.rs b/crates/core/component/shielded-pool/src/output/plan.rs index ef7308294f..86e31817f3 100644 --- a/crates/core/component/shielded-pool/src/output/plan.rs +++ b/crates/core/component/shielded-pool/src/output/plan.rs @@ -68,7 +68,7 @@ impl OutputPlan { } pub fn output_note(&self) -> Note { - Note::from_parts(self.dest_address, self.value, self.rseed) + Note::from_parts(self.dest_address.clone(), self.value, self.rseed) .expect("transmission key in address is always valid") } diff --git a/crates/core/component/shielded-pool/src/spend/proof.rs b/crates/core/component/shielded-pool/src/spend/proof.rs index a818fc8267..8852ad07c6 100644 --- a/crates/core/component/shielded-pool/src/spend/proof.rs +++ b/crates/core/component/shielded-pool/src/spend/proof.rs @@ -429,7 +429,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -445,7 +445,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); } @@ -498,7 +498,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -514,7 +514,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); } let incorrect_anchor = sct.root(); @@ -641,7 +641,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -657,12 +657,12 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); } // Insert one more note commitment and witness it. let rseed = Rseed([num_commitments as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); let incorrect_position = sct.witness(dummy_note_commitment).expect("can witness note commitment").position(); @@ -715,7 +715,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -731,7 +731,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); } @@ -784,7 +784,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -799,7 +799,7 @@ mod tests { for i in 0..num_commitments { // To avoid duplicate note commitments, we use the `i` counter as the Rseed randomness let rseed = Rseed([i as u8; 32]); - let dummy_note_commitment = Note::from_parts(sender, value_to_send, rseed).expect("can create note").commit(); + let dummy_note_commitment = Note::from_parts(sender.clone(), value_to_send, rseed).expect("can create note").commit(); sct.insert(tct::Witness::Keep, dummy_note_commitment).expect("should be able to insert note commitments into the SCT"); } @@ -853,7 +853,7 @@ mod tests { asset_id: asset::Id(Fq::from(asset_id64)), }; let note = Note::from_parts( - sender, + sender.clone(), value_to_send, Rseed(rseed_randomness), ).expect("should be able to create note"); @@ -1015,7 +1015,7 @@ mod tests { let mut sct = tct::Tree::new(); for _ in 0..5 { - let note_commitment = make_random_note_commitment(address); + let note_commitment = make_random_note_commitment(address.clone()); sct.insert(tct::Witness::Keep, note_commitment).unwrap(); let anchor = sct.root(); let state_commitment_proof = sct.witness(note_commitment).unwrap(); @@ -1043,12 +1043,12 @@ mod tests { sct.end_block().expect("can end block"); for _ in 0..100 { - let note_commitment = make_random_note_commitment(address); + let note_commitment = make_random_note_commitment(address.clone()); sct.insert(tct::Witness::Forget, note_commitment).unwrap(); } for _ in 0..5 { - let note_commitment = make_random_note_commitment(address); + let note_commitment = make_random_note_commitment(address.clone()); sct.insert(tct::Witness::Keep, note_commitment).unwrap(); let anchor = sct.root(); let state_commitment_proof = sct.witness(note_commitment).unwrap(); @@ -1076,12 +1076,12 @@ mod tests { sct.end_epoch().expect("can end epoch"); for _ in 0..100 { - let note_commitment = make_random_note_commitment(address); + let note_commitment = make_random_note_commitment(address.clone()); sct.insert(tct::Witness::Forget, note_commitment).unwrap(); } for _ in 0..5 { - let note_commitment = make_random_note_commitment(address); + let note_commitment = make_random_note_commitment(address.clone()); sct.insert(tct::Witness::Keep, note_commitment).unwrap(); let anchor = sct.root(); let state_commitment_proof = sct.witness(note_commitment).unwrap(); diff --git a/crates/core/component/stake/src/funding_stream.rs b/crates/core/component/stake/src/funding_stream.rs index 4a114d0968..67a63c4dd8 100644 --- a/crates/core/component/stake/src/funding_stream.rs +++ b/crates/core/component/stake/src/funding_stream.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; /// A destination for a portion of a validator's commission of staking rewards. #[allow(clippy::large_enum_variant)] -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] #[serde(try_from = "pb::FundingStream", into = "pb::FundingStream")] pub enum FundingStream { ToAddress { @@ -25,7 +25,7 @@ pub enum FundingStream { } #[allow(clippy::large_enum_variant)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Recipient { Address(Address), CommunityPool, @@ -41,7 +41,7 @@ impl FundingStream { pub fn recipient(&self) -> Recipient { match self { - FundingStream::ToAddress { address, .. } => Recipient::Address(*address), + FundingStream::ToAddress { address, .. } => Recipient::Address(address.clone()), FundingStream::ToCommunityPool { .. } => Recipient::CommunityPool, } } diff --git a/crates/core/keys/src/address.rs b/crates/core/keys/src/address.rs index 616ec7a482..e6dc61e918 100644 --- a/crates/core/keys/src/address.rs +++ b/crates/core/keys/src/address.rs @@ -1,4 +1,9 @@ -use std::io::{Cursor, Read, Write}; +//! [Payment address][Address] facilities. + +use std::{ + io::{Cursor, Read, Write}, + sync::OnceLock, +}; use anyhow::Context; use ark_serialize::CanonicalDeserialize; @@ -16,29 +21,70 @@ pub use view::AddressView; use crate::{fmd, ka, keys::Diversifier}; +/// The length of an [`Address`] in bytes. pub const ADDRESS_LEN_BYTES: usize = 80; + /// Number of bits in the address short form divided by the number of bits per Bech32m character pub const ADDRESS_NUM_CHARS_SHORT_FORM: usize = 24; /// A valid payment address. -#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Eq, Serialize, Deserialize)] #[serde(try_from = "pb::Address", into = "pb::Address")] pub struct Address { + /// The address diversifier. d: Diversifier, - /// cached copy of the diversified base - g_d: decaf377::Element, + /// A cached copy of the diversified base. + g_d: OnceLock, + /// The public key for this payment address. + /// /// extra invariant: the bytes in pk_d should be the canonical encoding of an /// s value (whether or not it is a valid decaf377 encoding) /// this ensures we can use a PaymentAddress to form a note commitment, /// which involves hashing s as a field element. pk_d: ka::Public, - /// transmission key s value + /// The transmission key s value. transmission_key_s: Fq, + /// The clue key for this payment address. ck_d: fmd::ClueKey, } +impl std::cmp::PartialEq for Address { + fn eq( + &self, + rhs @ Self { + d: rhs_d, + g_d: rhs_g_d, + pk_d: rhs_pk_d, + transmission_key_s: rhs_transmission_key_s, + ck_d: rhs_ck_d, + }: &Self, + ) -> bool { + let lhs @ Self { + d: lhs_d, + g_d: lhs_g_d, + pk_d: lhs_pk_d, + transmission_key_s: lhs_transmission_key_s, + ck_d: lhs_ck_d, + } = self; + + // When a `OnceLock` value is compared, it will only call `get()`, refraining from + // initializing the value. To make sure that an address that *hasn't* yet accessed its + // diversified base is considered equal to an address that *has*, compute the base points + // if they have not already been generated. + lhs.diversified_generator(); + rhs.diversified_generator(); + + // Compare all of the fields. + lhs_d.eq(rhs_d) + && lhs_g_d.eq(rhs_g_d) + && lhs_pk_d.eq(rhs_pk_d) + && lhs_transmission_key_s.eq(rhs_transmission_key_s) + && lhs_ck_d.eq(rhs_ck_d) + } +} + impl std::cmp::PartialOrd for Address { fn partial_cmp(&self, other: &Self) -> Option { Some(self.to_vec().cmp(&other.to_vec())) @@ -69,7 +115,7 @@ impl Address { // don't need an error type here, caller will probably .expect anyways Some(Self { d, - g_d: d.diversified_generator(), + g_d: OnceLock::new(), pk_d, ck_d, transmission_key_s, @@ -79,26 +125,36 @@ impl Address { } } + /// Returns a reference to the address diversifier. pub fn diversifier(&self) -> &Diversifier { &self.d } + /// Returns a reference to the diversified base. + /// + /// This method computes the diversified base if it has not been computed yet. This value is + /// cached after it has been computed once. pub fn diversified_generator(&self) -> &decaf377::Element { - &self.g_d + self.g_d + .get_or_init(|| self.diversifier().diversified_generator()) } + /// Returns a reference to the transmission key. pub fn transmission_key(&self) -> &ka::Public { &self.pk_d } + /// Returns a reference to the clue key. pub fn clue_key(&self) -> &fmd::ClueKey { &self.ck_d } + /// Returns a reference to the transmission key `s` value. pub fn transmission_key_s(&self) -> &Fq { &self.transmission_key_s } + /// Converts this address to a vector of bytes. pub fn to_vec(&self) -> Vec { let mut bytes = std::io::Cursor::new(Vec::new()); bytes @@ -114,7 +170,7 @@ impl Address { f4jumble(bytes.get_ref()).expect("can jumble") } - /// A randomized dummy address. + /// Generates a randomized dummy address. pub fn dummy(rng: &mut R) -> Self { loop { let mut diversifier_bytes = [0u8; 16]; @@ -151,7 +207,7 @@ impl Address { /// Compat (bech32 non-m) address format pub fn compat_encoding(&self) -> String { - let proto_address = pb::Address::from(*self); + let proto_address = pb::Address::from(self); bech32str::encode( &proto_address.inner, bech32str::compat_address::BECH32_PREFIX, @@ -166,6 +222,12 @@ impl DomainType for Address { impl From
for pb::Address { fn from(a: Address) -> Self { + Self::from(&a) + } +} + +impl From<&Address> for pb::Address { + fn from(a: &Address) -> Self { pb::Address { inner: a.to_vec(), // Always produce encodings without the alt format. @@ -193,7 +255,7 @@ impl TryFrom for Address { impl std::fmt::Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let proto_address = pb::Address::from(*self); + let proto_address = pb::Address::from(self); f.write_str(&bech32str::encode( &proto_address.inner, bech32str::address::BECH32_PREFIX, @@ -286,6 +348,18 @@ impl TryFrom<&[u8]> for Address { } } +/// Assert the addresses are both [`Send`] and [`Sync`]. +// NB: allow dead code, because this block only contains compile-time assertions. +#[allow(dead_code)] +mod assert_address_is_send_and_sync { + fn is_send() {} + fn is_sync() {} + fn f() { + is_send::(); + is_sync::(); + } +} + #[cfg(test)] mod tests { use std::str::FromStr; @@ -316,7 +390,7 @@ mod tests { alt_bech32m: bech32m_addr, } .encode_to_vec(); - let proto_addr_direct: pb::Address = dest.into(); + let proto_addr_direct: pb::Address = dest.clone().into(); let addr_from_proto: Address = proto_addr_direct .try_into() .expect("can convert from proto back to address"); diff --git a/crates/core/keys/src/address/r1cs.rs b/crates/core/keys/src/address/r1cs.rs index 635b72ade3..069f53abc1 100644 --- a/crates/core/keys/src/address/r1cs.rs +++ b/crates/core/keys/src/address/r1cs.rs @@ -36,7 +36,7 @@ impl AllocVar for AddressVar { ) -> Result { let ns = cs.into(); let cs = ns.cs(); - let address: Address = *f()?.borrow(); + let address: Address = f()?.borrow().to_owned(); let diversified_generator: ElementVar = AllocVar::::new_variable( cs.clone(), diff --git a/crates/core/keys/src/address/view.rs b/crates/core/keys/src/address/view.rs index 3dcf9c28c2..fd1cf6db0a 100644 --- a/crates/core/keys/src/address/view.rs +++ b/crates/core/keys/src/address/view.rs @@ -11,7 +11,7 @@ use super::Address; /// /// This type allows working with addresses and address indexes without knowing /// the corresponding FVK. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "pb::AddressView", into = "pb::AddressView")] pub enum AddressView { Opaque { @@ -27,8 +27,8 @@ pub enum AddressView { impl AddressView { pub fn address(&self) -> Address { match self { - AddressView::Opaque { address } => *address, - AddressView::Decoded { address, .. } => *address, + AddressView::Opaque { address } => address.clone(), + AddressView::Decoded { address, .. } => address.clone(), } } } @@ -121,49 +121,57 @@ mod tests { let addr2_1 = fvk2.payment_address(1.into()).0; assert_eq!( - fvk1.view_address(addr1_0), + fvk1.view_address(addr1_0.clone()), AddressView::Decoded { - address: addr1_0, + address: addr1_0.clone(), index: 0.into(), wallet_id: fvk1.wallet_id(), } ); assert_eq!( - fvk2.view_address(addr1_0), - AddressView::Opaque { address: addr1_0 } + fvk2.view_address(addr1_0.clone()), + AddressView::Opaque { + address: addr1_0.clone() + } ); assert_eq!( - fvk1.view_address(addr1_1), + fvk1.view_address(addr1_1.clone()), AddressView::Decoded { - address: addr1_1, + address: addr1_1.clone(), index: 1.into(), wallet_id: fvk1.wallet_id(), } ); assert_eq!( - fvk2.view_address(addr1_1), - AddressView::Opaque { address: addr1_1 } + fvk2.view_address(addr1_1.clone()), + AddressView::Opaque { + address: addr1_1.clone() + } ); assert_eq!( - fvk1.view_address(addr2_0), - AddressView::Opaque { address: addr2_0 } + fvk1.view_address(addr2_0.clone()), + AddressView::Opaque { + address: addr2_0.clone() + } ); assert_eq!( - fvk2.view_address(addr2_0), + fvk2.view_address(addr2_0.clone()), AddressView::Decoded { - address: addr2_0, + address: addr2_0.clone(), index: 0.into(), wallet_id: fvk2.wallet_id(), } ); assert_eq!( - fvk1.view_address(addr2_1), - AddressView::Opaque { address: addr2_1 } + fvk1.view_address(addr2_1.clone()), + AddressView::Opaque { + address: addr2_1.clone() + } ); assert_eq!( - fvk2.view_address(addr2_1), + fvk2.view_address(addr2_1.clone()), AddressView::Decoded { - address: addr2_1, + address: addr2_1.clone(), index: 1.into(), wallet_id: fvk2.wallet_id(), } diff --git a/crates/core/transaction/src/memo.rs b/crates/core/transaction/src/memo.rs index 1f76604784..484655247a 100644 --- a/crates/core/transaction/src/memo.rs +++ b/crates/core/transaction/src/memo.rs @@ -66,7 +66,7 @@ impl MemoPlaintext { } pub fn return_address(&self) -> Address { - self.return_address + self.return_address.clone() } pub fn text(&self) -> &str { @@ -284,7 +284,7 @@ mod tests { // On the sender side, we have to encrypt the memo to put into the transaction-level, // and also the memo key to put on the action-level (output). let memo = MemoPlaintext { - return_address: dest, + return_address: dest.clone(), text: String::from("Hi"), }; let memo_key = PayloadKey::random_key(&mut OsRng); @@ -331,7 +331,7 @@ mod tests { // On the sender side, we have to encrypt the memo to put into the transaction-level, // and also the memo key to put on the action-level (output). - let memo = MemoPlaintext::new(dest, "Hello, friend".into())?; + let memo = MemoPlaintext::new(dest.clone(), "Hello, friend".into())?; let memo_key = PayloadKey::random_key(&mut OsRng); let ciphertext = MemoCiphertext::encrypt(memo_key.clone(), &memo).expect("can encrypt memo"); diff --git a/crates/core/transaction/src/plan.rs b/crates/core/transaction/src/plan.rs index dbbb70931e..3f36c7f42c 100644 --- a/crates/core/transaction/src/plan.rs +++ b/crates/core/transaction/src/plan.rs @@ -318,7 +318,9 @@ impl TransactionPlan { /// Convenience method to get all the destination addresses for each `OutputPlan`s. pub fn dest_addresses(&self) -> Vec
{ - self.output_plans().map(|plan| plan.dest_address).collect() + self.output_plans() + .map(|plan| plan.dest_address.clone()) + .collect() } /// Convenience method to get the number of `OutputPlan`s in this transaction. @@ -492,7 +494,7 @@ mod tests { .unwrap() .id(), }), - addr, + addr.clone(), ); let mut rng = OsRng; diff --git a/crates/view/src/planner.rs b/crates/view/src/planner.rs index bce94191d2..dcf87e7f52 100644 --- a/crates/view/src/planner.rs +++ b/crates/view/src/planner.rs @@ -714,7 +714,7 @@ impl Planner { // For any remaining provided balance, make a single change note for each for value in self.balance.provided().collect::>() { - self.output(value, self_address); + self.output(value, self_address.clone()); } // All actions have now been added, so check to make sure that you don't build and submit an diff --git a/crates/view/src/service.rs b/crates/view/src/service.rs index afdcce9fc4..327f90707b 100644 --- a/crates/view/src/service.rs +++ b/crates/view/src/service.rs @@ -926,12 +926,12 @@ impl ViewService for ViewServer { match action_view { ActionView::Spend(SpendView::Visible { note, .. }) => { let address = note.address(); - address_views.insert(address, fvk.view_address(address)); + address_views.insert(address.clone(), fvk.view_address(address)); asset_ids.insert(note.asset_id()); } ActionView::Output(OutputView::Visible { note, .. }) => { let address = note.address(); - address_views.insert(address, fvk.view_address(address)); + address_views.insert(address.clone(), fvk.view_address(address.clone())); asset_ids.insert(note.asset_id()); // Also add an AddressView for the return address in the memo. @@ -941,8 +941,8 @@ impl ViewService for ViewServer { address_views.insert(memo.return_address(), fvk.view_address(address)); } ActionView::Swap(SwapView::Visible { swap_plaintext, .. }) => { - let address = swap_plaintext.claim_address; - address_views.insert(address, fvk.view_address(address)); + let address = swap_plaintext.claim_address.clone(); + address_views.insert(address.clone(), fvk.view_address(address)); asset_ids.insert(swap_plaintext.trading_pair.asset_1()); asset_ids.insert(swap_plaintext.trading_pair.asset_2()); } @@ -951,13 +951,13 @@ impl ViewService for ViewServer { }) => { // Both will be sent to the same address so this only needs to be added once let address = output_1.address(); - address_views.insert(address, fvk.view_address(address)); + address_views.insert(address.clone(), fvk.view_address(address)); asset_ids.insert(output_1.asset_id()); asset_ids.insert(output_2.asset_id()); } ActionView::DelegatorVote(DelegatorVoteView::Visible { note, .. }) => { let address = note.address(); - address_views.insert(address, fvk.view_address(address)); + address_views.insert(address.clone(), fvk.view_address(address)); asset_ids.insert(note.asset_id()); } _ => {} diff --git a/crates/wallet/src/plan.rs b/crates/wallet/src/plan.rs index dd993ccb5b..2d438dced0 100644 --- a/crates/wallet/src/plan.rs +++ b/crates/wallet/src/plan.rs @@ -106,7 +106,7 @@ where let mut planner = Planner::new(rng); planner.fee(fee); for value in values.iter().cloned() { - planner.output(value, dest_address); + planner.output(value, dest_address.clone()); } let source_address = view.address_by_index(source_address_index).await?; planner diff --git a/tools/summonerd/src/participant.rs b/tools/summonerd/src/participant.rs index 81bc38e1df..f0b0035505 100644 --- a/tools/summonerd/src/participant.rs +++ b/tools/summonerd/src/participant.rs @@ -43,7 +43,7 @@ impl Participant { } pub fn address(&self) -> Address { - self.address + self.address.clone() } pub fn is_live(&self) -> bool { diff --git a/tools/summonerd/src/queue.rs b/tools/summonerd/src/queue.rs index 21f8bc03ab..10aaa313f1 100644 --- a/tools/summonerd/src/queue.rs +++ b/tools/summonerd/src/queue.rs @@ -151,7 +151,7 @@ impl ParticipantQueue { for (i, (participant, bid)) in participants.iter().enumerate() { let address = participant.address(); match filter { - Some(f) if f != address => continue, + Some(ref f) if *f != address => continue, _ => {} } // Ignore failures (besides logging), let pruning happen later. diff --git a/tools/summonerd/src/server.rs b/tools/summonerd/src/server.rs index 83db48222f..f1b892cda9 100644 --- a/tools/summonerd/src/server.rs +++ b/tools/summonerd/src/server.rs @@ -97,7 +97,7 @@ impl server::CeremonyCoordinatorService for CoordinatorService { } }; tracing::info!(?amount, ?address, "bid"); - let (participant, response_rx) = Participant::new(address, streaming); + let (participant, response_rx) = Participant::new(address.clone(), streaming); self.queue.push(participant, amount).await; self.queue .inform_one(address)