mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-25 09:29:33 +00:00
Merge 3c6d623c495a815687d2489b9d451c2f280877e4 into 694e48422847771b9d3d1948dc796830986003f2
This commit is contained in:
commit
9a550847d9
@ -16,7 +16,6 @@ ignore = [
|
||||
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0145", reason = "`astral-tokio-tar` v0.6.1 is pulled transitively via testcontainers (integration_tests dev/test path); waiting on upstream fix" },
|
||||
]
|
||||
yanked = "deny"
|
||||
unused-ignored-advisory = "deny"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
artifacts/test_program_methods/pda_spend_proxy.bin
Normal file
BIN
artifacts/test_program_methods/pda_spend_proxy.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -704,6 +704,7 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> {
|
||||
npk,
|
||||
ssk,
|
||||
identifier: 1337,
|
||||
seed: None,
|
||||
},
|
||||
],
|
||||
&program_with_deps,
|
||||
|
||||
@ -6,27 +6,37 @@
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use authenticated_transfer_core::Instruction as AuthTransferInstruction;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{
|
||||
NSSA_PROGRAM_FOR_TEST_PDA_FUND_SPEND_PROXY, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
|
||||
NSSA_PROGRAM_FOR_TEST_PDA_SPEND_PROXY, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
|
||||
verify_commitment_is_in_state,
|
||||
};
|
||||
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use log::info;
|
||||
use nssa::{
|
||||
AccountId, ProgramId, privacy_preserving_transaction::circuit::ProgramWithDependencies,
|
||||
AccountId, PrivacyPreservingTransaction, ProgramId,
|
||||
privacy_preserving_transaction::{
|
||||
circuit::{ProgramWithDependencies, execute_and_prove},
|
||||
message::Message,
|
||||
witness_set::WitnessSet,
|
||||
},
|
||||
program::Program,
|
||||
};
|
||||
use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey, program::PdaSeed};
|
||||
use nssa_core::{
|
||||
InputAccountIdentity, NullifierPublicKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::ViewingPublicKey,
|
||||
program::PdaSeed,
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::{
|
||||
PrivacyPreservingAccount, WalletCore,
|
||||
cli::{Command, account::AccountSubcommand},
|
||||
};
|
||||
|
||||
/// Funds a private PDA via the proxy program with a chained call to `auth_transfer`.
|
||||
///
|
||||
/// A direct call to `auth_transfer` cannot establish the PDA-to-npk binding because it uses
|
||||
/// `Claim::Authorized` rather than `Claim::Pda`. Routing through the proxy provides the binding
|
||||
/// via `pda_seeds` in the chained call to `auth_transfer`.
|
||||
/// Funds a private PDA by calling `auth_transfer` directly.
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "test helper — grouping args would obscure intent"
|
||||
@ -34,32 +44,68 @@ use wallet::{
|
||||
async fn fund_private_pda(
|
||||
wallet: &WalletCore,
|
||||
sender: AccountId,
|
||||
pda_account_id: AccountId,
|
||||
npk: NullifierPublicKey,
|
||||
vpk: ViewingPublicKey,
|
||||
identifier: u128,
|
||||
seed: PdaSeed,
|
||||
authority_program_id: ProgramId,
|
||||
amount: u128,
|
||||
proxy_program: &ProgramWithDependencies,
|
||||
auth_transfer_id: ProgramId,
|
||||
auth_transfer: &ProgramWithDependencies,
|
||||
) -> Result<()> {
|
||||
wallet
|
||||
.send_privacy_preserving_tx(
|
||||
vec![
|
||||
PrivacyPreservingAccount::Public(sender),
|
||||
PrivacyPreservingAccount::PrivatePdaForeign {
|
||||
account_id: pda_account_id,
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
},
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, true))
|
||||
.context("failed to serialize pda_fund_spend_proxy fund instruction")?,
|
||||
proxy_program,
|
||||
)
|
||||
let pda_account_id = AccountId::for_private_pda(&authority_program_id, &seed, &npk, identifier);
|
||||
let sender_account = wallet
|
||||
.get_account_public(sender)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
.map_err(|e| anyhow::anyhow!("failed to get sender account: {e}"))?;
|
||||
let sender_sk = wallet
|
||||
.get_account_public_signing_key(sender)
|
||||
.context("sender signing key not found")?;
|
||||
|
||||
let sender_pre = AccountWithMetadata::new(sender_account.clone(), true, sender);
|
||||
let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_account_id);
|
||||
|
||||
let eph_holder = EphemeralKeyHolder::new(&npk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender(&vpk);
|
||||
let epk = eph_holder.generate_ephemeral_public_key();
|
||||
|
||||
let instruction = Program::serialize_instruction(AuthTransferInstruction::Transfer { amount })
|
||||
.context("failed to serialize auth_transfer instruction")?;
|
||||
|
||||
let account_identities = vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
npk,
|
||||
ssk,
|
||||
identifier,
|
||||
seed: Some((seed, authority_program_id)),
|
||||
},
|
||||
];
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender_pre, pda_pre],
|
||||
instruction,
|
||||
account_identities,
|
||||
auth_transfer,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("circuit proving failed: {e}"))?;
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![sender],
|
||||
vec![sender_account.nonce],
|
||||
vec![(npk, vpk, epk)],
|
||||
output,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("message build failed: {e}"))?;
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[sender_sk]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
wallet
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::PrivacyPreserving(tx))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("send transaction failed: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -78,7 +124,7 @@ async fn spend_private_pda(
|
||||
seed: PdaSeed,
|
||||
amount: u128,
|
||||
spend_program: &ProgramWithDependencies,
|
||||
auth_transfer_id: nssa::ProgramId,
|
||||
auth_transfer_id: ProgramId,
|
||||
) -> Result<()> {
|
||||
wallet
|
||||
.send_privacy_preserving_tx(
|
||||
@ -90,8 +136,8 @@ async fn spend_private_pda(
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, false))
|
||||
.context("failed to serialize pda_fund_spend_proxy instruction")?,
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id))
|
||||
.context("failed to serialize pda_spend_proxy instruction")?,
|
||||
spend_program,
|
||||
)
|
||||
.await
|
||||
@ -124,9 +170,9 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
|
||||
let proxy = {
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../artifacts/test_program_methods")
|
||||
.join(NSSA_PROGRAM_FOR_TEST_PDA_FUND_SPEND_PROXY);
|
||||
.join(NSSA_PROGRAM_FOR_TEST_PDA_SPEND_PROXY);
|
||||
Program::new(std::fs::read(&path).with_context(|| format!("reading {path:?}"))?)
|
||||
.context("invalid pda_fund_spend_proxy binary")?
|
||||
.context("invalid pda_spend_proxy binary")?
|
||||
};
|
||||
let auth_transfer = Program::authenticated_transfer_program();
|
||||
let proxy_id = proxy.id();
|
||||
@ -134,6 +180,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let amount: u128 = 100;
|
||||
|
||||
let auth_transfer_program = ProgramWithDependencies::new(auth_transfer.clone(), [].into());
|
||||
let spend_program =
|
||||
ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer)].into());
|
||||
|
||||
@ -151,14 +198,13 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
|
||||
fund_private_pda(
|
||||
ctx.wallet(),
|
||||
sender_0,
|
||||
alice_pda_0_id,
|
||||
alice_npk,
|
||||
alice_vpk.clone(),
|
||||
0,
|
||||
seed,
|
||||
proxy_id,
|
||||
amount,
|
||||
&spend_program,
|
||||
auth_transfer_id,
|
||||
&auth_transfer_program,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -166,14 +212,13 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
|
||||
fund_private_pda(
|
||||
ctx.wallet(),
|
||||
sender_1,
|
||||
alice_pda_1_id,
|
||||
alice_npk,
|
||||
alice_vpk.clone(),
|
||||
1,
|
||||
seed,
|
||||
proxy_id,
|
||||
amount,
|
||||
&spend_program,
|
||||
auth_transfer_id,
|
||||
&auth_transfer_program,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::Ciphertext,
|
||||
program::{BlockValidityWindow, ProgramId, ProgramOutput, TimestampValidityWindow},
|
||||
program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -60,15 +60,28 @@ pub enum InputAccountIdentity {
|
||||
npk: NullifierPublicKey,
|
||||
ssk: SharedSecretKey,
|
||||
identifier: Identifier,
|
||||
/// When `Some((seed, authority_program_id))`, the circuit binds this position via the
|
||||
/// external derivation check
|
||||
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) ==
|
||||
/// pre_state.account_id` rather than requiring a `Claim::Pda` or caller
|
||||
/// `pda_seeds` to establish the binding. The `pre_state` must have `is_authorized
|
||||
/// == false`.
|
||||
seed: Option<(PdaSeed, ProgramId)>,
|
||||
},
|
||||
/// Update of an existing private PDA, authorized, with membership proof. `npk` is derived
|
||||
/// from `nsk`. Authorization is established upstream by a caller `pda_seeds` match or a
|
||||
/// Update of an existing private PDA, with membership proof. `npk` is derived
|
||||
/// from `nsk`. Authorization may be established upstream by a caller `pda_seeds` match or a
|
||||
/// previously-seen authorization in a chained call.
|
||||
PrivatePdaUpdate {
|
||||
ssk: SharedSecretKey,
|
||||
nsk: NullifierSecretKey,
|
||||
membership_proof: MembershipProof,
|
||||
identifier: Identifier,
|
||||
/// When `Some((seed, authority_program_id))`, the circuit binds this position via the
|
||||
/// external derivation check
|
||||
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) ==
|
||||
/// pre_state.account_id` rather than requiring a caller `pda_seeds` to establish
|
||||
/// the binding. The `pre_state` must have `is_authorized == false`.
|
||||
seed: Option<(PdaSeed, ProgramId)>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -461,6 +461,7 @@ mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier,
|
||||
seed: None,
|
||||
}],
|
||||
&program.clone().into(),
|
||||
)
|
||||
@ -488,7 +489,7 @@ mod tests {
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret_pda = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
|
||||
// PDA (new, mask 3)
|
||||
// PDA (new, private PDA)
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 0);
|
||||
let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id);
|
||||
|
||||
@ -506,6 +507,7 @@ mod tests {
|
||||
npk,
|
||||
ssk: shared_secret_pda,
|
||||
identifier: 0,
|
||||
seed: None,
|
||||
}],
|
||||
&program_with_deps,
|
||||
);
|
||||
@ -557,6 +559,7 @@ mod tests {
|
||||
npk,
|
||||
ssk: shared_secret_pda,
|
||||
identifier: 0,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
@ -747,7 +750,7 @@ mod tests {
|
||||
/// to `PrivateAccountKind::Pda` carrying the correct `(program_id, seed, identifier)`.
|
||||
#[test]
|
||||
fn private_pda_update_encrypts_pda_kind_with_identifier() {
|
||||
let program = Program::pda_fund_spend_proxy();
|
||||
let program = Program::pda_spend_proxy();
|
||||
let auth_transfer = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
@ -784,6 +787,7 @@ mod tests {
|
||||
nsk: keys.nsk,
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
identifier,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
@ -819,6 +823,7 @@ mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: 99,
|
||||
seed: None,
|
||||
}],
|
||||
&program.into(),
|
||||
);
|
||||
@ -828,7 +833,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn private_pda_update_identifier_mismatch_fails() {
|
||||
let program = Program::pda_fund_spend_proxy();
|
||||
let program = Program::pda_spend_proxy();
|
||||
let auth_transfer = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
@ -862,6 +867,7 @@ mod tests {
|
||||
nsk: keys.nsk,
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
identifier: 99,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
|
||||
@ -350,12 +350,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pda_fund_spend_proxy() -> Self {
|
||||
use test_program_methods::{PDA_FUND_SPEND_PROXY_ELF, PDA_FUND_SPEND_PROXY_ID};
|
||||
pub fn pda_spend_proxy() -> Self {
|
||||
use test_program_methods::{PDA_SPEND_PROXY_ELF, PDA_SPEND_PROXY_ID};
|
||||
|
||||
Self {
|
||||
id: PDA_FUND_SPEND_PROXY_ID,
|
||||
elf: PDA_FUND_SPEND_PROXY_ELF.to_vec(),
|
||||
id: PDA_SPEND_PROXY_ID,
|
||||
elf: PDA_SPEND_PROXY_ELF.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2218,7 +2218,7 @@ pub mod tests {
|
||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// A mask-3 account that no program claims via `Claim::Pda` and no caller authorizes via
|
||||
/// A private PDA account that no program claims via `Claim::Pda` and no caller authorizes via
|
||||
/// `ChainedCall.pda_seeds` has no binding between its supplied npk and its `account_id`,
|
||||
/// so the circuit must reject. Here `simple_balance_transfer` emits no claim for the
|
||||
/// second account, leaving position 1 unbound.
|
||||
@ -2249,6 +2249,7 @@ pub mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
},
|
||||
],
|
||||
&program.into(),
|
||||
@ -2257,7 +2258,7 @@ pub mod tests {
|
||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// Happy path: a program claims a new mask-3 account via `Claim::Pda(seed)`. The circuit
|
||||
/// Happy path: a program claims a new private PDA via `Claim::Pda(seed)`. The circuit
|
||||
/// reads the npk for that `pre_state` from `private_account_keys` at the `pre_state`'s
|
||||
/// position, derives `AccountId` via `AccountId::for_private_pda(program_id, seed, npk)`, and
|
||||
/// asserts it equals the `pre_state`'s `account_id`. The equality both validates the claim
|
||||
@ -2280,11 +2281,12 @@ pub mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
}],
|
||||
&program.into(),
|
||||
);
|
||||
|
||||
let (output, _proof) = result.expect("mask-3 private PDA claim should succeed");
|
||||
let (output, _proof) = result.expect("private PDA claim should succeed");
|
||||
assert_eq!(output.new_nullifiers.len(), 1);
|
||||
assert_eq!(output.new_commitments.len(), 1);
|
||||
assert_eq!(output.ciphertexts.len(), 1);
|
||||
@ -2319,6 +2321,7 @@ pub mod tests {
|
||||
npk: npk_b,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
}],
|
||||
&program.into(),
|
||||
);
|
||||
@ -2326,7 +2329,7 @@ pub mod tests {
|
||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// Happy path for the caller-seeds authorization of a mask-3 PDA. The delegator claims a
|
||||
/// Happy path for the caller-seeds authorization of a private PDA. The delegator claims a
|
||||
/// private PDA via `Claim::Pda(seed)`, then chains to a callee (`noop`) delegating the same
|
||||
/// seed via `ChainedCall.pda_seeds`. In the callee's step, the `pre_state`'s authorization
|
||||
/// is established via the private derivation
|
||||
@ -2354,12 +2357,13 @@ pub mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
}],
|
||||
&program_with_deps,
|
||||
);
|
||||
|
||||
let (output, _proof) =
|
||||
result.expect("caller-seeds authorization of mask-3 private PDA should succeed");
|
||||
result.expect("caller-seeds authorization of private PDA should succeed");
|
||||
assert_eq!(output.new_commitments.len(), 1);
|
||||
assert_eq!(output.new_nullifiers.len(), 1);
|
||||
}
|
||||
@ -2392,6 +2396,7 @@ pub mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
}],
|
||||
&program_with_deps,
|
||||
);
|
||||
@ -2401,8 +2406,8 @@ pub mod tests {
|
||||
|
||||
/// Exploit-scenario pin. A single `(program_id, seed)` pair can derive a family of
|
||||
/// `AccountId`s, one public PDA and one private PDA per distinct npk. Without the tx-wide
|
||||
/// family-binding check, a program could claim `PDA_alice` (mask-3, `alice_npk`) and
|
||||
/// `PDA_bob` (mask-3, `bob_npk`) under the same seed in one transaction, and once reuse
|
||||
/// family-binding check, a program could claim `PDA_alice` (`alice_npk`) and
|
||||
/// `PDA_bob` (`bob_npk`) under the same seed in one transaction, and once reuse
|
||||
/// is supported a later chained call could delegate both to a callee via
|
||||
/// `pda_seeds: [S]` and mix balances across them. The binding check rejects the setup
|
||||
/// here: after the first claim records `(program, seed) → PDA_alice`, the second claim
|
||||
@ -2430,11 +2435,13 @@ pub mod tests {
|
||||
npk: keys_a.npk(),
|
||||
ssk: shared_a,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
npk: keys_b.npk(),
|
||||
ssk: shared_b,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
},
|
||||
],
|
||||
&program.into(),
|
||||
@ -2443,17 +2450,11 @@ pub mod tests {
|
||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// Pins the current limitation: a mask-3 PDA that was claimed in a previous transaction
|
||||
/// cannot be re-used in a new transaction as-is. This PR only binds supplied npks via a
|
||||
/// fresh `Claim::Pda` or a caller's `ChainedCall.pda_seeds`, neither is present when a
|
||||
/// program operates on an already-owned private PDA at top level. The reject site is the
|
||||
/// post-loop `private_pda_bound_positions` assertion in
|
||||
/// `privacy_preserving_circuit.rs`: `noop` emits no `Claim::Pda` and there is no caller
|
||||
/// A private PDA that is reused at top level without an external seed in the identity still
|
||||
/// fails binding. The noop program emits no `Claim::Pda` and there is no caller
|
||||
/// `ChainedCall.pda_seeds`, so position 0 is never bound and the assertion fires.
|
||||
// TODO: a follow-up PR in the Private PDAs series needs to let the wallet supply a
|
||||
// `(seed, original_owner_program_id)` side input per mask-3 `pre_state` so the circuit
|
||||
// can re-verify `AccountId::for_private_pda(owner, seed, npk) == pre.account_id` without a
|
||||
// claim.
|
||||
/// Supplying `seed: Some((seed, owner_program_id))` in the `PrivatePdaUpdate` identity is
|
||||
/// the correct path for top-level reuse; this test pins the failure when no seed is provided.
|
||||
#[test]
|
||||
fn private_pda_top_level_reuse_rejected_by_binding_check() {
|
||||
let program = Program::noop();
|
||||
@ -2481,6 +2482,7 @@ pub mod tests {
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
}],
|
||||
&program.into(),
|
||||
);
|
||||
@ -4372,15 +4374,15 @@ pub mod tests {
|
||||
let alice_keys = test_private_account_keys_1();
|
||||
let alice_npk = alice_keys.npk();
|
||||
|
||||
let proxy = Program::pda_fund_spend_proxy();
|
||||
let proxy = Program::pda_spend_proxy();
|
||||
let auth_transfer = Program::authenticated_transfer_program();
|
||||
let proxy_id = proxy.id();
|
||||
let auth_transfer_id = auth_transfer.id();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let amount: u128 = 100;
|
||||
|
||||
let program_with_deps =
|
||||
ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer)].into());
|
||||
let spend_with_deps =
|
||||
ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer.clone())].into());
|
||||
|
||||
let funder_id = funder_keys.account_id();
|
||||
let alice_pda_0_id = AccountId::for_private_pda(&proxy_id, &seed, &alice_npk, 0);
|
||||
@ -4406,7 +4408,7 @@ pub mod tests {
|
||||
let alice_shared_0 = SharedSecretKey::new([10; 32], &alice_keys.vpk());
|
||||
let alice_shared_1 = SharedSecretKey::new([11; 32], &alice_keys.vpk());
|
||||
|
||||
// Fund alice_pda_0
|
||||
// Fund alice_pda_0 via authenticated_transfer directly.
|
||||
{
|
||||
let funder_account = state.get_account_by_id(funder_id);
|
||||
let funder_nonce = funder_account.nonce;
|
||||
@ -4415,16 +4417,18 @@ pub mod tests {
|
||||
AccountWithMetadata::new(funder_account, true, funder_id),
|
||||
AccountWithMetadata::new(Account::default(), false, alice_pda_0_id),
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, true)).unwrap(),
|
||||
Program::serialize_instruction(AuthTransferInstruction::Transfer { amount })
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
npk: alice_npk,
|
||||
ssk: alice_shared_0,
|
||||
identifier: 0,
|
||||
seed: Some((seed, proxy_id)),
|
||||
},
|
||||
],
|
||||
&program_with_deps,
|
||||
&auth_transfer.clone().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
@ -4448,7 +4452,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Fund alice_pda_1
|
||||
// Fund alice_pda_1 the same way with identifier 1.
|
||||
{
|
||||
let funder_account = state.get_account_by_id(funder_id);
|
||||
let funder_nonce = funder_account.nonce;
|
||||
@ -4457,16 +4461,18 @@ pub mod tests {
|
||||
AccountWithMetadata::new(funder_account, true, funder_id),
|
||||
AccountWithMetadata::new(Account::default(), false, alice_pda_1_id),
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, true)).unwrap(),
|
||||
Program::serialize_instruction(AuthTransferInstruction::Transfer { amount })
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
npk: alice_npk,
|
||||
ssk: alice_shared_1,
|
||||
identifier: 1,
|
||||
seed: Some((seed, proxy_id)),
|
||||
},
|
||||
],
|
||||
&program_with_deps,
|
||||
&auth_transfer.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
@ -4504,7 +4510,7 @@ pub mod tests {
|
||||
AccountWithMetadata::new(alice_pda_0_account, true, alice_pda_0_id),
|
||||
AccountWithMetadata::new(recipient_account, true, recipient_id),
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, false)).unwrap(),
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
ssk: alice_shared_0,
|
||||
@ -4513,10 +4519,11 @@ pub mod tests {
|
||||
.get_proof_for_commitment(&commitment_pda_0)
|
||||
.expect("pda_0 must be in state"),
|
||||
identifier: 0,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
&program_with_deps,
|
||||
&spend_with_deps,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
@ -4545,10 +4552,10 @@ pub mod tests {
|
||||
let recipient_account = state.get_account_by_id(recipient_id);
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![
|
||||
AccountWithMetadata::new(alice_pda_1_account, true, alice_pda_1_id),
|
||||
AccountWithMetadata::new(alice_pda_1_account.clone(), true, alice_pda_1_id),
|
||||
AccountWithMetadata::new(recipient_account, false, recipient_id),
|
||||
],
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, false)).unwrap(),
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
ssk: alice_shared_1,
|
||||
@ -4557,10 +4564,11 @@ pub mod tests {
|
||||
.get_proof_for_commitment(&commitment_pda_1)
|
||||
.expect("pda_1 must be in state"),
|
||||
identifier: 1,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
&program_with_deps,
|
||||
&spend_with_deps,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
@ -4585,5 +4593,70 @@ pub mod tests {
|
||||
}
|
||||
|
||||
assert_eq!(state.get_account_by_id(recipient_id).balance, 2 * amount);
|
||||
|
||||
// Re-fund alice_pda_1 top-level via auth_transfer using PrivatePdaUpdate with an
|
||||
// external seed.
|
||||
let alice_pda_1_account_after_spend = Account {
|
||||
program_owner: auth_transfer_id,
|
||||
balance: 0,
|
||||
nonce: alice_pda_1_account
|
||||
.nonce
|
||||
.private_account_nonce_increment(&alice_keys.nsk),
|
||||
..Account::default()
|
||||
};
|
||||
let commitment_pda_1_after_spend =
|
||||
Commitment::new(&alice_pda_1_id, &alice_pda_1_account_after_spend);
|
||||
let alice_shared_1_refund = SharedSecretKey::new([12; 32], &alice_keys.vpk());
|
||||
{
|
||||
let recipient_account = state.get_account_by_id(recipient_id);
|
||||
let recipient_nonce = recipient_account.nonce;
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![
|
||||
AccountWithMetadata::new(recipient_account, true, recipient_id),
|
||||
AccountWithMetadata::new(
|
||||
alice_pda_1_account_after_spend,
|
||||
false,
|
||||
alice_pda_1_id,
|
||||
),
|
||||
],
|
||||
Program::serialize_instruction(AuthTransferInstruction::Transfer { amount })
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
nsk: alice_keys.nsk,
|
||||
ssk: alice_shared_1_refund,
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&commitment_pda_1_after_spend)
|
||||
.expect("pda_1 after spend must be in state"),
|
||||
identifier: 1,
|
||||
seed: Some((seed, proxy_id)),
|
||||
},
|
||||
],
|
||||
&Program::authenticated_transfer_program().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![recipient_nonce],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey::from_scalar([12; 32]),
|
||||
)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
&PrivacyPreservingTransaction::new(message, witness_set),
|
||||
5,
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(state.get_account_by_id(recipient_id).balance, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,6 +305,68 @@ impl ExecutionState {
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
// Pre state for the initial call
|
||||
let pre_state_position = self.pre_states.len();
|
||||
let external_seed = match account_identities.get(pre_state_position) {
|
||||
Some(InputAccountIdentity::PrivatePdaInit {
|
||||
npk,
|
||||
identifier,
|
||||
seed: Some((seed, authority_program_id)),
|
||||
..
|
||||
}) => {
|
||||
let expected = AccountId::for_private_pda(
|
||||
authority_program_id,
|
||||
seed,
|
||||
npk,
|
||||
*identifier,
|
||||
);
|
||||
assert_eq!(
|
||||
pre_account_id, expected,
|
||||
"External seed mismatch for PrivatePdaInit at position {pre_state_position}"
|
||||
);
|
||||
Some((*seed, *authority_program_id))
|
||||
}
|
||||
Some(InputAccountIdentity::PrivatePdaUpdate {
|
||||
nsk,
|
||||
identifier,
|
||||
seed: Some((seed, authority_program_id)),
|
||||
..
|
||||
}) => {
|
||||
let npk = NullifierPublicKey::from(nsk);
|
||||
let expected = AccountId::for_private_pda(
|
||||
authority_program_id,
|
||||
seed,
|
||||
&npk,
|
||||
*identifier,
|
||||
);
|
||||
assert_eq!(
|
||||
pre_account_id, expected,
|
||||
"External seed mismatch for PrivatePdaUpdate at position {pre_state_position}"
|
||||
);
|
||||
Some((*seed, *authority_program_id))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
// External seed is only consulted the first time the account is seen.
|
||||
// Subsequent calls need no re-check because the entry is already recorded on
|
||||
// private_pda_bound_positions.
|
||||
if let Some((seed, authority_program_id)) = external_seed {
|
||||
assert!(
|
||||
!pre.is_authorized,
|
||||
"Private PDA with externally-provided seed must not be authorized at position {pre_state_position}"
|
||||
);
|
||||
bind_private_pda_position(
|
||||
&mut self.private_pda_bound_positions,
|
||||
pre_state_position,
|
||||
authority_program_id,
|
||||
seed,
|
||||
);
|
||||
assert_family_binding(
|
||||
&mut self.pda_family_binding,
|
||||
authority_program_id,
|
||||
seed,
|
||||
pre_account_id,
|
||||
);
|
||||
}
|
||||
self.pre_states.push(pre);
|
||||
}
|
||||
}
|
||||
@ -348,14 +410,11 @@ impl ExecutionState {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if account_identity.is_private_pda() {
|
||||
} else {
|
||||
// Private accounts: don't enforce the claim semantics. Unauthorized private
|
||||
// claiming is intentionally allowed
|
||||
match claim {
|
||||
Claim::Authorized => {
|
||||
assert!(
|
||||
pre_is_authorized,
|
||||
"Cannot claim unauthorized private PDA {pre_account_id}"
|
||||
);
|
||||
}
|
||||
Claim::Authorized => {}
|
||||
Claim::Pda(seed) => {
|
||||
let (npk, identifier) = self
|
||||
.private_pda_npk_by_position
|
||||
@ -383,10 +442,6 @@ impl ExecutionState {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Standalone private accounts: don't enforce the claim semantics.
|
||||
// Unauthorized private claiming is intentionally allowed since operating
|
||||
// these accounts requires the npk/nsk keypair anyway.
|
||||
}
|
||||
|
||||
post.account_mut().program_owner = program_id;
|
||||
|
||||
@ -148,6 +148,7 @@ pub fn compute_circuit_output(
|
||||
npk: _,
|
||||
ssk,
|
||||
identifier,
|
||||
seed: _,
|
||||
} => {
|
||||
// The npk-to-account_id binding is established upstream in
|
||||
// `validate_and_sync_states` via `Claim::Pda(seed)` or a caller `pda_seeds`
|
||||
@ -172,7 +173,7 @@ pub fn compute_circuit_output(
|
||||
let new_nonce = Nonce::private_account_nonce_init(&pre_state.account_id);
|
||||
|
||||
let account_id = pre_state.account_id;
|
||||
let (pda_program_id, seed) = pda_seed_by_position
|
||||
let (authority_program_id, seed) = pda_seed_by_position
|
||||
.get(&pos)
|
||||
.expect("PrivatePdaInit position must be in pda_seed_by_position");
|
||||
emit_private_output(
|
||||
@ -181,7 +182,7 @@ pub fn compute_circuit_output(
|
||||
post_state,
|
||||
&account_id,
|
||||
&PrivateAccountKind::Pda {
|
||||
program_id: *pda_program_id,
|
||||
program_id: *authority_program_id,
|
||||
seed: *seed,
|
||||
identifier: *identifier,
|
||||
},
|
||||
@ -195,14 +196,16 @@ pub fn compute_circuit_output(
|
||||
nsk,
|
||||
membership_proof,
|
||||
identifier,
|
||||
seed: external_seed,
|
||||
} => {
|
||||
// The npk binding is established upstream. Authorization must already be set;
|
||||
// an unauthorized PrivatePdaUpdate would mean the prover supplied an nsk for an
|
||||
// unbound PDA, which the upstream binding check would have rejected anyway,
|
||||
// but we assert here to fail fast and document the precondition.
|
||||
// With an external seed the binding comes from the circuit input and the
|
||||
// pre_state is intentionally unauthorized; without one the binding comes from
|
||||
// a Claim or caller pda_seeds, so the pre_state must already be authorized.
|
||||
// When `external_seed` is `Some`, execution_state already asserted
|
||||
// `!pre_state.is_authorized`.
|
||||
assert!(
|
||||
pre_state.is_authorized,
|
||||
"PrivatePdaUpdate requires authorized pre_state"
|
||||
pre_state.is_authorized ^ external_seed.is_some(),
|
||||
"PrivatePdaUpdate requires authorized pre_state or external seed"
|
||||
);
|
||||
|
||||
let new_nullifier = compute_update_nullifier_and_set_digest(
|
||||
@ -214,7 +217,7 @@ pub fn compute_circuit_output(
|
||||
let new_nonce = pre_state.account.nonce.private_account_nonce_increment(nsk);
|
||||
|
||||
let account_id = pre_state.account_id;
|
||||
let (pda_program_id, seed) = pda_seed_by_position
|
||||
let (authority_program_id, seed) = pda_seed_by_position
|
||||
.get(&pos)
|
||||
.expect("PrivatePdaUpdate position must be in pda_seed_by_position");
|
||||
emit_private_output(
|
||||
@ -223,7 +226,7 @@ pub fn compute_circuit_output(
|
||||
post_state,
|
||||
&account_id,
|
||||
&PrivateAccountKind::Pda {
|
||||
program_id: *pda_program_id,
|
||||
program_id: *authority_program_id,
|
||||
seed: *seed,
|
||||
identifier: *identifier,
|
||||
},
|
||||
|
||||
@ -34,7 +34,7 @@ pub mod setup;
|
||||
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
||||
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &str = "data_changer.bin";
|
||||
pub const NSSA_PROGRAM_FOR_TEST_NOOP: &str = "noop.bin";
|
||||
pub const NSSA_PROGRAM_FOR_TEST_PDA_FUND_SPEND_PROXY: &str = "pda_fund_spend_proxy.bin";
|
||||
pub const NSSA_PROGRAM_FOR_TEST_PDA_SPEND_PROXY: &str = "pda_spend_proxy.bin";
|
||||
|
||||
pub(crate) const BEDROCK_SERVICE_WITH_OPEN_PORT: &str = "logos-blockchain-node-0";
|
||||
pub(crate) const BEDROCK_SERVICE_PORT: u16 = 18080;
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
use nssa_core::{
|
||||
account::AccountWithMetadata,
|
||||
program::{
|
||||
AccountPostState, ChainedCall, PdaSeed, ProgramId, ProgramInput, ProgramOutput,
|
||||
read_nssa_inputs,
|
||||
},
|
||||
};
|
||||
use risc0_zkvm::serde::to_vec;
|
||||
|
||||
/// Proxy for interacting with private PDAs via `auth_transfer`.
|
||||
///
|
||||
/// The `is_fund` flag selects the operating mode:
|
||||
///
|
||||
/// - `false` (Spend): `pre_states = [pda (authorized), recipient]`. Debits the PDA. The PDA-to-npk
|
||||
/// binding is established via `pda_seeds` in the chained call to `auth_transfer`.
|
||||
///
|
||||
/// - `true` (Fund): `pre_states = [sender (authorized), pda (foreign/uninitialized)]`. Credits the
|
||||
/// PDA. A direct call to `auth_transfer` cannot bind the PDA because `auth_transfer` uses
|
||||
/// `Claim::Authorized`, not `Claim::Pda`. Routing through this proxy establishes the binding via
|
||||
/// `pda_seeds` in the chained call.
|
||||
type Instruction = (PdaSeed, u128, ProgramId, bool);
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
pre_states,
|
||||
instruction: (seed, amount, auth_transfer_id, is_fund),
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let Ok([first, second]) = <[_; 2]>::try_from(pre_states) else {
|
||||
return;
|
||||
};
|
||||
|
||||
assert!(first.is_authorized, "first pre_state must be authorized");
|
||||
|
||||
let chained_pre_states = if is_fund {
|
||||
let pda_authorized = AccountWithMetadata {
|
||||
account: second.account.clone(),
|
||||
account_id: second.account_id,
|
||||
is_authorized: true,
|
||||
};
|
||||
vec![first.clone(), pda_authorized]
|
||||
} else {
|
||||
vec![first.clone(), second.clone()]
|
||||
};
|
||||
|
||||
let first_post = AccountPostState::new(first.account.clone());
|
||||
let second_post = AccountPostState::new(second.account.clone());
|
||||
|
||||
let chained_call = ChainedCall {
|
||||
program_id: auth_transfer_id,
|
||||
instruction_data: to_vec(&authenticated_transfer_core::Instruction::Transfer { amount })
|
||||
.unwrap(),
|
||||
pre_states: chained_pre_states,
|
||||
pda_seeds: vec![seed],
|
||||
};
|
||||
|
||||
ProgramOutput::new(
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
instruction_words,
|
||||
vec![first, second],
|
||||
vec![first_post, second_post],
|
||||
)
|
||||
.with_chained_calls(vec![chained_call])
|
||||
.write();
|
||||
}
|
||||
50
test_program_methods/guest/src/bin/pda_spend_proxy.rs
Normal file
50
test_program_methods/guest/src/bin/pda_spend_proxy.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use nssa_core::program::{
|
||||
AccountPostState, ChainedCall, PdaSeed, ProgramId, ProgramInput, ProgramOutput,
|
||||
read_nssa_inputs,
|
||||
};
|
||||
use risc0_zkvm::serde::to_vec;
|
||||
|
||||
/// Proxy for spending from a private PDA via `auth_transfer`.
|
||||
///
|
||||
/// `pre_states = [pda (authorized), recipient]`. Debits the PDA and credits the recipient.
|
||||
/// The PDA-to-npk binding is established via `pda_seeds` in the chained call to `auth_transfer`.
|
||||
type Instruction = (PdaSeed, u128, ProgramId);
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
pre_states,
|
||||
instruction: (seed, amount, auth_transfer_id),
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let Ok([first, second]) = <[_; 2]>::try_from(pre_states) else {
|
||||
return;
|
||||
};
|
||||
|
||||
assert!(first.is_authorized, "first pre_state must be authorized");
|
||||
|
||||
let first_post = AccountPostState::new(first.account.clone());
|
||||
let second_post = AccountPostState::new(second.account.clone());
|
||||
|
||||
let chained_call = ChainedCall {
|
||||
program_id: auth_transfer_id,
|
||||
instruction_data: to_vec(&authenticated_transfer_core::Instruction::Transfer { amount })
|
||||
.unwrap(),
|
||||
pre_states: vec![first.clone(), second.clone()],
|
||||
pda_seeds: vec![seed],
|
||||
};
|
||||
|
||||
ProgramOutput::new(
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
instruction_words,
|
||||
vec![first, second],
|
||||
vec![first_post, second_post],
|
||||
)
|
||||
.with_chained_calls(vec![chained_call])
|
||||
.write();
|
||||
}
|
||||
@ -15,7 +15,7 @@ use test_fixtures::{DiskSizes, TestContext};
|
||||
use wallet::cli::SubcommandReturnValue;
|
||||
|
||||
const TX_INCLUSION_POLL_INTERVAL: Duration = Duration::from_millis(250);
|
||||
const TX_INCLUSION_TIMEOUT: Duration = Duration::from_secs(120);
|
||||
const TX_INCLUSION_TIMEOUT: Duration = Duration::from_mins(2);
|
||||
|
||||
/// Borsh-serialized sizes for one zone block fetched after a step. `block_bytes`
|
||||
/// is the full Block (header + body + bedrock metadata) and is the closest
|
||||
|
||||
@ -181,7 +181,7 @@ async fn measure_bedrock_finality(ctx: &TestContext) -> Result<Duration> {
|
||||
.context("connect indexer WS")?;
|
||||
let sequencer_tip = ctx.sequencer_client().get_last_block_id().await?;
|
||||
|
||||
let timeout = Duration::from_secs(60);
|
||||
let timeout = Duration::from_mins(1);
|
||||
let started = std::time::Instant::now();
|
||||
let poll = async {
|
||||
loop {
|
||||
|
||||
@ -297,7 +297,7 @@ impl WalletCore {
|
||||
.key_chain()
|
||||
.group_key_holder(&entry.group_label)?;
|
||||
|
||||
if let (Some(pda_seed), Some(program_id)) = (entry.pda_seed, entry.pda_program_id) {
|
||||
if let (Some(pda_seed), Some(program_id)) = (entry.pda_seed, entry.authority_program_id) {
|
||||
let keys = holder.derive_keys_for_pda(&program_id, &pda_seed);
|
||||
Some(PrivacyPreservingAccount::PrivatePdaShared {
|
||||
account_id,
|
||||
@ -340,7 +340,7 @@ impl WalletCore {
|
||||
group_label: Label,
|
||||
identifier: nssa_core::Identifier,
|
||||
pda_seed: Option<nssa_core::program::PdaSeed>,
|
||||
pda_program_id: Option<nssa_core::program::ProgramId>,
|
||||
authority_program_id: Option<nssa_core::program::ProgramId>,
|
||||
) {
|
||||
self.storage.key_chain_mut().insert_shared_private_account(
|
||||
account_id,
|
||||
@ -348,7 +348,7 @@ impl WalletCore {
|
||||
group_label,
|
||||
identifier,
|
||||
pda_seed,
|
||||
pda_program_id,
|
||||
authority_program_id,
|
||||
account: Account::default(),
|
||||
},
|
||||
);
|
||||
@ -729,7 +729,7 @@ impl WalletCore {
|
||||
.key_chain()
|
||||
.group_key_holder(&entry.group_label)?;
|
||||
|
||||
let keys = match (&entry.pda_seed, &entry.pda_program_id) {
|
||||
let keys = match (&entry.pda_seed, &entry.authority_program_id) {
|
||||
(Some(pda_seed), Some(program_id)) => {
|
||||
holder.derive_keys_for_pda(program_id, pda_seed)
|
||||
}
|
||||
|
||||
@ -252,11 +252,13 @@ impl AccountManager {
|
||||
nsk,
|
||||
membership_proof,
|
||||
identifier: pre.identifier,
|
||||
seed: None,
|
||||
},
|
||||
_ => InputAccountIdentity::PrivatePdaInit {
|
||||
npk: pre.npk,
|
||||
ssk: pre.ssk,
|
||||
identifier: pre.identifier,
|
||||
seed: None,
|
||||
},
|
||||
},
|
||||
State::Private(pre) => match (pre.nsk, pre.proof.clone()) {
|
||||
|
||||
@ -55,7 +55,7 @@ pub struct SharedAccountEntry {
|
||||
/// For PDA accounts, the seed and program ID used to derive keys via `derive_keys_for_pda`.
|
||||
/// `None` for regular shared accounts (keys derived from identifier via derivation seed).
|
||||
pub pda_seed: Option<nssa_core::program::PdaSeed>,
|
||||
pub pda_program_id: Option<nssa_core::program::ProgramId>,
|
||||
pub authority_program_id: Option<nssa_core::program::ProgramId>,
|
||||
pub account: Account,
|
||||
}
|
||||
|
||||
@ -858,7 +858,7 @@ mod tests {
|
||||
group_label: Label::new("test-group"),
|
||||
identifier: 42,
|
||||
pda_seed: None,
|
||||
pda_program_id: None,
|
||||
authority_program_id: None,
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
||||
@ -871,7 +871,7 @@ mod tests {
|
||||
group_label: Label::new("pda-group"),
|
||||
identifier: u128::MAX,
|
||||
pda_seed: Some(PdaSeed::new([7_u8; 32])),
|
||||
pda_program_id: Some([9; 8]),
|
||||
authority_program_id: Some([9; 8]),
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let pda_encoded = bincode::serialize(&pda_entry).expect("serialize pda");
|
||||
@ -890,7 +890,7 @@ mod tests {
|
||||
group_label: Label::new("old"),
|
||||
identifier: 1,
|
||||
pda_seed: None,
|
||||
pda_program_id: None,
|
||||
authority_program_id: None,
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user