diff --git a/Cargo.lock b/Cargo.lock index a3b3d832f..773203db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,6 +1271,7 @@ dependencies = [ "bridge_lock_core", "cross_zone_outbox_core", "lee_core", + "program_loader_core", "risc0-zkvm", "wrapped_token_core", ] @@ -3182,6 +3183,7 @@ dependencies = [ "bytemuck", "hex", "lee_core", + "program_loader_core", "risc0-zkvm", ] @@ -12225,6 +12227,7 @@ dependencies = [ "clock_core", "faucet_core", "lee_core", + "program_loader_core", "risc0-zkvm", ] diff --git a/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin b/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin index 89429edfd..a2b323f23 100644 Binary files a/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin and b/artifacts/lee/privacy_preserving_circuit/privacy_preserving_circuit.bin differ diff --git a/artifacts/lez/programs/amm.bin b/artifacts/lez/programs/amm.bin index 6bb4300c6..5b3cd4666 100644 Binary files a/artifacts/lez/programs/amm.bin and b/artifacts/lez/programs/amm.bin differ diff --git a/artifacts/lez/programs/associated_token_account.bin b/artifacts/lez/programs/associated_token_account.bin index e3547d164..7bbbece3a 100644 Binary files a/artifacts/lez/programs/associated_token_account.bin and b/artifacts/lez/programs/associated_token_account.bin differ diff --git a/artifacts/lez/programs/authenticated_transfer.bin b/artifacts/lez/programs/authenticated_transfer.bin index dd2dd418d..a5aa623d8 100644 Binary files a/artifacts/lez/programs/authenticated_transfer.bin and b/artifacts/lez/programs/authenticated_transfer.bin differ diff --git a/artifacts/lez/programs/bridge.bin b/artifacts/lez/programs/bridge.bin index 0e042d225..0efc69d93 100644 Binary files a/artifacts/lez/programs/bridge.bin and b/artifacts/lez/programs/bridge.bin differ diff --git a/artifacts/lez/programs/bridge_lock.bin b/artifacts/lez/programs/bridge_lock.bin index d60332e5e..87673b2c8 100644 Binary files a/artifacts/lez/programs/bridge_lock.bin and b/artifacts/lez/programs/bridge_lock.bin differ diff --git a/artifacts/lez/programs/clock.bin b/artifacts/lez/programs/clock.bin index e987fdd20..773af5962 100644 Binary files a/artifacts/lez/programs/clock.bin and b/artifacts/lez/programs/clock.bin differ diff --git a/artifacts/lez/programs/cross_zone_inbox.bin b/artifacts/lez/programs/cross_zone_inbox.bin index c04e5089e..c5681fc24 100644 Binary files a/artifacts/lez/programs/cross_zone_inbox.bin and b/artifacts/lez/programs/cross_zone_inbox.bin differ diff --git a/artifacts/lez/programs/cross_zone_outbox.bin b/artifacts/lez/programs/cross_zone_outbox.bin index f073e4be9..9bf2bdf7c 100644 Binary files a/artifacts/lez/programs/cross_zone_outbox.bin and b/artifacts/lez/programs/cross_zone_outbox.bin differ diff --git a/artifacts/lez/programs/faucet.bin b/artifacts/lez/programs/faucet.bin index 340818c69..bc88b13e2 100644 Binary files a/artifacts/lez/programs/faucet.bin and b/artifacts/lez/programs/faucet.bin differ diff --git a/artifacts/lez/programs/pinata.bin b/artifacts/lez/programs/pinata.bin index 20d4a8954..f25d360c4 100644 Binary files a/artifacts/lez/programs/pinata.bin and b/artifacts/lez/programs/pinata.bin differ diff --git a/artifacts/lez/programs/pinata_token.bin b/artifacts/lez/programs/pinata_token.bin index 4220b0172..8452f3699 100644 Binary files a/artifacts/lez/programs/pinata_token.bin and b/artifacts/lez/programs/pinata_token.bin differ diff --git a/artifacts/lez/programs/ping_receiver.bin b/artifacts/lez/programs/ping_receiver.bin index 4cb85648a..bcf5c3b6e 100644 Binary files a/artifacts/lez/programs/ping_receiver.bin and b/artifacts/lez/programs/ping_receiver.bin differ diff --git a/artifacts/lez/programs/ping_sender.bin b/artifacts/lez/programs/ping_sender.bin index a2a158d3e..43248a8b2 100644 Binary files a/artifacts/lez/programs/ping_sender.bin and b/artifacts/lez/programs/ping_sender.bin differ diff --git a/artifacts/lez/programs/sequencer_stake.bin b/artifacts/lez/programs/sequencer_stake.bin index c66f7392a..c6054ff69 100644 Binary files a/artifacts/lez/programs/sequencer_stake.bin and b/artifacts/lez/programs/sequencer_stake.bin differ diff --git a/artifacts/lez/programs/token.bin b/artifacts/lez/programs/token.bin index b591a0158..ab464aa31 100644 Binary files a/artifacts/lez/programs/token.bin and b/artifacts/lez/programs/token.bin differ diff --git a/artifacts/lez/programs/vault.bin b/artifacts/lez/programs/vault.bin index cef8770c5..d2a396f12 100644 Binary files a/artifacts/lez/programs/vault.bin and b/artifacts/lez/programs/vault.bin differ diff --git a/artifacts/lez/programs/wrapped_token.bin b/artifacts/lez/programs/wrapped_token.bin index b38b56c33..9a3aca24c 100644 Binary files a/artifacts/lez/programs/wrapped_token.bin and b/artifacts/lez/programs/wrapped_token.bin differ diff --git a/examples/program_deployment/methods/guest/Cargo.toml b/examples/program_deployment/methods/guest/Cargo.toml index e57718538..b152d5311 100644 --- a/examples/program_deployment/methods/guest/Cargo.toml +++ b/examples/program_deployment/methods/guest/Cargo.toml @@ -9,6 +9,7 @@ workspace = true [dependencies] lee_core.workspace = true +program_loader_core.workspace = true hex.workspace = true bytemuck.workspace = true diff --git a/examples/program_deployment/methods/guest/src/bin/simple_tail_call.rs b/examples/program_deployment/methods/guest/src/bin/simple_tail_call.rs index f743f8cf5..6e553f3f5 100644 --- a/examples/program_deployment/methods/guest/src/bin/simple_tail_call.rs +++ b/examples/program_deployment/methods/guest/src/bin/simple_tail_call.rs @@ -48,7 +48,9 @@ fn main() { let chained_call_greeting: Vec = b"Hello from tail call".to_vec(); let chained_call_instruction_data = risc0_zkvm::serde::to_vec(&chained_call_greeting).unwrap(); let chained_call = ChainedCall { - program_account_id: hello_world_program_id().into(), + program_account_id: program_loader_core::immutable_deploy_account_id( + hello_world_program_id(), + ), instruction_data: chained_call_instruction_data, pre_states, pda_seeds: vec![], diff --git a/examples/program_deployment/methods/guest/src/bin/tail_call_with_pda.rs b/examples/program_deployment/methods/guest/src/bin/tail_call_with_pda.rs index 5ee2e410e..2e808c969 100644 --- a/examples/program_deployment/methods/guest/src/bin/tail_call_with_pda.rs +++ b/examples/program_deployment/methods/guest/src/bin/tail_call_with_pda.rs @@ -60,7 +60,9 @@ fn main() { this }; let chained_call = ChainedCall { - program_account_id: hello_world_program_id().into(), + program_account_id: program_loader_core::immutable_deploy_account_id( + hello_world_program_id(), + ), instruction_data: chained_call_instruction_data, pre_states: vec![pre_state_for_chained_call], pda_seeds: vec![PDA_SEED], diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 8229532a1..ed450d61c 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -47,7 +47,7 @@ async fn main() { let hello_world_bytecode: Vec = std::fs::read(hello_world_path).unwrap(); let hello_world = Program::new(hello_world_bytecode.into()).unwrap(); let dependencies: HashMap = - std::iter::once((hello_world.id().into(), hello_world)).collect(); + std::iter::once((hello_world.deployed_account_id(), hello_world)).collect(); let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies); let accounts = vec![AccountIdentity::PrivateOwned(account_id)]; diff --git a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs index 85b44f627..155229ae6 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs @@ -46,13 +46,14 @@ async fn main() { let program = Program::new(bytecode.into()).unwrap(); // Compute the PDA to pass it as input account to the public execution - let pda = AccountId::for_public_pda(&program.id(), &PDA_SEED); + let program_account_id = program.deployed_account_id(); + let pda = AccountId::for_public_pda(&program_account_id, &PDA_SEED); let account_ids = vec![pda]; let instruction_data = (); let nonces = vec![]; let signing_keys = []; let message = - Message::try_new(program.id().into(), account_ids, nonces, instruction_data).unwrap(); + Message::try_new(program_account_id, account_ids, nonces, instruction_data).unwrap(); let witness_set = WitnessSet::for_message(&message, &signing_keys); let tx = PublicTransaction::new(message, witness_set); diff --git a/integration_tests/src/utils.rs b/integration_tests/src/utils.rs index bbf9a7dba..af8ae3c6b 100644 --- a/integration_tests/src/utils.rs +++ b/integration_tests/src/utils.rs @@ -326,13 +326,16 @@ pub async fn wait_for_indexer_to_catch_up(ctx: &TestContext) -> anyhow::Result (AccountId, AccountId) { - let loader_id: lee_core::program::ProgramId = RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID.into(); let image_id: lee_core::program::ProgramId = risc0_binfmt::compute_image_id(bytecode).unwrap().into(); - let header = - program_loader_core::deploy_header_account_id(loader_id, image_id, 0, AccountId::default()); + let header = program_loader_core::deploy_header_account_id( + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, + image_id, + 0, + AccountId::default(), + ); let segment = program_loader_core::deploy_segment_account_id( - loader_id, + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, image_id, 0, AccountId::default(), @@ -351,9 +354,8 @@ pub fn deploy_transaction( segment: AccountId, bytecode: Vec, ) -> lee::PublicTransaction { - let loader_id: lee_core::program::ProgramId = RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID.into(); let message = lee::public_transaction::Message::try_new( - loader_id.into(), + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, vec![header, segment], vec![], program_loader_core::Instruction::Deploy { bytecode }, diff --git a/integration_tests/tests/auth_transfer/private.rs b/integration_tests/tests/auth_transfer/private.rs index 8fa281f69..a7c0b4433 100644 --- a/integration_tests/tests/auth_transfer/private.rs +++ b/integration_tests/tests/auth_transfer/private.rs @@ -593,8 +593,6 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { bytecode, )); - // `Deploy`'s bytecode payload runs ~4x its raw size on the wire (see `encoded_tx_size`'s - // docs), so the default 1 MiB block size isn't enough headroom for a real guest binary. let tx_size = encoded_tx_size(&deploy_tx); let ctx = MultiZoneTestContextBuilder::default() .with_zone( @@ -616,6 +614,7 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { let attacker_id = ctx.existing_public_accounts()[0]; let faucet_program_id = programs::faucet().id(); let vault_program_id = programs::vault().id(); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); let auth_transfer_program_id = programs::authenticated_transfer().id(); let ask = lee_core::AuthorizationSecretKey([3; 32]); let nsk = lee_core::NullifierSecretKey::from(&ask); @@ -623,7 +622,7 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap(); let attacker_vault_id = { let seed = vault_core::compute_vault_seed(attacker_id); - AccountId::for_private_pda(&vault_program_id, &seed, &npk, &vpk, 1337) + AccountId::for_private_pda(&vault_account_id, &seed, &npk, &vpk, 1337) }; let amount: u128 = 1; @@ -645,10 +644,7 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { program_loader_core::immutable_deploy_account_id(faucet_program_id), programs::faucet(), ), - ( - program_loader_core::immutable_deploy_account_id(vault_program_id), - programs::vault(), - ), + (vault_account_id, programs::vault()), ( program_loader_core::immutable_deploy_account_id(auth_transfer_program_id), programs::authenticated_transfer(), @@ -659,9 +655,8 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> { .with_program_account_id(faucet_chain_caller_header); let instruction = Program::serialize_instruction(( - faucet_program_id, program_loader_core::immutable_deploy_account_id(faucet_program_id), - program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, attacker_id, amount, ))?; diff --git a/integration_tests/tests/auth_transfer/public.rs b/integration_tests/tests/auth_transfer/public.rs index 8d5822662..922b57346 100644 --- a/integration_tests/tests/auth_transfer/public.rs +++ b/integration_tests/tests/auth_transfer/public.rs @@ -354,7 +354,8 @@ async fn cannot_execute_faucet_program() -> Result<()> { let recipient = ctx.existing_public_accounts()[0]; let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, recipient); let recipient_balance_before = account_balance(&ctx, recipient).await?; let faucet_balance_before = account_balance(&ctx, faucet_account_id).await?; @@ -365,8 +366,7 @@ async fn cannot_execute_faucet_program() -> Result<()> { vec![faucet_account_id, recipient_vault_id], vec![], faucet_core::Instruction::GenesisTransferVault { - self_program_id: programs::faucet().id(), - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id: recipient, amount, }, @@ -405,8 +405,6 @@ async fn user_tx_that_chain_calls_faucet_is_dropped() -> Result<()> { bytecode, )); - // `Deploy`'s bytecode payload runs ~4x its raw size on the wire (see `encoded_tx_size`'s - // docs), so the default 1 MiB block size isn't enough headroom for a real guest binary. let tx_size = encoded_tx_size(&deploy_tx); let ctx = MultiZoneTestContextBuilder::default() .with_zone( @@ -428,7 +426,8 @@ async fn user_tx_that_chain_calls_faucet_is_dropped() -> Result<()> { let attacker = ctx.existing_public_accounts()[0]; let faucet_program_id = programs::faucet().id(); let vault_program_id = programs::vault().id(); - let attacker_vault_id = vault_core::compute_vault_account_id(vault_program_id, attacker); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let attacker_vault_id = vault_core::compute_vault_account_id(vault_account_id, attacker); let amount: u128 = 1; let message = public_transaction::Message::try_new( @@ -436,9 +435,8 @@ async fn user_tx_that_chain_calls_faucet_is_dropped() -> Result<()> { vec![faucet_account_id, attacker_vault_id], vec![], ( - faucet_program_id, program_loader_core::immutable_deploy_account_id(faucet_program_id), - program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, attacker, amount, ), diff --git a/integration_tests/tests/block_size_limit.rs b/integration_tests/tests/block_size_limit.rs index 67e1d75aa..1efd63281 100644 --- a/integration_tests/tests/block_size_limit.rs +++ b/integration_tests/tests/block_size_limit.rs @@ -32,9 +32,6 @@ async fn reject_oversized_transaction() -> Result<()> { ZoneTestContextBuilder::new(MultiNodeTestContextConfig::default()) .with_sequencer_partial_config(SequencerPartialConfig { max_num_tx_in_block: 100, - // Below the transaction's actual size, so it's rejected outright (the - // sequencer additionally reserves ~200 bytes of block-header overhead off of - // this limit, so being equal to `tx_size` is already enough of a margin). max_block_size: ByteSize::b(tx_size), mempool_max_size: 1000, block_create_timeout: Duration::from_secs(10), @@ -66,6 +63,9 @@ async fn reject_oversized_transaction() -> Result<()> { #[test] async fn accept_transaction_within_limit() -> Result<()> { + // A real Deploy of a valid guest binary: the native Deploy dispatch path parses + // the bytecode as an actual RISC0 image, so an arbitrary small buffer no longer + // qualifies as "a small transaction" the way it did under the legacy path. let bytecode = test_programs::claimer().elf().to_vec(); let (header, segment) = deploy_targets(&bytecode); let tx = LeeTransaction::Public(deploy_transaction(header, segment, bytecode)); @@ -76,7 +76,8 @@ async fn accept_transaction_within_limit() -> Result<()> { ZoneTestContextBuilder::new(MultiNodeTestContextConfig::default()) .with_sequencer_partial_config(SequencerPartialConfig { max_num_tx_in_block: 100, - // Comfortably above the transaction's actual size. + // TOFIX: should be `ByteSize::mib(1)` again once the program-as-account + // migration is finished. max_block_size: ByteSize::b(tx_size + 10 * 1024), mempool_max_size: 1000, block_create_timeout: Duration::from_secs(10), diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index 3b8a6343c..0d3a1b083 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -26,8 +26,12 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { let recipient_id = ctx.existing_public_accounts()[0]; let bridge_account_id = system_accounts::bridge_account_id(); let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); - let receipt_id = bridge_core::deposit_receipt_account_id(programs::bridge().id(), [0_u8; 32]); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, recipient_id); + let receipt_id = bridge_core::deposit_receipt_account_id( + programs::bridge().deployed_account_id(), + [0_u8; 32], + ); let message = public_transaction::Message::try_new( program_loader_core::immutable_deploy_account_id(programs::bridge().id()), @@ -35,9 +39,7 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { vec![], bridge_core::Instruction::Deposit { l1_deposit_op_id: [0_u8; 32], - self_program_id: programs::bridge().id(), - vault_program_id, - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id, amount: 1, }, @@ -77,8 +79,12 @@ async fn public_bridge_deposit_with_zero_amount_is_rejected() -> anyhow::Result< let recipient_id = ctx.existing_public_accounts()[0]; let bridge_account_id = system_accounts::bridge_account_id(); let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); - let receipt_id = bridge_core::deposit_receipt_account_id(programs::bridge().id(), [0_u8; 32]); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, recipient_id); + let receipt_id = bridge_core::deposit_receipt_account_id( + programs::bridge().deployed_account_id(), + [0_u8; 32], + ); let message = public_transaction::Message::try_new( program_loader_core::immutable_deploy_account_id(programs::bridge().id()), @@ -86,9 +92,7 @@ async fn public_bridge_deposit_with_zero_amount_is_rejected() -> anyhow::Result< vec![], bridge_core::Instruction::Deposit { l1_deposit_op_id: [0_u8; 32], - self_program_id: programs::bridge().id(), - vault_program_id, - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id, amount: 0, }, @@ -140,8 +144,12 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { let recipient_id = ctx.existing_public_accounts()[0]; let bridge_account_id = system_accounts::bridge_account_id(); let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id); - let receipt_id = bridge_core::deposit_receipt_account_id(programs::bridge().id(), [0_u8; 32]); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, recipient_id); + let receipt_id = bridge_core::deposit_receipt_account_id( + programs::bridge().deployed_account_id(), + [0_u8; 32], + ); // Get pre-state of bridge and vault accounts; the receipt is unminted (a // default account), so the program would create it on a first mint. @@ -183,9 +191,7 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> { // Serialize the bridge deposit instruction let instruction = Program::serialize_instruction(bridge_core::Instruction::Deposit { l1_deposit_op_id: [0_u8; 32], - self_program_id: programs::bridge().id(), - vault_program_id, - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id, amount: 1, }) diff --git a/integration_tests/tests/cross_zone_bridge.rs b/integration_tests/tests/cross_zone_bridge.rs index 4a45e85f0..414289a9d 100644 --- a/integration_tests/tests/cross_zone_bridge.rs +++ b/integration_tests/tests/cross_zone_bridge.rs @@ -110,7 +110,10 @@ async fn lock_on_zone_a_mints_wrapped_token_on_zone_b() -> Result<()> { .context("Failed to submit lock on zone A")?; // Wait until zone B's indexer reflects the verified mint. - let holding_id = wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT); + let holding_id = wrapped_token_core::holding_account_id( + programs::wrapped_token().deployed_account_id(), + &RECIPIENT, + ); let minted = wait_for_mint(ind_client_b, holding_id).await?; assert_eq!( @@ -121,7 +124,8 @@ async fn lock_on_zone_a_mints_wrapped_token_on_zone_b() -> Result<()> { // Conservation: the mint on B must be backed by an equal lock on A. The lock // has already landed (it preceded delivery), so zone A reflects the debit and // escrow now. - let escrow_id = bridge_lock_core::escrow_account_id(programs::bridge_lock().id()); + let escrow_id = + bridge_lock_core::escrow_account_id(programs::bridge_lock().deployed_account_id()); let escrowed = seq_client_a.get_account(escrow_id).await?.balance; assert_eq!( escrowed, LOCK_AMOUNT, @@ -144,12 +148,13 @@ fn build_lock_tx( target_zone: [u8; 32], ) -> LeeTransaction { let bridge_lock_id = programs::bridge_lock().id(); + let bridge_lock_account_id = programs::bridge_lock().deployed_account_id(); let wrapped_token_id = programs::wrapped_token().id(); - let outbox_id = programs::cross_zone_outbox().id(); + let wrapped_token_account_id = programs::wrapped_token().deployed_account_id(); + let outbox_id = programs::cross_zone_outbox().deployed_account_id(); let ordinal = 0; let mint = wrapped_token_core::Instruction::Mint { - self_program_id: wrapped_token_id, recipient: RECIPIENT, amount: LOCK_AMOUNT, }; @@ -157,11 +162,10 @@ fn build_lock_tx( let payload: Vec = words.iter().flat_map(|word| word.to_le_bytes()).collect(); let target_accounts = vec![ - wrapped_token_core::config_account_id(wrapped_token_id).into_value(), - wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT).into_value(), + wrapped_token_core::config_account_id(wrapped_token_account_id).into_value(), + wrapped_token_core::holding_account_id(wrapped_token_account_id, &RECIPIENT).into_value(), ]; let lock = bridge_lock_core::Instruction::Lock { - self_program_id: bridge_lock_id, amount: LOCK_AMOUNT, target_zone, target_program_id: wrapped_token_id, @@ -171,9 +175,9 @@ fn build_lock_tx( }; let accounts = vec![ - bridge_lock_core::config_account_id(bridge_lock_id), + bridge_lock_core::config_account_id(bridge_lock_account_id), holder_id, - bridge_lock_core::escrow_account_id(bridge_lock_id), + bridge_lock_core::escrow_account_id(bridge_lock_account_id), outbox_pda( outbox_id, program_loader_core::immutable_deploy_account_id(bridge_lock_id), diff --git a/integration_tests/tests/cross_zone_ingress_guard.rs b/integration_tests/tests/cross_zone_ingress_guard.rs index b16d7a4b4..6a27b1690 100644 --- a/integration_tests/tests/cross_zone_ingress_guard.rs +++ b/integration_tests/tests/cross_zone_ingress_guard.rs @@ -57,15 +57,13 @@ async fn user_origin_inbox_call_rejected() -> Result<()> { payload: vec![], l1_inclusion_witness: None, }; - let seen_id = inbox_seen_shard_account_id(inbox_id, &msg.src_zone, msg.src_block_id); + let inbox_account_id = program_loader_core::immutable_deploy_account_id(inbox_id); + let seen_id = inbox_seen_shard_account_id(inbox_account_id, &msg.src_zone, msg.src_block_id); let message = Message::try_new( - program_loader_core::immutable_deploy_account_id(inbox_id), - vec![inbox_config_account_id(inbox_id), seen_id], + inbox_account_id, + vec![inbox_config_account_id(inbox_account_id), seen_id], vec![], - Instruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + Instruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = LeeTransaction::Public(PublicTransaction::new( diff --git a/integration_tests/tests/cross_zone_ping.rs b/integration_tests/tests/cross_zone_ping.rs index d9bb632d4..56d132e8d 100644 --- a/integration_tests/tests/cross_zone_ping.rs +++ b/integration_tests/tests/cross_zone_ping.rs @@ -101,7 +101,7 @@ async fn ping_crosses_from_zone_a_to_zone_b() -> Result<()> { .context("Failed to submit ping on zone A")?; // Wait until zone B's sequencer records the delivered payload. - let record_id = ping_record_pda(receiver_id); + let record_id = ping_record_pda(programs::ping_receiver().deployed_account_id()); let delivered = wait_for_delivery(seq_client_b.clone(), record_id).await?; assert_eq!( @@ -114,26 +114,26 @@ async fn ping_crosses_from_zone_a_to_zone_b() -> Result<()> { /// Builds a top-level `ping_sender` transaction that chains into the outbox to emit /// a message carrying a `ping_receiver::Record` instruction for the target zone. fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransaction { - let outbox_id = programs::cross_zone_outbox().id(); + let receiver_account_id = program_loader_core::immutable_deploy_account_id(receiver_id); + let outbox_id = programs::cross_zone_outbox().deployed_account_id(); let ordinal = 0; // The payload is the ping_receiver instruction, serialized as risc0 words in // little-endian bytes (the contract the inbox reverses when forwarding). let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: PING_PAYLOAD.to_vec(), }) .expect("serialize ping instruction"); let payload: Vec = words.iter().flat_map(|word| word.to_le_bytes()).collect(); let sender_id = programs::ping_sender().id(); + let sender_account_id = programs::ping_sender().deployed_account_id(); let send = SenderInstruction::Send { - self_program_id: sender_id, target_zone, target_program_id: receiver_id, target_accounts: vec![ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload, ordinal, @@ -147,7 +147,7 @@ fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransactio ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(sender_id), - vec![sender_config_account_id(sender_id), outbox_account], + vec![sender_config_account_id(sender_account_id), outbox_account], vec![], send, ) diff --git a/integration_tests/tests/cross_zone_state_machine.rs b/integration_tests/tests/cross_zone_state_machine.rs index 4dd6f94be..8ff973f09 100644 --- a/integration_tests/tests/cross_zone_state_machine.rs +++ b/integration_tests/tests/cross_zone_state_machine.rs @@ -75,7 +75,7 @@ fn seed_inbox_config(state: &mut V03State, self_zone: [u8; 32]) { let inbox_id = programs::cross_zone_inbox().id(); let config = InboxConfig { self_zone }; *state = std::mem::replace(state, V03State::new()).with_public_accounts([( - inbox_config_account_id(inbox_id), + inbox_config_account_id(program_loader_core::immutable_deploy_account_id(inbox_id)), Account { program_owner: program_loader_core::immutable_deploy_account_id(inbox_id), balance: 0, @@ -108,12 +108,14 @@ fn seed_wrapped_config_with_governance( let wrapped_token_id = programs::wrapped_token().id(); let config = wrapped_token_core::WrappedTokenConfig { minter: program_loader_core::immutable_deploy_account_id(programs::cross_zone_inbox().id()), - governance, + governance: governance.map(program_loader_core::immutable_deploy_account_id), authority, sources, }; *state = std::mem::replace(state, V03State::new()).with_public_accounts([( - wrapped_token_core::config_account_id(wrapped_token_id), + wrapped_token_core::config_account_id(program_loader_core::immutable_deploy_account_id( + wrapped_token_id, + )), Account { program_owner: program_loader_core::immutable_deploy_account_id(wrapped_token_id), data: config @@ -147,12 +149,14 @@ fn seed_receiver_config_with_governance( deliverer: program_loader_core::immutable_deploy_account_id( programs::cross_zone_inbox().id(), ), - governance, + governance: governance.map(program_loader_core::immutable_deploy_account_id), authority, sources, }; *state = std::mem::replace(state, V03State::new()).with_public_accounts([( - receiver_config_account_id(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), Account { program_owner: program_loader_core::immutable_deploy_account_id(receiver_id), data: config @@ -170,16 +174,13 @@ fn seed_ping_sender_config(state: &mut V03State) { let sender_id = programs::ping_sender().id(); let outbox_id = programs::cross_zone_outbox().id(); *state = std::mem::replace(state, V03State::new()).with_public_accounts([( - sender_config_account_id(sender_id), + sender_config_account_id(program_loader_core::immutable_deploy_account_id(sender_id)), Account { program_owner: program_loader_core::immutable_deploy_account_id(sender_id), - data: outbox_bytes( - program_loader_core::immutable_deploy_account_id(outbox_id), - outbox_id, - ) - .to_vec() - .try_into() - .expect("outbox id fits in account data"), + data: outbox_bytes(program_loader_core::immutable_deploy_account_id(outbox_id)) + .to_vec() + .try_into() + .expect("outbox id fits in account data"), ..Default::default() }, )]); @@ -190,14 +191,15 @@ fn seed_ping_sender_config(state: &mut V03State) { fn seed_bridge_lock_config(state: &mut V03State) { let bridge_lock_id = programs::bridge_lock().id(); *state = std::mem::replace(state, V03State::new()).with_public_accounts([( - bridge_lock_core::config_account_id(bridge_lock_id), + bridge_lock_core::config_account_id(program_loader_core::immutable_deploy_account_id( + bridge_lock_id, + )), Account { program_owner: program_loader_core::immutable_deploy_account_id(bridge_lock_id), data: bridge_lock_core::config_bytes( program_loader_core::immutable_deploy_account_id( programs::cross_zone_outbox().id(), ), - programs::cross_zone_outbox().id(), programs::wrapped_token().id(), ) .to_vec() @@ -216,8 +218,12 @@ fn dispatch_accounts( targets: Vec, ) -> Vec { let mut ids = vec![ - inbox_config_account_id(inbox_id), - inbox_seen_shard_account_id(inbox_id, &msg.src_zone, msg.src_block_id), + inbox_config_account_id(program_loader_core::immutable_deploy_account_id(inbox_id)), + inbox_seen_shard_account_id( + program_loader_core::immutable_deploy_account_id(inbox_id), + &msg.src_zone, + msg.src_block_id, + ), inbox_source_marker_account_id( program_loader_core::immutable_deploy_account_id(inbox_id), &msg.src_zone, @@ -249,7 +255,12 @@ fn signed_tx( words: Vec, key: &PrivateKey, ) -> PublicTransaction { - let message = Message::new_preserialized(program.into(), accounts, vec![nonce.into()], words); + let message = Message::new_preserialized( + program_loader_core::immutable_deploy_account_id(program), + accounts, + vec![nonce.into()], + words, + ); let witness = WitnessSet::for_message(&message, &[key]); PublicTransaction::new(message, witness) } @@ -265,7 +276,7 @@ fn via_proxy( words: Vec, ) -> PublicTransaction { let message = Message::try_new( - proxy_id.into(), + program_loader_core::immutable_deploy_account_id(proxy_id), vec![config, authority], vec![], (target, words, delegated), @@ -297,13 +308,10 @@ fn chained_via_inbox( l1_inclusion_witness: None, }; let message = Message::try_new( - inbox_id.into(), + program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts(inbox_id, &msg, vec![config_id, authority]), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])) @@ -314,18 +322,22 @@ fn chained_via_inbox( fn send_tx(accounts: Vec, target_zone: [u8; 32], ordinal: u32) -> PublicTransaction { let receiver_id = programs::ping_receiver().id(); let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: b"ping".to_vec(), }) .expect("serialize ping instruction"); let sender_id = programs::ping_sender().id(); let send = ping_core::SenderInstruction::Send { - self_program_id: sender_id, target_zone, target_program_id: receiver_id, target_accounts: vec![ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )) + .into_value(), + ping_record_pda(program_loader_core::immutable_deploy_account_id( + receiver_id, + )) + .into_value(), ], payload: words.iter().flat_map(|word| word.to_le_bytes()).collect(), ordinal, @@ -348,7 +360,6 @@ fn mint_payload() -> Vec { fn mint_payload_of(amount: u128) -> Vec { let mint = wrapped_token_core::Instruction::Mint { - self_program_id: programs::wrapped_token().id(), recipient: RECIPIENT, amount, }; @@ -386,15 +397,17 @@ fn dispatch_mint(amount: u128) -> Result Vec<[u8; 32]> { vec![ - wrapped_token_core::config_account_id(wrapped_token_id).into_value(), - wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT).into_value(), + wrapped_token_core::config_account_id(program_loader_core::immutable_deploy_account_id( + wrapped_token_id, + )) + .into_value(), + wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ) + .into_value(), ] } @@ -603,7 +630,6 @@ fn lock_tx_to( let outbox_id = programs::cross_zone_outbox().id(); let lock = bridge_lock_core::Instruction::Lock { - self_program_id: bridge_lock_id, amount: LOCK_AMOUNT, target_zone: zone_b, target_program_id, @@ -614,11 +640,15 @@ fn lock_tx_to( let message = Message::try_new( program_loader_core::immutable_deploy_account_id(bridge_lock_id), vec![ - bridge_lock_core::config_account_id(bridge_lock_id), + bridge_lock_core::config_account_id(program_loader_core::immutable_deploy_account_id( + bridge_lock_id, + )), holder_id, - bridge_lock_core::escrow_account_id(bridge_lock_id), + bridge_lock_core::escrow_account_id(program_loader_core::immutable_deploy_account_id( + bridge_lock_id, + )), outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(bridge_lock_id), &zone_b, ordinal, @@ -705,13 +735,13 @@ fn two_emitters_share_an_ordinal_without_colliding() { seed_bridge_lock_config(&mut state); let lock_slot = outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(bridge_lock_id), &zone_b, ordinal, ); let send_slot = outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(sender_id), &zone_b, ordinal, @@ -727,7 +757,10 @@ fn two_emitters_share_an_ordinal_without_colliding() { state.apply_state_diff(diff); let send = send_tx( - vec![sender_config_account_id(sender_id), send_slot], + vec![ + sender_config_account_id(program_loader_core::immutable_deploy_account_id(sender_id)), + send_slot, + ], zone_b, ordinal, ); @@ -772,13 +805,16 @@ fn a_send_into_a_foreign_outbox_slot_is_rejected() { // A slot under some other program, which is what the caller would have to // pass to reach it. let foreign_slot = outbox_pda( - [3; 8], + program_loader_core::immutable_deploy_account_id([3; 8]), program_loader_core::immutable_deploy_account_id(sender_id), &zone_b, ordinal, ); let send = send_tx( - vec![sender_config_account_id(sender_id), foreign_slot], + vec![ + sender_config_account_id(program_loader_core::immutable_deploy_account_id(sender_id)), + foreign_slot, + ], zone_b, ordinal, ); @@ -862,8 +898,11 @@ fn a_lock_naming_other_mint_accounts_is_rejected() { // A holding under someone other than the payload's recipient: a mint the // destination would credit to the wrong account if it credited it at all. - let other_holding = - wrapped_token_core::holding_account_id(wrapped_token_id, &[4; 32]).into_value(); + let other_holding = wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &[4; 32], + ) + .into_value(); let lock = lock_tx_to( &holder_key, holder_id, @@ -872,7 +911,10 @@ fn a_lock_naming_other_mint_accounts_is_rejected() { 0, wrapped_token_id, vec![ - wrapped_token_core::config_account_id(wrapped_token_id).into_value(), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ) + .into_value(), other_holding, ], ); @@ -921,7 +963,7 @@ fn a_lock_with_a_substituted_config_account_is_rejected() { decoy_id, Account { program_owner: program_loader_core::immutable_deploy_account_id(bridge_lock_id), - data: bridge_lock_core::config_bytes(AccountId::new([3; 32]), [3; 8], [4; 8]) + data: bridge_lock_core::config_bytes(AccountId::new([3; 32]), [4; 8]) .to_vec() .try_into() .expect("pinned ids fit in account data"), @@ -932,7 +974,6 @@ fn a_lock_with_a_substituted_config_account_is_rejected() { seed_bridge_lock_config(&mut state); let lock = bridge_lock_core::Instruction::Lock { - self_program_id: bridge_lock_id, amount: LOCK_AMOUNT, target_zone: zone_b, target_program_id: wrapped_token_id, @@ -945,9 +986,11 @@ fn a_lock_with_a_substituted_config_account_is_rejected() { vec![ decoy_id, holder_id, - bridge_lock_core::escrow_account_id(bridge_lock_id), + bridge_lock_core::escrow_account_id(program_loader_core::immutable_deploy_account_id( + bridge_lock_id, + )), outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(bridge_lock_id), &zone_b, ordinal, @@ -1004,7 +1047,9 @@ fn a_lock_before_the_pins_are_set_is_rejected() { #[test] fn the_bridge_pins_are_written_once_and_replayable() { let bridge_lock_id = programs::bridge_lock().id(); - let config_id = bridge_lock_core::config_account_id(bridge_lock_id); + let config_id = bridge_lock_core::config_account_id( + program_loader_core::immutable_deploy_account_id(bridge_lock_id), + ); let outbox_id = programs::cross_zone_outbox().id(); let wrapped_token_id = programs::wrapped_token().id(); @@ -1014,9 +1059,7 @@ fn the_bridge_pins_are_written_once_and_replayable() { vec![config_id], vec![], bridge_lock_core::Instruction::InitConfig { - self_program_id: bridge_lock_id, outbox_account_id: program_loader_core::immutable_deploy_account_id(outbox), - outbox_program_id: outbox, target_program_id: target, }, ) @@ -1038,7 +1081,6 @@ fn the_bridge_pins_are_written_once_and_replayable() { bridge_lock_core::read_config(&state.get_account_by_id(config_id).data.into_inner()), Some(( program_loader_core::immutable_deploy_account_id(outbox_id), - outbox_id, wrapped_token_id )), "the config pins both programs after genesis" @@ -1076,13 +1118,16 @@ fn a_send_before_the_pin_is_set_is_rejected() { let state = base_state(); let slot = outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(sender_id), &zone_b, ordinal, ); let send = send_tx( - vec![sender_config_account_id(sender_id), slot], + vec![ + sender_config_account_id(program_loader_core::immutable_deploy_account_id(sender_id)), + slot, + ], zone_b, ordinal, ); @@ -1091,7 +1136,7 @@ fn a_send_before_the_pin_is_set_is_rejected() { panic!("a send with no outbox pinned must not execute"); }; assert!( - format!("{err:?}").contains("config account holds an outbox dispatch address and image id"), + format!("{err:?}").contains("config account holds an outbox dispatch address"), "rejected for the wrong reason: {err:?}" ); } @@ -1109,12 +1154,19 @@ fn a_send_with_a_substituted_config_account_is_rejected() { seed_ping_sender_config(&mut state); let slot = outbox_pda( - outbox_id, + program_loader_core::immutable_deploy_account_id(outbox_id), program_loader_core::immutable_deploy_account_id(sender_id), &zone_b, ordinal, ); - let send = send_tx(vec![ping_record_pda(sender_id), slot], zone_b, ordinal); + let send = send_tx( + vec![ + ping_record_pda(program_loader_core::immutable_deploy_account_id(sender_id)), + slot, + ], + zone_b, + ordinal, + ); let Err(err) = ValidatedStateDiff::from_public_transaction(&send, &state, 1, 0) else { panic!("a send over a substituted config account must not execute"); @@ -1131,7 +1183,8 @@ fn a_send_with_a_substituted_config_account_is_rejected() { #[test] fn the_outbox_pin_is_written_once_and_replayable() { let sender_id = programs::ping_sender().id(); - let config_id = sender_config_account_id(sender_id); + let config_id = + sender_config_account_id(program_loader_core::immutable_deploy_account_id(sender_id)); // Unsigned and nonce-free, as genesis builds it: the config PDA has no signer. let init = |outbox: lee_core::program::ProgramId| { @@ -1140,9 +1193,7 @@ fn the_outbox_pin_is_written_once_and_replayable() { vec![config_id], vec![], ping_core::SenderInstruction::InitConfig { - self_program_id: sender_id, outbox_account_id: program_loader_core::immutable_deploy_account_id(outbox), - outbox_program_id: outbox, }, ) .expect("build InitConfig message"); @@ -1158,10 +1209,7 @@ fn the_outbox_pin_is_written_once_and_replayable() { state.apply_state_diff(diff); assert_eq!( read_outbox(&state.get_account_by_id(config_id).data.into_inner()), - Some(( - program_loader_core::immutable_deploy_account_id(outbox_id), - outbox_id - )), + Some(program_loader_core::immutable_deploy_account_id(outbox_id)), "the config pins the outbox after genesis" ); @@ -1183,7 +1231,9 @@ fn the_outbox_pin_is_written_once_and_replayable() { #[test] fn the_token_authority_path_holds() { let wrapped_token_id = programs::wrapped_token().id(); - let config_id = wrapped_token_core::config_account_id(wrapped_token_id); + let config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); let src_zone = [2_u8; 32]; let key = PrivateKey::try_new([7; 32]).expect("valid key"); @@ -1208,7 +1258,7 @@ fn the_token_authority_path_holds() { wrapped_token_id, vec![config_id, account], nonce, - words_of!(&wrapped_token_core::Instruction::RenounceAuthority), + words_of!(&wrapped_token_core::Instruction::RenounceAuthority {}), signer, ) }; @@ -1264,7 +1314,12 @@ fn the_token_authority_path_holds() { let substituted = |words: Vec| { signed_tx( wrapped_token_id, - vec![ping_record_pda(wrapped_token_id), authority], + vec![ + ping_record_pda(program_loader_core::immutable_deploy_account_id( + wrapped_token_id, + )), + authority, + ], 0, words, &key, @@ -1281,7 +1336,7 @@ fn the_token_authority_path_holds() { rejects_at( &state, &substituted(words_of!( - &wrapped_token_core::Instruction::RenounceAuthority + &wrapped_token_core::Instruction::RenounceAuthority {} )), 1, "must be the wrapped-token config PDA", @@ -1308,7 +1363,7 @@ fn the_token_authority_path_holds() { ); assert_eq!( state.get_account_by_id(authority).program_owner, - wrapped_token_id.into(), + program_loader_core::immutable_deploy_account_id(wrapped_token_id), "the first use claims the authority account for the target" ); @@ -1394,7 +1449,6 @@ fn a_delivery_from_an_unauthorized_source_does_not_reach_ping_receiver() { ); let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: b"ping".to_vec(), }) .expect("serialize ping instruction"); @@ -1412,20 +1466,21 @@ fn a_delivery_from_an_unauthorized_source_does_not_reach_ping_receiver() { l1_inclusion_witness: None, }; let message = Message::try_new( - inbox_id.into(), + program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts( inbox_id, &msg, vec![ - receiver_config_account_id(receiver_id), - ping_record_pda(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + ping_record_pda(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), ], ), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -1462,7 +1517,6 @@ fn the_inbox_refuses_a_marker_that_does_not_match_the_message() { ); let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: b"ping".to_vec(), }) .expect("serialize ping instruction"); @@ -1481,23 +1535,28 @@ fn the_inbox_refuses_a_marker_that_does_not_match_the_message() { // The message says ping_sender; the marker names bridge_lock, which the // receiver also would not accept. The inbox must refuse it first. let message = Message::try_new( - inbox_id.into(), + program_loader_core::immutable_deploy_account_id(inbox_id), vec![ - inbox_config_account_id(inbox_id), - inbox_seen_shard_account_id(inbox_id, &msg.src_zone, msg.src_block_id), + inbox_config_account_id(program_loader_core::immutable_deploy_account_id(inbox_id)), + inbox_seen_shard_account_id( + program_loader_core::immutable_deploy_account_id(inbox_id), + &msg.src_zone, + msg.src_block_id, + ), inbox_source_marker_account_id( program_loader_core::immutable_deploy_account_id(inbox_id), &src_zone, program_loader_core::immutable_deploy_account_id(programs::bridge_lock().id()), ), - receiver_config_account_id(receiver_id), - ping_record_pda(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + ping_record_pda(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), ], vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -1516,7 +1575,9 @@ fn the_inbox_refuses_a_marker_that_does_not_match_the_message() { #[test] fn the_receiver_authority_path_holds() { let receiver_id = programs::ping_receiver().id(); - let config_id = receiver_config_account_id(receiver_id); + let config_id = receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )); let src_zone = [2_u8; 32]; let sender_id = programs::ping_sender().id(); @@ -1541,7 +1602,7 @@ fn the_receiver_authority_path_holds() { receiver_id, vec![config_id, account], nonce, - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), signer, ) }; @@ -1622,7 +1683,9 @@ fn the_receiver_authority_path_holds() { fn the_inbox_cannot_reach_the_authority_instructions() { let wrapped_token_id = programs::wrapped_token().id(); let inbox_id = programs::cross_zone_inbox().id(); - let config_id = wrapped_token_core::config_account_id(wrapped_token_id); + let config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); let self_zone = [1_u8; 32]; let src_zone = [2_u8; 32]; @@ -1669,11 +1732,16 @@ fn the_inbox_cannot_reach_the_authority_instructions() { fn the_governance_path_holds() { let wrapped_token_id = programs::wrapped_token().id(); let proxy_id = test_programs::authority_proxy().id(); - let config_id = wrapped_token_core::config_account_id(wrapped_token_id); + let config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); let src_zone = [2_u8; 32]; let seed = lee_core::program::PdaSeed::new([3; 32]); - let authority = AccountId::for_public_pda(&proxy_id, &seed); + let authority = AccountId::for_public_pda( + &program_loader_core::immutable_deploy_account_id(proxy_id), + &seed, + ); let mut state = base_state().with_programs([test_programs::authority_proxy()]); seed_wrapped_config_with_governance(&mut state, Some(proxy_id), Some(authority), vec![]); @@ -1695,7 +1763,7 @@ fn the_governance_path_holds() { config_id, authority, Some(seed), - words_of!(&wrapped_token_core::Instruction::RenounceAuthority), + words_of!(&wrapped_token_core::Instruction::RenounceAuthority {}), ) }; @@ -1718,7 +1786,7 @@ fn the_governance_path_holds() { ); assert_eq!( state.get_account_by_id(authority).program_owner, - wrapped_token_id.into(), + program_loader_core::immutable_deploy_account_id(wrapped_token_id), "the first use claims the delegated PDA for the target" ); @@ -1763,11 +1831,16 @@ fn the_governance_path_guards_hold() { let wrapped_token_id = programs::wrapped_token().id(); let receiver_id = programs::ping_receiver().id(); let proxy_id = test_programs::authority_proxy().id(); - let config_id = wrapped_token_core::config_account_id(wrapped_token_id); + let config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); let src_zone = [2_u8; 32]; let seed = lee_core::program::PdaSeed::new([3; 32]); - let authority = AccountId::for_public_pda(&proxy_id, &seed); + let authority = AccountId::for_public_pda( + &program_loader_core::immutable_deploy_account_id(proxy_id), + &seed, + ); let call = |delegated: Option| { via_proxy( @@ -1815,19 +1888,23 @@ fn the_governance_path_guards_hold() { ( wrapped_token_id, config_id, - words_of!(&wrapped_token_core::Instruction::RenounceAuthority), + words_of!(&wrapped_token_core::Instruction::RenounceAuthority {}), ), ( receiver_id, - receiver_config_account_id(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), words_of!(&ping_core::ReceiverInstruction::UpdateSources { sources: vec![(src_zone, programs::ping_sender().id())], }), ), ( receiver_id, - receiver_config_account_id(receiver_id), - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), ), ] { rejects_at( @@ -1855,11 +1932,16 @@ fn the_governance_path_guards_hold() { fn the_receiver_governance_path_holds() { let receiver_id = programs::ping_receiver().id(); let proxy_id = test_programs::authority_proxy().id(); - let config_id = receiver_config_account_id(receiver_id); + let config_id = receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )); let src_zone = [2_u8; 32]; let seed = lee_core::program::PdaSeed::new([3; 32]); - let authority = AccountId::for_public_pda(&proxy_id, &seed); + let authority = AccountId::for_public_pda( + &program_loader_core::immutable_deploy_account_id(proxy_id), + &seed, + ); let mut state = base_state().with_programs([test_programs::authority_proxy()]); seed_receiver_config_with_governance(&mut state, Some(proxy_id), Some(authority), vec![]); @@ -1888,7 +1970,7 @@ fn the_receiver_governance_path_holds() { ); assert_eq!( state.get_account_by_id(authority).program_owner, - receiver_id.into(), + program_loader_core::immutable_deploy_account_id(receiver_id), "the first use claims the delegated PDA for the receiver" ); } @@ -1901,12 +1983,19 @@ fn a_shared_authority_survives_the_first_claim() { let wrapped_token_id = programs::wrapped_token().id(); let receiver_id = programs::ping_receiver().id(); let proxy_id = test_programs::authority_proxy().id(); - let token_config_id = wrapped_token_core::config_account_id(wrapped_token_id); - let receiver_config_id = receiver_config_account_id(receiver_id); + let token_config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); + let receiver_config_id = receiver_config_account_id( + program_loader_core::immutable_deploy_account_id(receiver_id), + ); let src_zone = [2_u8; 32]; let seed = lee_core::program::PdaSeed::new([3; 32]); - let authority = AccountId::for_public_pda(&proxy_id, &seed); + let authority = AccountId::for_public_pda( + &program_loader_core::immutable_deploy_account_id(proxy_id), + &seed, + ); let mut state = base_state().with_programs([test_programs::authority_proxy()]); seed_wrapped_config_with_governance(&mut state, Some(proxy_id), Some(authority), vec![]); @@ -1927,7 +2016,7 @@ fn a_shared_authority_survives_the_first_claim() { state.apply_state_diff(first); assert_eq!( state.get_account_by_id(authority).program_owner, - wrapped_token_id.into(), + program_loader_core::immutable_deploy_account_id(wrapped_token_id), "the first target to be used owns the account" ); @@ -1957,7 +2046,7 @@ fn a_shared_authority_survives_the_first_claim() { ); assert_eq!( state.get_account_by_id(authority).program_owner, - wrapped_token_id.into(), + program_loader_core::immutable_deploy_account_id(wrapped_token_id), "the receiver never takes the account over" ); @@ -1967,7 +2056,7 @@ fn a_shared_authority_survives_the_first_claim() { receiver_config_id, authority, Some(seed), - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), ); let third = ValidatedStateDiff::from_public_transaction(&receiver_renounce, &state, 3, 0) .expect("the other target renounces on the token-owned authority"); @@ -2017,27 +2106,35 @@ fn an_authority_account_with_history_is_refused() { for (program, config_id, words) in [ ( wrapped_token_id, - wrapped_token_core::config_account_id(wrapped_token_id), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ), words_of!(&wrapped_token_core::Instruction::UpdateSources { sources: vec![(src_zone, programs::bridge_lock().id())], }), ), ( wrapped_token_id, - wrapped_token_core::config_account_id(wrapped_token_id), - words_of!(&wrapped_token_core::Instruction::RenounceAuthority), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ), + words_of!(&wrapped_token_core::Instruction::RenounceAuthority {}), ), ( receiver_id, - receiver_config_account_id(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), words_of!(&ping_core::ReceiverInstruction::UpdateSources { sources: vec![(src_zone, programs::ping_sender().id())], }), ), ( receiver_id, - receiver_config_account_id(receiver_id), - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), ), ] { rejects_at( @@ -2072,13 +2169,18 @@ fn the_remaining_authority_guards_hold() { words_of!(&ping_core::ReceiverInstruction::UpdateSources { sources: vec![(src_zone, programs::ping_sender().id())], }), - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), ] { rejects_at( &state, &signed_tx( receiver_id, - vec![ping_record_pda(receiver_id), authority], + vec![ + ping_record_pda(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + authority, + ], 0, words, &key, @@ -2095,19 +2197,25 @@ fn the_remaining_authority_guards_hold() { for (target, config_id, words, expected) in [ ( wrapped_token_id, - wrapped_token_core::config_account_id(wrapped_token_id), - words_of!(&wrapped_token_core::Instruction::RenounceAuthority), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ), + words_of!(&wrapped_token_core::Instruction::RenounceAuthority {}), "must be the wrapped-token config PDA", ), ( receiver_id, - receiver_config_account_id(receiver_id), - words_of!(&ping_core::ReceiverInstruction::RenounceAuthority), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + words_of!(&ping_core::ReceiverInstruction::RenounceAuthority {}), "must be the receiver config PDA", ), ( receiver_id, - receiver_config_account_id(receiver_id), + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), words_of!(&ping_core::ReceiverInstruction::UpdateSources { sources: vec![(src_zone, programs::ping_sender().id())], }), @@ -2156,15 +2264,17 @@ fn a_mint_is_refused_when_the_token_authorizes_no_source() { inbox_id, &msg, vec![ - wrapped_token_core::config_account_id(wrapped_token_id), - wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ), + wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ), ], ), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -2201,12 +2311,16 @@ fn a_top_level_mint_is_refused() { program_loader_core::immutable_deploy_account_id(wrapped_token_id), vec![ marker_id, - wrapped_token_core::config_account_id(wrapped_token_id), - wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT), + wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ), + wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ), ], vec![], wrapped_token_core::Instruction::Mint { - self_program_id: wrapped_token_id, recipient: RECIPIENT, amount: LOCK_AMOUNT, }, @@ -2228,8 +2342,10 @@ fn a_top_level_mint_is_refused() { #[test] fn inbox_dispatch_mints_wrapped_token() { let diff = dispatch_mint(LOCK_AMOUNT).expect("dispatch must validate and execute"); - let holding_id = - wrapped_token_core::holding_account_id(programs::wrapped_token().id(), &RECIPIENT); + let holding_id = wrapped_token_core::holding_account_id( + programs::wrapped_token().deployed_account_id(), + &RECIPIENT, + ); let minted = wrapped_token_core::read_balance( &diff.public_diff()[&holding_id].data.clone().into_inner(), ); @@ -2280,17 +2396,19 @@ fn a_mint_from_an_unrouted_emitter_is_rejected() { l1_inclusion_witness: None, }; - let wrapped_config_id = wrapped_token_core::config_account_id(wrapped_token_id); - let holding_id = wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT); + let wrapped_config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); + let holding_id = wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts(inbox_id, &msg, vec![wrapped_config_id, holding_id]), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -2340,17 +2458,19 @@ fn a_mint_from_the_routed_emitter_is_accepted() { l1_inclusion_witness: None, }; - let wrapped_config_id = wrapped_token_core::config_account_id(wrapped_token_id); - let holding_id = wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT); + let wrapped_config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); + let holding_id = wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts(inbox_id, &msg, vec![wrapped_config_id, holding_id]), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -2384,7 +2504,11 @@ fn mint_replay_rejected() { // the replay no-op branch. The shard is inbox-owned (claimed on a prior // delivery) and bound to the same source block, so the guest leaves it // untouched. - let seen_id = inbox_seen_shard_account_id(inbox_id, &src_zone, src_block_id); + let seen_id = inbox_seen_shard_account_id( + program_loader_core::immutable_deploy_account_id(inbox_id), + &src_zone, + src_block_id, + ); let mut shard = SeenShard::default(); shard.insert(SRC_BLOCK_HASH, src_tx_index); state = state.with_public_accounts([( @@ -2412,17 +2536,19 @@ fn mint_replay_rejected() { l1_inclusion_witness: None, }; - let wrapped_config_id = wrapped_token_core::config_account_id(wrapped_token_id); - let holding_id = wrapped_token_core::holding_account_id(wrapped_token_id, &RECIPIENT); + let wrapped_config_id = wrapped_token_core::config_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + ); + let holding_id = wrapped_token_core::holding_account_id( + program_loader_core::immutable_deploy_account_id(wrapped_token_id), + &RECIPIENT, + ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts(inbox_id, &msg, vec![wrapped_config_id, holding_id]), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -2465,7 +2591,11 @@ fn a_delivery_from_a_second_block_at_the_same_id_is_refused() { seed_receiver_config(&mut state, None, vec![(src_zone, AccountId::new([9; 32]))]); // The shard as the first delivery left it: bound, holding transaction 0. - let seen_id = inbox_seen_shard_account_id(inbox_id, &src_zone, src_block_id); + let seen_id = inbox_seen_shard_account_id( + program_loader_core::immutable_deploy_account_id(inbox_id), + &src_zone, + src_block_id, + ); let mut shard = SeenShard::default(); shard.insert(SRC_BLOCK_HASH, 0); state = state.with_public_accounts([( @@ -2482,7 +2612,6 @@ fn a_delivery_from_a_second_block_at_the_same_id_is_refused() { )]); let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: b"from-the-other-block".to_vec(), }) .expect("serialize ping instruction"); @@ -2502,19 +2631,23 @@ fn a_delivery_from_a_second_block_at_the_same_id_is_refused() { l1_inclusion_witness: None, }; - let record_id = ping_record_pda(receiver_id); + let record_id = ping_record_pda(program_loader_core::immutable_deploy_account_id( + receiver_id, + )); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(inbox_id), dispatch_accounts( inbox_id, &msg, - vec![receiver_config_account_id(receiver_id), record_id], + vec![ + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + record_id, + ], ), vec![], - InboxInstruction::Dispatch { - message: msg, - self_program_id: inbox_id, - }, + InboxInstruction::Dispatch { message: msg }, ) .expect("build dispatch message"); let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![])); @@ -2527,7 +2660,6 @@ fn a_delivery_from_a_second_block_at_the_same_id_is_refused() { // Control: the same delivery naming the bound block executes, so the refusal // above is the binding and not the transaction's shape. let control_words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: b"from-the-bound-block".to_vec(), }) .expect("serialize ping instruction"); @@ -2550,12 +2682,16 @@ fn a_delivery_from_a_second_block_at_the_same_id_is_refused() { dispatch_accounts( inbox_id, &control_msg, - vec![receiver_config_account_id(receiver_id), record_id], + vec![ + receiver_config_account_id(program_loader_core::immutable_deploy_account_id( + receiver_id, + )), + record_id, + ], ), vec![], InboxInstruction::Dispatch { message: control_msg, - self_program_id: inbox_id, }, ) .expect("build dispatch message"); diff --git a/integration_tests/tests/cross_zone_verified.rs b/integration_tests/tests/cross_zone_verified.rs index 9fc32db9e..b313eb95c 100644 --- a/integration_tests/tests/cross_zone_verified.rs +++ b/integration_tests/tests/cross_zone_verified.rs @@ -98,7 +98,7 @@ async fn indexer_verifies_and_delivers_cross_zone_ping() -> Result<()> { // Wait until zone B's indexer records the delivered payload. The indexer only // applies the dispatch after re-deriving and verifying it. - let record_id = ping_record_pda(receiver_id); + let record_id = ping_record_pda(programs::ping_receiver().deployed_account_id()); let delivered = wait_for_indexer_delivery(ind_client_b, record_id).await?; assert_eq!( @@ -109,24 +109,24 @@ async fn indexer_verifies_and_delivers_cross_zone_ping() -> Result<()> { } fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransaction { - let outbox_id = programs::cross_zone_outbox().id(); + let receiver_account_id = program_loader_core::immutable_deploy_account_id(receiver_id); + let outbox_id = programs::cross_zone_outbox().deployed_account_id(); let ordinal = 0; let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: PING_PAYLOAD.to_vec(), }) .expect("serialize ping instruction"); let payload: Vec = words.iter().flat_map(|word| word.to_le_bytes()).collect(); let sender_id = programs::ping_sender().id(); + let sender_account_id = programs::ping_sender().deployed_account_id(); let send = SenderInstruction::Send { - self_program_id: sender_id, target_zone, target_program_id: receiver_id, target_accounts: vec![ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload, ordinal, @@ -140,7 +140,7 @@ fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransactio ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(sender_id), - vec![sender_config_account_id(sender_id), outbox_account], + vec![sender_config_account_id(sender_account_id), outbox_account], vec![], send, ) diff --git a/integration_tests/tests/cross_zone_watcher_restart.rs b/integration_tests/tests/cross_zone_watcher_restart.rs index b499ad706..0bf745489 100644 --- a/integration_tests/tests/cross_zone_watcher_restart.rs +++ b/integration_tests/tests/cross_zone_watcher_restart.rs @@ -92,7 +92,7 @@ async fn restarted_watcher_resumes_instead_of_replaying_the_peer_channel() -> Re .send_transaction(build_ping_tx(zone_b, receiver_id)) .await .context("Failed to submit ping on zone A")?; - let record_id = ping_record_pda(receiver_id); + let record_id = ping_record_pda(programs::ping_receiver().deployed_account_id()); let delivered = wait_for_delivery(sequencer_client(seq_b.addr())?, record_id).await?; assert_eq!( delivered, PING_PAYLOAD, @@ -188,24 +188,24 @@ async fn wait_for_block_id( /// Builds a top-level `ping_sender` transaction that chains into the outbox to emit /// a message carrying a `ping_receiver::Record` instruction for the target zone. fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransaction { - let outbox_id = programs::cross_zone_outbox().id(); + let receiver_account_id = program_loader_core::immutable_deploy_account_id(receiver_id); + let outbox_id = programs::cross_zone_outbox().deployed_account_id(); let ordinal = 0; let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: receiver_id, payload: PING_PAYLOAD.to_vec(), }) .expect("serialize ping instruction"); let payload: Vec = words.iter().flat_map(|word| word.to_le_bytes()).collect(); let sender_id = programs::ping_sender().id(); + let sender_account_id = programs::ping_sender().deployed_account_id(); let send = SenderInstruction::Send { - self_program_id: sender_id, target_zone, target_program_id: receiver_id, target_accounts: vec![ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload, ordinal, @@ -219,7 +219,7 @@ fn build_ping_tx(target_zone: [u8; 32], receiver_id: ProgramId) -> LeeTransactio ); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(sender_id), - vec![sender_config_account_id(sender_id), outbox_account], + vec![sender_config_account_id(sender_account_id), outbox_account], vec![], send, ) diff --git a/integration_tests/tests/private_pda.rs b/integration_tests/tests/private_pda.rs index b7d41d9cb..ca34ed398 100644 --- a/integration_tests/tests/private_pda.rs +++ b/integration_tests/tests/private_pda.rs @@ -12,7 +12,7 @@ use integration_tests::{ TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, utils::sync_private, verify_commitment_is_in_state, }; use lee::{ - AccountId, PrivacyPreservingTransaction, ProgramId, + AccountId, PrivacyPreservingTransaction, privacy_preserving_transaction::{ circuit::{ProgramWithDependencies, execute_and_prove}, message::Message, @@ -43,12 +43,12 @@ async fn fund_private_pda( vpk: ViewingPublicKey, identifier: u128, seed: PdaSeed, - authority_program_id: ProgramId, + authority_account_id: AccountId, amount: u128, auth_transfer: &ProgramWithDependencies, ) -> Result<()> { let pda_account_id = - AccountId::for_private_pda(&authority_program_id, &seed, &npk, &vpk, identifier); + AccountId::for_private_pda(&authority_account_id, &seed, &npk, &vpk, identifier); let sender_account = wallet .get_account_public(sender) .await @@ -70,7 +70,7 @@ async fn fund_private_pda( random_seed: [0; 32], identifier, kind: WitnessKind::Pda { - binding: Some((authority_program_id, seed)), + binding: Some((authority_account_id, seed)), }, nullifier: NullifierWitness::Init { npk, @@ -161,7 +161,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> { let proxy = test_programs::pda_spend_proxy(); let auth_transfer = programs::authenticated_transfer(); - let proxy_id = proxy.id(); + let proxy_id = proxy.deployed_account_id(); let auth_transfer_id = auth_transfer.id(); let seed = PdaSeed::new([42; 32]); let amount: u128 = 100; diff --git a/integration_tests/tests/sequencer_bootstrap.rs b/integration_tests/tests/sequencer_bootstrap.rs index dc6d72739..b092c65c7 100644 --- a/integration_tests/tests/sequencer_bootstrap.rs +++ b/integration_tests/tests/sequencer_bootstrap.rs @@ -110,7 +110,8 @@ fn supplied_account(balance: u128) -> (AccountId, GenesisAction) { let account_id = AccountId::from(&PublicKey::new_from_private_key( &PrivateKey::new_os_random(), )); - let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), account_id); + let vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), account_id); ( vault_id, GenesisAction::SupplyAccount { diff --git a/integration_tests/tests/sequencer_rejoin.rs b/integration_tests/tests/sequencer_rejoin.rs index 4a0b577a2..e00049a5b 100644 --- a/integration_tests/tests/sequencer_rejoin.rs +++ b/integration_tests/tests/sequencer_rejoin.rs @@ -92,7 +92,6 @@ async fn a_sequencer_leaves_the_committee_and_rejoins() -> Result<()> { AccountIdentity::PublicNoSign(config_id), ], &sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount: STAKE, destination: settlement, }, @@ -138,7 +137,6 @@ async fn a_sequencer_leaves_the_committee_and_rejoins() -> Result<()> { AccountIdentity::PublicNoSign(config_id), ], &sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key: stake_key_b, amount: STAKE, mover_account_id: program_loader_core::immutable_deploy_account_id( diff --git a/integration_tests/tests/sequencer_stake_demo.rs b/integration_tests/tests/sequencer_stake_demo.rs index 69c8f107b..a8ba3d881 100644 --- a/integration_tests/tests/sequencer_stake_demo.rs +++ b/integration_tests/tests/sequencer_stake_demo.rs @@ -73,7 +73,8 @@ async fn stake_transaction_joins_the_bedrock_committee() -> Result<()> { .add_imported_public_account(funding_private_key); // Claim the genesis supply out of its vault. - let owner_vault_id = vault_core::compute_vault_account_id(programs::vault().id(), funding_id); + let owner_vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), funding_id); let claim_instruction_data = Program::serialize_instruction(vault_core::Instruction::Claim { amount: FUNDING_BALANCE, }) @@ -112,7 +113,6 @@ async fn stake_transaction_joins_the_bedrock_committee() -> Result<()> { .context("Failed to serialize mover instruction")?; let stake_instruction_data = Program::serialize_instruction(sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key: demo_stake_key, amount: FUNDING_BALANCE, mover_account_id: program_loader_core::immutable_deploy_account_id( @@ -271,7 +271,6 @@ async fn stake_transaction_joins_the_bedrock_committee() -> Result<()> { let unstake_request_data = Program::serialize_instruction(sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount: FUNDING_BALANCE, destination: destination_id, }) diff --git a/integration_tests/tests/shared_accounts.rs b/integration_tests/tests/shared_accounts.rs index ac7c3429a..0e4d0518a 100644 --- a/integration_tests/tests/shared_accounts.rs +++ b/integration_tests/tests/shared_accounts.rs @@ -59,7 +59,7 @@ async fn group_create_and_shared_account_registration() -> Result<()> { label: Some("shared-acc".into()), pda: false, seed: None, - program_id: None, + authority_account_id: None, identifier: None, })); @@ -177,7 +177,7 @@ async fn fund_shared_account_from_public() -> Result<()> { label: None, pda: false, seed: None, - program_id: None, + authority_account_id: None, identifier: None, })); let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/tps.rs b/integration_tests/tests/tps.rs index b94c33485..4d332b924 100644 --- a/integration_tests/tests/tps.rs +++ b/integration_tests/tests/tps.rs @@ -79,11 +79,12 @@ impl TpsTestManager { sequencer_client: &sequencer_service_rpc::SequencerClient, ) -> Result<()> { let vault_program_id = programs::vault().id(); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); let mut tx_hashes = Vec::with_capacity(self.public_keypairs.len()); for (private_key, account_id) in &self.public_keypairs { let owner_vault_id = - vault_core::compute_vault_account_id(vault_program_id, *account_id); + vault_core::compute_vault_account_id(vault_account_id, *account_id); let message = putx::Message::try_new( program_loader_core::immutable_deploy_account_id(vault_program_id), vec![*account_id, owner_vault_id], diff --git a/integration_tests/tests/vault.rs b/integration_tests/tests/vault.rs index 70be480ee..c3d055c0f 100644 --- a/integration_tests/tests/vault.rs +++ b/integration_tests/tests/vault.rs @@ -17,8 +17,8 @@ async fn public_transfer_and_public_claim() -> Result<()> { let sender = ctx.existing_public_accounts()[0]; let recipient = ctx.existing_public_accounts()[1]; - let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient); + let vault_account_id = programs::vault().deployed_account_id(); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, recipient); let sender_balance_before = ctx.sequencer_client().get_account_balance(sender).await?; let recipient_balance_before = ctx @@ -100,8 +100,8 @@ async fn private_transfer_and_private_claim() -> Result<()> { let sender = ctx.existing_private_accounts()[0]; let owner = ctx.existing_private_accounts()[1]; - let vault_program_id = programs::vault().id(); - let owner_vault_id = vault_core::compute_vault_account_id(vault_program_id, owner); + let vault_account_id = programs::vault().deployed_account_id(); + let owner_vault_id = vault_core::compute_vault_account_id(vault_account_id, owner); let sender_balance_before = ctx .wallet() diff --git a/lee/key_protocol/src/key_management/group_key_holder.rs b/lee/key_protocol/src/key_management/group_key_holder.rs index e2a7cce84..ea6b14515 100644 --- a/lee/key_protocol/src/key_management/group_key_holder.rs +++ b/lee/key_protocol/src/key_management/group_key_holder.rs @@ -1,8 +1,9 @@ use aes_gcm::{Aes256Gcm, KeyInit as _, aead::Aead as _}; use lee_core::{ Identifier, SharedSecretKey, + account::AccountId, encryption::{EphemeralPublicKey, ML_KEM_768_CIPHERTEXT_LEN, ViewingPublicKey}, - program::{PdaSeed, ProgramId}, + program::PdaSeed, }; use rand::{RngCore as _, rngs::OsRng}; use serde::{Deserialize, Serialize}; @@ -98,21 +99,22 @@ impl GroupKeyHolder { /// Derive a per-PDA [`SecretSpendingKey`] by mixing the seed into the SHA-256 input. /// - /// Each distinct `(program_id, pda_seed)` pair produces a distinct SSK in the full 256-bit - /// space, so adversarial seed-grinding cannot collide two PDAs' derived keys under the same - /// group. Uses the codebase's 32-byte protocol-versioned domain-separation convention. + /// Each distinct `(program_account_id, pda_seed)` pair produces a distinct SSK in the full + /// 256-bit space, so adversarial seed-grinding cannot collide two PDAs' derived keys under + /// the same group. Keyed on the program's dispatch `AccountId` rather than its `ProgramId` + /// for the same reason `AccountId::for_public_pda`/`for_private_pda` are: two different + /// deployments of identical bytecode must derive different key families. Uses the codebase's + /// 32-byte protocol-versioned domain-separation convention. fn secret_spending_key_for_pda( &self, - program_id: &ProgramId, + program_account_id: &AccountId, pda_seed: &PdaSeed, ) -> SecretSpendingKey { const PREFIX: &[u8; 32] = b"/LEE/v0.3/GroupKeyDerivation/SSK"; let mut hasher = sha2::Sha256::new(); hasher.update(PREFIX); hasher.update(self.gms); - for word in program_id { - hasher.update(word.to_le_bytes()); - } + hasher.update(program_account_id.value()); hasher.update(pda_seed.as_ref()); SecretSpendingKey(hasher.finalize_fixed().into()) } @@ -120,14 +122,14 @@ impl GroupKeyHolder { /// Derive keys for a specific PDA under a given program. /// /// All controllers holding the same GMS independently derive the same keys for the - /// same `(program_id, seed)` because the derivation is deterministic. + /// same `(program_account_id, seed)` because the derivation is deterministic. #[must_use] pub fn derive_keys_for_pda( &self, - program_id: &ProgramId, + program_account_id: &AccountId, pda_seed: &PdaSeed, ) -> PrivateKeyHolder { - self.secret_spending_key_for_pda(program_id, pda_seed) + self.secret_spending_key_for_pda(program_account_id, pda_seed) .produce_private_key_holder(None) } @@ -256,7 +258,7 @@ mod tests { use super::*; - const TEST_PROGRAM_ID: ProgramId = [9; 8]; + const TEST_PROGRAM_ACCOUNT_ID: AccountId = AccountId::new([9; 32]); /// Two holders from the same GMS derive identical keys for the same PDA seed. #[test] @@ -266,8 +268,8 @@ mod tests { let holder_b = GroupKeyHolder::from_gms(gms); let seed = PdaSeed::new([1; 32]); - let keys_a = holder_a.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed); - let keys_b = holder_b.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed); + let keys_a = holder_a.derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed); + let keys_b = holder_b.derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed); assert_eq!( keys_a.generate_nullifier_public_key().to_byte_array(), @@ -283,10 +285,10 @@ mod tests { let seed_b = PdaSeed::new([2; 32]); let npk_a = holder - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed_a) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed_a) .generate_nullifier_public_key(); let npk_b = holder - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed_b) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed_b) .generate_nullifier_public_key(); assert_ne!(npk_a.to_byte_array(), npk_b.to_byte_array()); @@ -300,10 +302,10 @@ mod tests { let seed = PdaSeed::new([1; 32]); let npk_a = holder_a - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); let npk_b = holder_b - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); assert_ne!(npk_a.to_byte_array(), npk_b.to_byte_array()); @@ -317,10 +319,10 @@ mod tests { let seed = PdaSeed::new([1; 32]); let npk_original = original - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); let npk_restored = restored - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); assert_eq!(npk_original.to_byte_array(), npk_restored.to_byte_array()); @@ -332,38 +334,41 @@ mod tests { let holder = GroupKeyHolder::from_gms([42_u8; 32]); let seed = PdaSeed::new([1; 32]); let npk = holder - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); assert_ne!(npk, NullifierPublicKey([0; 32])); } - /// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change + /// Pins the end-to-end derivation for a fixed (GMS, `AccountId`, `PdaSeed`). Any change /// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` ask/nsk/npk chain, or the /// `AccountId::for_private_pda` formula breaks this test. Mirrors the pinned-value /// pattern from `for_private_pda_matches_pinned_value` in `lee_core`. #[test] fn pinned_end_to_end_derivation_for_private_pda() { - use lee_core::{account::AccountId, program::ProgramId}; - let gms = [42_u8; 32]; let seed = PdaSeed::new([1; 32]); - let program_id: ProgramId = [9; 8]; let holder = GroupKeyHolder::from_gms(gms); - let keys = holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed); + let keys = holder.derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed); let npk = keys.generate_nullifier_public_key(); let vpk = keys.generate_viewing_public_key(); - let account_id = AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX); + let account_id = + AccountId::for_private_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed, &npk, &vpk, u128::MAX); let expected_npk = NullifierPublicKey([ - 59, 136, 7, 185, 56, 46, 38, 4, 195, 155, 85, 32, 161, 24, 119, 14, 148, 100, 26, 152, - 239, 255, 145, 142, 122, 166, 219, 75, 200, 9, 168, 7, + 157, 78, 138, 125, 166, 166, 136, 141, 157, 143, 238, 187, 210, 89, 187, 136, 196, 15, + 217, 30, 21, 89, 46, 158, 180, 44, 73, 55, 147, 158, 210, 66, ]); - // AccountId is derived from (program_id, seed, npk), so it changes when npk changes. - // We verify npk is pinned, and AccountId is deterministically derived from it. - let expected_account_id = - AccountId::for_private_pda(&program_id, &seed, &expected_npk, &vpk, u128::MAX); + // AccountId is derived from (program_account_id, seed, npk), so it changes when npk + // changes. We verify npk is pinned, and AccountId is deterministically derived from it. + let expected_account_id = AccountId::for_private_pda( + &TEST_PROGRAM_ACCOUNT_ID, + &seed, + &expected_npk, + &vpk, + u128::MAX, + ); assert_eq!(npk, expected_npk); assert_eq!(account_id, expected_account_id); @@ -381,10 +386,10 @@ mod tests { let seed = PdaSeed::new([1; 32]); let npk_original = original - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); let npk_restored = restored - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); assert_eq!(npk_original, npk_restored); @@ -402,7 +407,7 @@ mod tests { let seed = PdaSeed::new([5; 32]); let group_npk = GroupKeyHolder::from_gms(shared_bytes) - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(); let personal_npk = SecretSpendingKey(shared_bytes) @@ -432,10 +437,10 @@ mod tests { let seed = PdaSeed::new([1; 32]); assert_eq!( holder - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(), restored - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key(), ); } @@ -523,7 +528,7 @@ mod tests { .iter() .map(|gms| { GroupKeyHolder::from_gms(*gms) - .derive_keys_for_pda(&TEST_PROGRAM_ID, &seed) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &seed) .generate_nullifier_public_key() }) .collect(); @@ -541,13 +546,10 @@ mod tests { /// Full lifecycle: create group, distribute GMS via seal/unseal, verify key agreement. #[test] fn group_pda_lifecycle() { - use lee_core::account::AccountId; - let alice_holder = GroupKeyHolder::new(); let pda_seed = PdaSeed::new([42_u8; 32]); - let program_id: lee_core::program::ProgramId = [1; 8]; - let alice_keys = alice_holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &pda_seed); + let alice_keys = alice_holder.derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &pda_seed); let alice_npk = alice_keys.generate_nullifier_public_key(); let bob_ssk = SecretSpendingKey([77_u8; 32]); @@ -560,16 +562,26 @@ mod tests { let bob_holder = GroupKeyHolder::unseal(&sealed, &bob_vsk).expect("Bob should unseal the GMS"); - let bob_group_keys = bob_holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &pda_seed); + let bob_group_keys = bob_holder.derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &pda_seed); let bob_npk = bob_group_keys.generate_nullifier_public_key(); assert_eq!(alice_npk, bob_npk); let alice_vpk = alice_keys.generate_viewing_public_key(); let bob_group_vpk = bob_group_keys.generate_viewing_public_key(); - let alice_account_id = - AccountId::for_private_pda(&program_id, &pda_seed, &alice_npk, &alice_vpk, 0); - let bob_account_id = - AccountId::for_private_pda(&program_id, &pda_seed, &bob_npk, &bob_group_vpk, 0); + let alice_account_id = AccountId::for_private_pda( + &TEST_PROGRAM_ACCOUNT_ID, + &pda_seed, + &alice_npk, + &alice_vpk, + 0, + ); + let bob_account_id = AccountId::for_private_pda( + &TEST_PROGRAM_ACCOUNT_ID, + &pda_seed, + &bob_npk, + &bob_group_vpk, + 0, + ); assert_eq!(alice_account_id, bob_account_id); } @@ -612,7 +624,7 @@ mod tests { let bytes = [1_u8; 32]; let pda_npk = holder - .derive_keys_for_pda(&TEST_PROGRAM_ID, &PdaSeed::new(bytes)) + .derive_keys_for_pda(&TEST_PROGRAM_ACCOUNT_ID, &PdaSeed::new(bytes)) .generate_nullifier_public_key(); let shared_npk = holder .derive_keys_for_shared_account(&bytes) diff --git a/lee/privacy_preserving_circuit/src/execution_state.rs b/lee/privacy_preserving_circuit/src/execution_state.rs index b8f0712ac..d895d91a8 100644 --- a/lee/privacy_preserving_circuit/src/execution_state.rs +++ b/lee/privacy_preserving_circuit/src/execution_state.rs @@ -23,8 +23,8 @@ pub struct ExecutionState { block_validity_window: BlockValidityWindow, timestamp_validity_window: TimestampValidityWindow, /// Positions (in `pre_states`) of private-PDA accounts whose supplied npk has been bound to - /// their `AccountId` via a proven `AccountId::for_private_pda(program_id, seed, npk, vpk, - /// identifier)` check. + /// their `AccountId` via a proven `AccountId::for_private_pda(program_account_id, seed, npk, + /// vpk, identifier)` check. /// Two proof paths populate this set: a `Claim::Pda(seed)` in a program's `post_state` on /// that `pre_state`, or a caller's `ChainedCall.pda_seeds` entry matching that `pre_state` /// under the private derivation. Binding is an idempotent property, not an event: the same @@ -32,24 +32,24 @@ pub struct ExecutionState { /// claims a private PDA and then delegates it to a callee), and the map uses `contains_key`, /// not `assert!(insert)`. After the main loop, every private-PDA position must appear in this /// map; otherwise the npk is unbound and the circuit rejects. - /// The stored `(ProgramId, PdaSeed)` is the owner program and seed, used in - /// `compute_circuit_output` to construct `PrivateAccountKind::Pda { program_id, seed, - /// identifier }`. - private_pda_bound_positions: HashMap, - /// Across the whole transaction, each `(program_id, seed)` pair may resolve to at most one - /// `AccountId`. A seed under a program can derive a family of accounts, one public PDA and - /// one private PDA per distinct npk. Without this check, a single `pda_seeds: [S]` entry in - /// a chained call could authorize multiple family members at once (different npks under the - /// same seed) and let a callee mix balances across them. Every claim and every - /// caller-authorization resolution is recorded here, either as a new `(program, seed)` → - /// `AccountId` entry or as an equality check against the existing one, making the rule: one - /// `(program, seed)` → one account per tx. - pda_family_binding: HashMap<(ProgramId, PdaSeed), AccountId>, + /// The stored `(AccountId, PdaSeed)` is the owning program's dispatch address and seed, used + /// in `compute_circuit_output` to construct `PrivateAccountKind::Pda { program_account_id, + /// seed, identifier }`. + private_pda_bound_positions: HashMap, + /// Across the whole transaction, each `(program_account_id, seed)` pair may resolve to at + /// most one `AccountId`. A seed under a program can derive a family of accounts, one public + /// PDA and one private PDA per distinct npk. Without this check, a single `pda_seeds: [S]` + /// entry in a chained call could authorize multiple family members at once (different npks + /// under the same seed) and let a callee mix balances across them. Every claim and every + /// caller-authorization resolution is recorded here, either as a new + /// `(program_account_id, seed)` → `AccountId` entry or as an equality check against the + /// existing one, making the rule: one `(program_account_id, seed)` → one account per tx. + pda_family_binding: HashMap<(AccountId, PdaSeed), AccountId>, /// Map from a private-PDA `pre_state`'s position in `account_identities` to the (npk, vpk, /// identifier) supplied for that position. Built once in `derive_from_outputs` by walking /// `account_identities` and consulting `npk_vpk_if_private_pda`. Used later by the claim and /// caller-seeds authorization paths to verify - /// `AccountId::for_private_pda(program_id, seed, npk, vpk, identifier) == + /// `AccountId::for_private_pda(program_account_id, seed, npk, vpk, identifier) == /// pre_state.account_id`. private_pda_by_position: HashMap, /// The set containing non-PDA accounts authorized at their first sight, anywhere in the @@ -58,15 +58,12 @@ pub struct ExecutionState { } /// A chained call's caller context, threaded down its own lineage only (never shared across -/// sibling branches): who called it, that caller's real image id (needed for PDA derivation, -/// since a `Deploy`-created caller's address doesn't encode it), and the accounts authorized -/// along the path from the root down to this call. `authorized_accounts` is monotonically -/// growing per lineage — each child inherits its parent's set plus whatever the parent itself -/// authorized in its own `pre_states` — so authorization earned in one branch never leaks into an -/// unrelated sibling branch. +/// sibling branches): who called it, and the accounts authorized along the path from the root +/// down to this call. `authorized_accounts` is monotonically growing per lineage — each child +/// inherits its parent's set plus whatever the parent itself authorized in its own `pre_states` +/// — so authorization earned in one branch never leaks into an unrelated sibling branch. struct CallerData { caller_account_id: Option, - caller_image_id: Option, authorized_accounts: HashSet, } @@ -154,7 +151,6 @@ impl ExecutionState { }; let initial_caller_data = CallerData { caller_account_id: None, - caller_image_id: None, authorized_accounts: HashSet::new(), }; let mut chained_calls = VecDeque::from_iter([(initial_call, initial_caller_data)]); @@ -229,7 +225,6 @@ impl ExecutionState { let authorized_accounts = execution_state.validate_and_sync_states( account_identities, chained_call.program_account_id, - current_program_id, caller_data, &chained_call.pda_seeds, program_output.pre_states, @@ -241,7 +236,6 @@ impl ExecutionState { next_call, CallerData { caller_account_id: Some(chained_call.program_account_id), - caller_image_id: Some(current_program_id), authorized_accounts: authorized_accounts.clone(), }, )); @@ -296,15 +290,10 @@ impl ExecutionState { } /// Validate program pre and post states and populate the execution state. - #[expect( - clippy::too_many_arguments, - reason = "breaking out a context struct does not buy us anything here" - )] fn validate_and_sync_states( &mut self, account_identities: &[InputAccountIdentity], account_id: AccountId, - program_id: ProgramId, caller: CallerData, caller_pda_seeds: &[PdaSeed], output_pre_states: Vec, @@ -369,13 +358,13 @@ impl ExecutionState { identifier, kind: WitnessKind::Pda { - binding: Some((authority_program_id, seed)), + binding: Some((authority_account_id, seed)), }, nullifier, .. })) => { let expected = AccountId::for_private_pda( - authority_program_id, + authority_account_id, seed, &nullifier.npk(), vpk, @@ -385,23 +374,23 @@ impl ExecutionState { pre_account_id, expected, "External seed mismatch for private PDA at position {pre_state_position}" ); - Some((*authority_program_id, *seed)) + Some((*authority_account_id, *seed)) } _ => 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((authority_program_id, seed)) = external_seed { + if let Some((authority_account_id, seed)) = external_seed { bind_private_pda_position( &mut self.private_pda_bound_positions, pre_state_position, - authority_program_id, + authority_account_id, seed, ); assert_family_binding( &mut self.pda_family_binding, - authority_program_id, + authority_account_id, seed, pre_account_id, ); @@ -478,14 +467,14 @@ impl ExecutionState { ); } Claim::Pda(seed) => { - let pda = AccountId::for_public_pda(&program_id, &seed); + let pda = AccountId::for_public_pda(&account_id, &seed); assert_eq!( pre_account_id, pda, "Invalid PDA claim for account {pre_account_id} which does not match derived PDA {pda}" ); assert_family_binding( &mut self.pda_family_binding, - program_id, + account_id, seed, pre_account_id, ); @@ -504,7 +493,7 @@ impl ExecutionState { "private PDA pre_state must have an npk in the position map", ); let pda = AccountId::for_private_pda( - &program_id, + &account_id, &seed, npk, vpk, @@ -517,12 +506,12 @@ impl ExecutionState { bind_private_pda_position( &mut self.private_pda_bound_positions, pre_state_position, - program_id, + account_id, seed, ); assert_family_binding( &mut self.pda_family_binding, - program_id, + account_id, seed, pre_account_id, ); @@ -554,7 +543,7 @@ impl ExecutionState { ) -> ( BlockValidityWindow, TimestampValidityWindow, - HashMap, + HashMap, impl ExactSizeIterator, ) { let block_validity_window = self.block_validity_window; @@ -576,20 +565,20 @@ impl ExecutionState { } } -/// Record or re-verify the `(program_id, seed) → account_id` family binding for the +/// Record or re-verify the `(program_account_id, seed) → account_id` family binding for the /// transaction. Any claim or caller-seed authorization that resolves a `pre_state` under -/// `(program_id, seed)` must agree with every prior resolution of the same pair; otherwise a -/// single `pda_seeds: [seed]` entry could authorize multiple private-PDA family members at -/// once (different npks under the same seed) and let a callee mix balances across them. Free -/// function so callers can pass `&mut self.pda_family_binding` without holding a borrow on -/// the surrounding struct's other fields. +/// `(program_account_id, seed)` must agree with every prior resolution of the same pair; +/// otherwise a single `pda_seeds: [seed]` entry could authorize multiple private-PDA family +/// members at once (different npks under the same seed) and let a callee mix balances across +/// them. Free function so callers can pass `&mut self.pda_family_binding` without holding a +/// borrow on the surrounding struct's other fields. fn assert_family_binding( - bindings: &mut HashMap<(ProgramId, PdaSeed), AccountId>, - program_id: ProgramId, + bindings: &mut HashMap<(AccountId, PdaSeed), AccountId>, + program_account_id: AccountId, seed: PdaSeed, account_id: AccountId, ) { - match bindings.entry((program_id, seed)) { + match bindings.entry((program_account_id, seed)) { Entry::Vacant(e) => { e.insert(account_id); } @@ -605,19 +594,19 @@ fn assert_family_binding( } fn bind_private_pda_position( - map: &mut HashMap, + map: &mut HashMap, position: usize, - program_id: ProgramId, + program_account_id: AccountId, seed: PdaSeed, ) { match map.entry(position) { Entry::Occupied(e) => assert_eq!( *e.get(), - (program_id, seed), - "Duplicate binding at position {position}: conflicting (program_id, seed)" + (program_account_id, seed), + "Duplicate binding at position {position}: conflicting (program_account_id, seed)" ), Entry::Vacant(e) => { - e.insert((program_id, seed)); + e.insert((program_account_id, seed)); } } } @@ -627,30 +616,30 @@ fn bind_private_pda_position( /// Either the account is a public PDA the caller delegates via `caller_pda_seeds`, in which case /// the public mask must be cleared before export (see the `pre.is_authorized = false` comment at /// the call site), or it's a regular account, whose authorization (if any) becomes globally -/// visible for the rest of the call tree. Only reachable when `caller.caller_image_id.is_some()`; -/// top-level flows have no caller-emitted seeds, so a first-sight PDA there must come through the -/// claim path instead. +/// visible for the rest of the call tree. Only reachable when +/// `caller.caller_account_id.is_some()`; top-level flows have no caller-emitted seeds, so a +/// first-sight PDA there must come through the claim path instead. fn authorize_first_sight_without_pda_witness( - pda_family_binding: &mut HashMap<(ProgramId, PdaSeed), AccountId>, + pda_family_binding: &mut HashMap<(AccountId, PdaSeed), AccountId>, globally_authorized: &mut HashSet, caller: &CallerData, caller_pda_seeds: &[PdaSeed], pre_account_id: AccountId, pre_is_authorized: bool, ) -> bool { - let matched_caller_seed = caller.caller_image_id.and_then(|caller_id| { + let matched_caller_seed = caller.caller_account_id.and_then(|caller_account_id| { caller_pda_seeds .iter() - .find(|seed| AccountId::for_public_pda(&caller_id, seed) == pre_account_id) - .map(|seed| (*seed, caller_id)) + .find(|seed| AccountId::for_public_pda(&caller_account_id, seed) == pre_account_id) + .map(|seed| (*seed, caller_account_id)) }); - if let Some((seed, caller_id)) = matched_caller_seed { + if let Some((seed, caller_account_id)) = matched_caller_seed { assert!( pre_is_authorized, "Caller-seeded public PDA must be declared authorized at first sight: {pre_account_id}" ); - assert_family_binding(pda_family_binding, caller_id, seed, pre_account_id); + assert_family_binding(pda_family_binding, caller_account_id, seed, pre_account_id); true } else { if pre_is_authorized { @@ -672,16 +661,16 @@ fn authorize_first_sight_without_pda_witness( /// let authorization earned in one branch leak into an unrelated sibling branch. When a caller /// seed matches, also records the `(caller, seed) → account_id` family binding and, for the /// private form, marks the position in `private_pda_bound_positions`. Only reachable when -/// `caller.caller_image_id.is_some()`, top-level flows have no caller-emitted seeds, so binding at -/// top level must come from the claim path. Free function so callers can pass individual +/// `caller.caller_account_id.is_some()`, top-level flows have no caller-emitted seeds, so binding +/// at top level must come from the claim path. Free function so callers can pass individual /// `&mut self.*` field borrows without holding a borrow on the surrounding struct's other fields. #[expect( clippy::too_many_arguments, reason = "breaking out a context struct does not buy us anything here" )] fn resolve_authorization_and_record_bindings( - pda_family_binding: &mut HashMap<(ProgramId, PdaSeed), AccountId>, - private_pda_bound_positions: &mut HashMap, + pda_family_binding: &mut HashMap<(AccountId, PdaSeed), AccountId>, + private_pda_bound_positions: &mut HashMap, private_pda_by_position: &HashMap, globally_authorized: &HashSet, caller: &CallerData, @@ -689,34 +678,30 @@ fn resolve_authorization_and_record_bindings( pre_state_position: usize, caller_pda_seeds: &[PdaSeed], ) -> bool { - // `for_public_pda`/`for_private_pda`'s derivation formula is pinned to the caller's real - // image id, not its dispatch-facing `AccountId` — a `Deploy`-created caller's address doesn't - // encode it, so `caller.caller_image_id` must be the recovered real image id (see - // `derive_from_outputs`'s `current_program_id`), not a bijection round-trip. - let matched_caller_seed: Option<(PdaSeed, bool, ProgramId)> = - caller.caller_image_id.and_then(|caller_id| { + let matched_caller_seed: Option<(PdaSeed, bool, AccountId)> = + caller.caller_account_id.and_then(|caller_account_id| { caller_pda_seeds.iter().find_map(|seed| { - if AccountId::for_public_pda(&caller_id, seed) == pre_account_id { - return Some((*seed, false, caller_id)); + if AccountId::for_public_pda(&caller_account_id, seed) == pre_account_id { + return Some((*seed, false, caller_account_id)); } if let Some((npk, vpk, identifier)) = private_pda_by_position.get(&pre_state_position) - && AccountId::for_private_pda(&caller_id, seed, npk, vpk, *identifier) + && AccountId::for_private_pda(&caller_account_id, seed, npk, vpk, *identifier) == pre_account_id { - return Some((*seed, true, caller_id)); + return Some((*seed, true, caller_account_id)); } None }) }); - if let Some((seed, is_private_form, caller_id)) = matched_caller_seed { - assert_family_binding(pda_family_binding, caller_id, seed, pre_account_id); + if let Some((seed, is_private_form, caller_account_id)) = matched_caller_seed { + assert_family_binding(pda_family_binding, caller_account_id, seed, pre_account_id); if is_private_form { bind_private_pda_position( private_pda_bound_positions, pre_state_position, - caller_id, + caller_account_id, seed, ); } diff --git a/lee/privacy_preserving_circuit/src/output.rs b/lee/privacy_preserving_circuit/src/output.rs index 0b7f1069e..325970cf0 100644 --- a/lee/privacy_preserving_circuit/src/output.rs +++ b/lee/privacy_preserving_circuit/src/output.rs @@ -131,11 +131,11 @@ pub fn compute_circuit_output( let account_kind = match kind { WitnessKind::Regular { .. } => PrivateAccountKind::Regular(*identifier), WitnessKind::Pda { .. } => { - let (authority_program_id, seed) = pda_seed_by_position + let (authority_account_id, seed) = pda_seed_by_position .get(&pos) .expect("private PDA position must be in pda_seed_by_position"); PrivateAccountKind::Pda { - program_id: *authority_program_id, + program_account_id: *authority_account_id, seed: *seed, identifier: *identifier, } diff --git a/lee/state_machine/core/src/circuit_io.rs b/lee/state_machine/core/src/circuit_io.rs index f44a5ba95..1b0a11665 100644 --- a/lee/state_machine/core/src/circuit_io.rs +++ b/lee/state_machine/core/src/circuit_io.rs @@ -73,14 +73,15 @@ pub enum WitnessKind { Regular { ask: Option }, /// Private PDA. The npk-to-account_id binding is proven upstream via `Claim::Pda(seed)` or a /// caller's `pda_seeds` match. The identifier diversifies the PDA within the - /// `(program_id, seed, npk)` family: `AccountId::for_private_pda` uses it as the 4th input. + /// `(program_account_id, seed, npk)` family: `AccountId::for_private_pda` uses it as the 4th + /// input. Pda { - /// When `Some((authority_program_id, seed))`, the circuit binds this position via the + /// When `Some((authority_account_id, seed))`, the circuit binds this position via the /// external derivation check - /// `AccountId::for_private_pda(authority_program_id, seed, npk, vpk, identifier) == + /// `AccountId::for_private_pda(authority_account_id, seed, npk, vpk, identifier) == /// pre_state.account_id` rather than requiring a `Claim::Pda` or caller /// `pda_seeds` to establish the binding. - binding: Option<(ProgramId, PdaSeed)>, + binding: Option<(AccountId, PdaSeed)>, }, } diff --git a/lee/state_machine/core/src/encryption/mod.rs b/lee/state_machine/core/src/encryption/mod.rs index de68ab720..4fd1f0460 100644 --- a/lee/state_machine/core/src/encryption/mod.rs +++ b/lee/state_machine/core/src/encryption/mod.rs @@ -282,7 +282,7 @@ mod tests { let pda_ct = EncryptionScheme::encrypt( &account, &PrivateAccountKind::Pda { - program_id: [1_u32; 8], + program_account_id: crate::account::AccountId::new([1_u8; 32]), seed: PdaSeed::new([2_u8; 32]), identifier: 42, }, diff --git a/lee/state_machine/core/src/program/mod.rs b/lee/state_machine/core/src/program/mod.rs index c6f07e73d..7ba5e4956 100644 --- a/lee/state_machine/core/src/program/mod.rs +++ b/lee/state_machine/core/src/program/mod.rs @@ -162,22 +162,12 @@ impl AsRef<[u8]> for PdaSeed { /// to reconstruct the account's [`AccountId`] on the receiver side. /// /// [`AccountId`]: crate::account::AccountId -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - BorshSerialize, - BorshDeserialize, -)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[cfg_attr(any(feature = "host", test), derive(PartialOrd, Ord))] pub enum PrivateAccountKind { Regular(Identifier), Pda { - program_id: ProgramId, + program_account_id: AccountId, seed: PdaSeed, identifier: Identifier, }, @@ -187,8 +177,9 @@ impl PrivateAccountKind { /// Borsh layout (all integers little-endian, variant index is u8): /// /// ```text - /// Regular(ident): 0x00 || ident (16 LE) || [0u8; 64] - /// Pda { program_id, seed, ident }: 0x01 || program_id (32) || seed (32) || ident (16 LE) + /// Regular(ident): 0x00 || ident (16 LE) || [0u8; 64] + /// Pda { program_account_id, seed, ident }: 0x01 || program_account_id (32) || seed (32) + /// || ident (16 LE) /// ``` /// /// Both variants are zero-padded to the same length so all ciphertexts are the same size, @@ -219,18 +210,22 @@ impl PrivateAccountKind { } impl AccountId { - /// Derives an [`AccountId`] for a public PDA from the program ID and seed. + /// Derives an [`AccountId`] for a public PDA from the owning program's dispatch address and + /// a seed. + /// + /// Keyed on the program's `AccountId` (its actual dispatch address), not its `ProgramId` + /// (bytecode image id): two different deployments of identical bytecode get different + /// `AccountId`s (see `program_loader_core::deploy_header_account_id`'s `update_auth` + /// parameter), and each must own a disjoint family of PDAs. #[must_use] - pub fn for_public_pda(program_id: &ProgramId, seed: &PdaSeed) -> Self { + pub fn for_public_pda(program_account_id: &Self, seed: &PdaSeed) -> Self { use risc0_zkvm::sha::{Impl, Sha256 as _}; const PROGRAM_DERIVED_ACCOUNT_ID_PREFIX: &[u8; 32] = - b"/LEE/v0.2/AccountId/PDA/\x00\x00\x00\x00\x00\x00\x00\x00"; + b"/LEE/v0.3/AccountId/PDA/\x00\x00\x00\x00\x00\x00\x00\x00"; let mut bytes = [0; 96]; bytes[0..32].copy_from_slice(PROGRAM_DERIVED_ACCOUNT_ID_PREFIX); - let program_id_bytes: &[u8] = - bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]"); - bytes[32..64].copy_from_slice(program_id_bytes); + bytes[32..64].copy_from_slice(program_account_id.value()); bytes[64..].copy_from_slice(&seed.0); Self::new( Impl::hash_bytes(&bytes) @@ -240,16 +235,17 @@ impl AccountId { ) } - /// Derives an [`AccountId`] for a private PDA from the program ID, seed, nullifier public - /// key, and identifier. + /// Derives an [`AccountId`] for a private PDA from the owning program's dispatch address, + /// seed, nullifier public key, and identifier. /// /// Unlike public PDAs ([`AccountId::for_public_pda`]), this includes the `npk` in the /// derivation, making the address unique per group of controllers sharing viewing keys. - /// The `identifier` further diversifies the address, so a single `(program_id, seed, npk)` - /// tuple controls a family of 2^128 addresses. + /// The `identifier` further diversifies the address, so a single + /// `(program_account_id, seed, npk)` tuple controls a family of 2^128 addresses. See + /// `for_public_pda`'s doc for why this is keyed on `AccountId` rather than `ProgramId`. #[must_use] pub fn for_private_pda( - program_id: &ProgramId, + program_account_id: &Self, seed: &PdaSeed, npk: &NullifierPublicKey, vpk: &ViewingPublicKey, @@ -260,9 +256,7 @@ impl AccountId { let mut bytes = [0_u8; 32 + 32 + 32 + 32 + ViewingPublicKey::LEN + 16]; bytes[0..32].copy_from_slice(PRIVATE_PDA_PREFIX); - let program_id_bytes: &[u8] = - bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]"); - bytes[32..64].copy_from_slice(program_id_bytes); + bytes[32..64].copy_from_slice(program_account_id.value()); bytes[64..96].copy_from_slice(&seed.0); bytes[96..128].copy_from_slice(&npk.to_byte_array()); bytes[128..128 + ViewingPublicKey::LEN].copy_from_slice(vpk.to_bytes()); @@ -287,10 +281,10 @@ impl AccountId { Self::for_regular_private_account(npk, vpk, *identifier) } PrivateAccountKind::Pda { - program_id, + program_account_id, seed, identifier, - } => Self::for_private_pda(program_id, seed, npk, vpk, *identifier), + } => Self::for_private_pda(program_account_id, seed, npk, vpk, *identifier), } } } @@ -727,10 +721,10 @@ pub enum ExecutionValidationError { /// `pre_state`. #[must_use] pub fn compute_public_authorized_pdas( - caller_image_id: Option, + caller_account_id: Option, pda_seeds: &[PdaSeed], ) -> HashSet { - let Some(caller) = caller_image_id else { + let Some(caller) = caller_account_id else { return HashSet::new(); }; pda_seeds diff --git a/lee/state_machine/core/src/program/tests.rs b/lee/state_machine/core/src/program/tests.rs index 4a0b1104b..b31735870 100644 --- a/lee/state_machine/core/src/program/tests.rs +++ b/lee/state_machine/core/src/program/tests.rs @@ -177,21 +177,21 @@ fn post_state_account_getter() { // ---- AccountId::for_private_pda tests ---- /// Pins `AccountId::for_private_pda` against a hardcoded expected output for a specific -/// `(program_id, seed, npk, identifier)` tuple. Any change to `PRIVATE_PDA_PREFIX`, byte +/// `(program_account_id, seed, npk, identifier)` tuple. Any change to `PRIVATE_PDA_PREFIX`, byte /// ordering, or the underlying hash breaks this test. #[test] fn for_private_pda_matches_pinned_value() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let identifier: Identifier = u128::MAX; let expected = AccountId::new([ - 5, 87, 128, 244, 206, 244, 65, 130, 178, 88, 225, 183, 0, 159, 201, 201, 212, 206, 6, 156, - 13, 55, 32, 139, 91, 222, 209, 83, 172, 148, 123, 179, + 45, 111, 98, 244, 74, 174, 52, 116, 162, 206, 73, 35, 144, 34, 128, 43, 111, 35, 11, 7, 70, + 138, 245, 238, 254, 218, 194, 225, 201, 202, 160, 109, ]); assert_eq!( - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, identifier), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, identifier), expected ); } @@ -199,60 +199,61 @@ fn for_private_pda_matches_pinned_value() { /// Two groups with different viewing keys at the same (program, seed) get different addresses. #[test] fn for_private_pda_differs_for_different_npk() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let npk_a = NullifierPublicKey([3; 32]); let npk_b = NullifierPublicKey([4; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); assert_ne!( - AccountId::for_private_pda(&program_id, &seed, &npk_a, &vpk, u128::MAX), - AccountId::for_private_pda(&program_id, &seed, &npk_b, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id, &seed, &npk_a, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id, &seed, &npk_b, &vpk, u128::MAX), ); } /// Different seeds produce different addresses, even with the same program and npk. #[test] fn for_private_pda_differs_for_different_seed() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed_a = PdaSeed::new([2; 32]); let seed_b = PdaSeed::new([5; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); assert_ne!( - AccountId::for_private_pda(&program_id, &seed_a, &npk, &vpk, u128::MAX), - AccountId::for_private_pda(&program_id, &seed_b, &npk, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id, &seed_a, &npk, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id, &seed_b, &npk, &vpk, u128::MAX), ); } /// Different programs produce different addresses, even with the same seed and npk. #[test] -fn for_private_pda_differs_for_different_program_id() { - let program_id_a: ProgramId = [1; 8]; - let program_id_b: ProgramId = [9; 8]; +fn for_private_pda_differs_for_different_program_account_id() { + let program_account_id_a = AccountId::new([1; 32]); + let program_account_id_b = AccountId::new([9; 32]); let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); assert_ne!( - AccountId::for_private_pda(&program_id_a, &seed, &npk, &vpk, u128::MAX), - AccountId::for_private_pda(&program_id_b, &seed, &npk, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id_a, &seed, &npk, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id_b, &seed, &npk, &vpk, u128::MAX), ); } -/// Different identifiers produce different addresses for the same `(program_id, seed, npk)`, -/// confirming that each `(program_id, seed, npk)` tuple controls a family of 2^128 addresses. +/// Different identifiers produce different addresses for the same +/// `(program_account_id, seed, npk)`, confirming that each `(program_account_id, seed, npk)` +/// tuple controls a family of 2^128 addresses. #[test] fn for_private_pda_differs_for_different_identifier() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); assert_ne!( - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 0), - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 1), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, 0), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, 1), ); assert_ne!( - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 0), - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, 0), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, u128::MAX), ); } @@ -260,12 +261,12 @@ fn for_private_pda_differs_for_different_identifier() { /// because the private formula uses a different prefix and includes npk. #[test] fn for_private_pda_differs_from_public_pda() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); - let private_id = AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX); - let public_id = AccountId::for_public_pda(&program_id, &seed); + let private_id = AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, u128::MAX); + let public_id = AccountId::for_public_pda(&program_account_id, &seed); assert_ne!(private_id, public_id); } @@ -274,7 +275,7 @@ fn for_private_pda_differs_from_public_pda() { fn private_account_kind_header_round_trips() { let regular = PrivateAccountKind::Regular(42); let pda = PrivateAccountKind::Pda { - program_id: [1_u32; 8], + program_account_id: AccountId::new([1_u8; 32]), seed: PdaSeed::new([2_u8; 32]), identifier: u128::MAX, }; @@ -298,7 +299,7 @@ fn private_account_kind_unknown_discriminant_returns_none() { #[test] fn for_private_account_dispatches_correctly() { - let program_id: ProgramId = [1; 8]; + let program_account_id = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let npk = NullifierPublicKey([3; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); @@ -313,18 +314,18 @@ fn for_private_account_dispatches_correctly() { &npk, &vpk, &PrivateAccountKind::Pda { - program_id, + program_account_id, seed, identifier } ), - AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, identifier), + AccountId::for_private_pda(&program_account_id, &seed, &npk, &vpk, identifier), ); } #[test] fn compute_public_authorized_pdas_with_seeds() { - let caller: ProgramId = [1; 8]; + let caller = AccountId::new([1; 32]); let seed = PdaSeed::new([2; 32]); let result = compute_public_authorized_pdas(Some(caller), &[seed]); let expected = AccountId::for_public_pda(&caller, &seed); diff --git a/lee/state_machine/src/privacy_preserving_transaction/circuit/mod.rs b/lee/state_machine/src/privacy_preserving_transaction/circuit/mod.rs index 15d14cd22..7624783ff 100644 --- a/lee/state_machine/src/privacy_preserving_transaction/circuit/mod.rs +++ b/lee/state_machine/src/privacy_preserving_transaction/circuit/mod.rs @@ -43,9 +43,11 @@ impl Proof { #[derive(Clone)] pub struct ProgramWithDependencies { pub program: Program, - /// Where `program` is dispatched at. Defaults to `AccountId::from(program.id())` (correct - /// for a legacy, bijection-addressed program); override via - /// [`Self::with_program_account_id`] for a program deployed to a PDA (e.g. via `Deploy`). + /// Where `program` is dispatched at. Defaults to + /// `program_loader_core::immutable_deploy_account_id(program.id())`, matching how every + /// genesis-seeded builtin is actually dispatched; override via + /// [`Self::with_program_account_id`] for a program deployed to a different PDA (e.g. a + /// `Deploy` with a non-default `update_auth`). pub program_account_id: AccountId, // TODO: avoid having a copy of the bytecode of each dependency. pub dependencies: HashMap, @@ -54,7 +56,7 @@ pub struct ProgramWithDependencies { impl ProgramWithDependencies { #[must_use] pub fn new(program: Program, dependencies: HashMap) -> Self { - let program_account_id = AccountId::from(program.id()); + let program_account_id = program_loader_core::immutable_deploy_account_id(program.id()); Self { program, program_account_id, @@ -62,8 +64,8 @@ impl ProgramWithDependencies { } } - /// Overrides the address `program` is dispatched at, for a program whose address isn't - /// derived from its own image id (e.g. deployed via `Deploy` to a PDA). + /// Overrides the address `program` is dispatched at, for a program not deployed to the + /// default immutable PDA (e.g. a `Deploy` with a non-default `update_auth`). #[must_use] pub const fn with_program_account_id(mut self, program_account_id: AccountId) -> Self { self.program_account_id = program_account_id; diff --git a/lee/state_machine/src/privacy_preserving_transaction/circuit/tests.rs b/lee/state_machine/src/privacy_preserving_transaction/circuit/tests.rs index 59888323a..5592ba501 100644 --- a/lee/state_machine/src/privacy_preserving_transaction/circuit/tests.rs +++ b/lee/state_machine/src/privacy_preserving_transaction/circuit/tests.rs @@ -50,7 +50,7 @@ fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() let program = crate::test_methods::simple_balance_transfer(); let sender = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -65,14 +65,14 @@ fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() let balance_to_move: u128 = 37; let expected_sender_post = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100 - balance_to_move, nonce: Nonce::default(), data: Data::default(), }; let expected_recipient_post = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: balance_to_move, nonce: Nonce::private_account_nonce_init(&recipient_account_id), data: Data::default(), @@ -134,7 +134,7 @@ fn prove_privacy_preserving_execution_circuit_fully_private() { Account { balance: 100, nonce: sender_nonce, - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), data: Data::default(), }, true, @@ -165,13 +165,13 @@ fn prove_privacy_preserving_execution_circuit_fully_private() { let program = crate::test_methods::simple_balance_transfer(); let expected_private_account_1 = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100 - balance_to_move, nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()), ..Default::default() }; let expected_private_account_2 = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: balance_to_move, nonce: Nonce::private_account_nonce_init(&recipient_account_id), ..Default::default() @@ -317,7 +317,7 @@ fn update_note_view_tag_is_the_supplied_value() { let identifier: u128 = 99; let account_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); let account = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 1, ..Account::default() }; @@ -415,8 +415,13 @@ fn private_pda_claim_with_custom_identifier_encrypts_correct_kind() { let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); let identifier: u128 = 99; - let account_id = - AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), identifier); + let account_id = AccountId::for_private_pda( + &program.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + identifier, + ); let init_nonce = Nonce::private_account_nonce_init(&account_id); let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &init_nonce); let shared_secret = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; @@ -443,7 +448,7 @@ fn private_pda_claim_with_custom_identifier_encrypts_correct_kind() { assert_eq!( decrypt_kind(&output, &shared_secret, 0), PrivateAccountKind::Pda { - program_id: program.id(), + program_account_id: program.deployed_account_id(), seed, identifier }, @@ -461,7 +466,8 @@ fn private_pda_init() { let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); // PDA (new, private PDA) - let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 0); + let pda_id = + AccountId::for_private_pda(&program.deployed_account_id(), &seed, &npk, &keys.vpk(), 0); let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id); let auth_id = simple_transfer.deployed_account_id(); @@ -493,7 +499,8 @@ fn private_pda_withdraw() { let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); // PDA (new, private PDA) - let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 0); + let pda_id = + AccountId::for_private_pda(&program.deployed_account_id(), &seed, &npk, &keys.vpk(), 0); let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id); // Recipient (public) @@ -545,7 +552,7 @@ fn shared_account_receives_via_simple_transfer() { let sender_id = AccountId::new([99; 32]); let sender = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 1000, ..Account::default() }, @@ -685,7 +692,7 @@ fn private_authorized_update_encrypts_regular_kind_with_identifier() { ); let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let account = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 1, ..Account::default() }; @@ -730,7 +737,7 @@ fn seeded_regular_account( ) -> (AccountId, AccountWithMetadata, lee_core::MembershipProof) { let account_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); let account = Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 1, ..Account::default() }; @@ -865,7 +872,7 @@ fn regular_init_with_non_chaining_ask_npk_is_rejected() { #[test] fn unauthorized_private_init_can_be_claimed() { let program = crate::test_methods::claimer(); - let program_id = program.id(); + let program_account_id = program.deployed_account_id(); let keys = test_private_account_keys_1(); let recipient_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), 0); let recipient = AccountWithMetadata::new(Account::default(), false, recipient_id); @@ -899,7 +906,7 @@ fn unauthorized_private_init_can_be_claimed() { &output.private_actions[0].nullifier, ) .unwrap(); - assert_eq!(claimed.program_owner, program_id.into()); + assert_eq!(claimed.program_owner, program_account_id); } /// A program that asserts authorization over its pre-states rejects a regular private account @@ -943,7 +950,7 @@ fn pda_update_attempt( let seed = PdaSeed::new([42; 32]); let simple_transfer_account_id = simple_transfer.deployed_account_id(); let pda_id = AccountId::for_private_pda( - &program.id(), + &program.deployed_account_id(), &seed, &keys.npk(), &keys.vpk(), @@ -992,7 +999,7 @@ fn pda_update_attempt( /// to `PrivateAccountKind::Pda` carrying the correct `(program_id, seed, identifier)`. #[test] fn private_pda_update_encrypts_pda_kind_with_identifier() { - let program_id = crate::test_methods::pda_spend_proxy().id(); + let program_account_id = crate::test_methods::pda_spend_proxy().deployed_account_id(); let keys = test_private_account_keys_1(); let seed = PdaSeed::new([42; 32]); let identifier: u128 = 99; @@ -1000,8 +1007,13 @@ fn private_pda_update_encrypts_pda_kind_with_identifier() { let output = pda_update_attempt(false, identifier, identifier) .expect("a well-formed private PDA update must prove"); - let pda_id = - AccountId::for_private_pda(&program_id, &seed, &keys.npk(), &keys.vpk(), identifier); + let pda_id = AccountId::for_private_pda( + &program_account_id, + &seed, + &keys.npk(), + &keys.vpk(), + identifier, + ); let esk = EphemeralSecretKey::new( &pda_id, &[0; 32], @@ -1011,7 +1023,7 @@ fn private_pda_update_encrypts_pda_kind_with_identifier() { assert_eq!( decrypt_kind(&output, &ssk, 0), PrivateAccountKind::Pda { - program_id, + program_account_id, seed, identifier }, @@ -1031,7 +1043,8 @@ fn private_pda_init_identifier_mismatch_fails() { let keys = test_private_account_keys_1(); let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); - let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 5); + let account_id = + AccountId::for_private_pda(&program.deployed_account_id(), &seed, &npk, &keys.vpk(), 5); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let result = execute_and_prove( @@ -1051,8 +1064,13 @@ fn private_pda_init_at_root_call_may_not_declare_authorization() { let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); let identifier: u128 = 5; - let account_id = - AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), identifier); + let account_id = AccountId::for_private_pda( + &program.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + identifier, + ); let pre_state = AccountWithMetadata::new(Account::default(), true, account_id); let result = execute_and_prove( diff --git a/lee/state_machine/src/state/mod.rs b/lee/state_machine/src/state/mod.rs index 9fa9b315b..2a45ffd4b 100644 --- a/lee/state_machine/src/state/mod.rs +++ b/lee/state_machine/src/state/mod.rs @@ -212,10 +212,9 @@ impl V03State { let image_id = program.id(); let segment_number = 0; let update_auth = AccountId::default(); - let loader_id = ProgramId::from(RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID); let header_account_id = program_loader_core::deploy_header_account_id( - loader_id, + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, image_id, segment_number, update_auth, @@ -231,7 +230,7 @@ impl V03State { }; let segment_account_id = program_loader_core::deploy_segment_account_id( - loader_id, + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, image_id, segment_number, update_auth, @@ -352,9 +351,8 @@ impl V03State { } if account.program_owner == RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID { let header = ProgramData::try_from(&account.data).ok()?; - let loader_id = ProgramId::from(RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID); let segment_account_id = program_loader_core::deploy_segment_account_id( - loader_id, + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, header.image_id, header.segment_number, header.update_auth, diff --git a/lee/state_machine/src/state/tests/circuit.rs b/lee/state_machine/src/state/tests/circuit.rs index 768bd7f7f..16e6ef9d3 100644 --- a/lee/state_machine/src/state/tests/circuit.rs +++ b/lee/state_machine/src/state/tests/circuit.rs @@ -1,5 +1,3 @@ -use lee_core::program::ProgramId; - use super::*; #[test] @@ -7,7 +5,7 @@ fn circuit_fails_if_visibility_masks_have_incorrect_lenght() { let program = crate::test_methods::simple_balance_transfer(); let public_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -16,7 +14,7 @@ fn circuit_fails_if_visibility_masks_have_incorrect_lenght() { ); let public_account_2 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -42,7 +40,7 @@ fn circuit_fails_if_invalid_auth_keys_are_provided() { let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -102,7 +100,7 @@ fn circuit_should_fail_if_new_private_account_with_non_default_balance_is_provid let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -162,7 +160,7 @@ fn circuit_should_fail_if_new_private_account_with_non_default_program_owner_is_ let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -222,7 +220,7 @@ fn circuit_should_fail_if_new_private_account_with_non_default_data_is_provided( let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -282,7 +280,7 @@ fn circuit_should_fail_if_new_private_account_with_non_default_nonce_is_provided let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -343,7 +341,7 @@ fn circuit_should_fail_if_new_private_account_is_provided_with_default_values_bu let recipient_keys = test_private_account_keys_2(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -403,7 +401,7 @@ fn private_pda_without_binding_fails() { let keys = test_private_account_keys_1(); let public_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -438,7 +436,13 @@ fn private_pda_claim_succeeds() { let npk = keys.npk(); let seed = PdaSeed::new([42; 32]); - let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), u128::MAX); + let account_id = AccountId::for_private_pda( + &program.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + u128::MAX, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let result = execute_and_prove( @@ -468,8 +472,13 @@ fn private_pda_npk_mismatch_fails() { // `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state. // `AccountId::for_private_pda(program, seed, npk_b) != account_id`, so the claim check in // the circuit must reject. - let account_id = - AccountId::for_private_pda(&program.id(), &seed, &npk_a, &keys_a.vpk(), u128::MAX); + let account_id = AccountId::for_private_pda( + &program.deployed_account_id(), + &seed, + &npk_a, + &keys_a.vpk(), + u128::MAX, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let result = execute_and_prove( @@ -495,8 +504,13 @@ fn caller_pda_seeds_authorize_private_pda_for_callee() { let npk = keys.npk(); let seed = PdaSeed::new([77; 32]); - let account_id = - AccountId::for_private_pda(&delegator.id(), &seed, &npk, &keys.vpk(), u128::MAX); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + u128::MAX, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let callee_account_id = callee.deployed_account_id(); @@ -528,8 +542,13 @@ fn caller_pda_seeds_with_wrong_seed_rejects_private_pda_for_callee() { let claim_seed = PdaSeed::new([77; 32]); let wrong_delegated_seed = PdaSeed::new([88; 32]); - let account_id = - AccountId::for_private_pda(&delegator.id(), &claim_seed, &npk, &keys.vpk(), u128::MAX); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &claim_seed, + &npk, + &keys.vpk(), + u128::MAX, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let callee_account_id = callee.deployed_account_id(); @@ -555,14 +574,20 @@ fn sibling_declaring_delegated_pda(pda_is_authorized: bool) -> Result<(), LeeErr let npk = keys.npk(); let seed = PdaSeed::new([77; 32]); - let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, &keys.vpk(), 0); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + 0, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); - let callee_id = callee.id(); - let sibling_id = sibling.id(); + let callee_id = callee.deployed_account_id(); + let sibling_id = sibling.deployed_account_id(); let program_with_deps = ProgramWithDependencies::new( delegator, - [(callee_id.into(), callee), (sibling_id.into(), sibling)].into(), + [(callee_id, callee), (sibling_id, sibling)].into(), ); execute_and_prove( @@ -603,16 +628,22 @@ fn delegated_pda_stays_authorized_in_delegated_subtree() { let npk = keys.npk(); let seed = PdaSeed::new([77; 32]); - let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, &keys.vpk(), 0); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + 0, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); - let forwarder_id = forwarder.id(); - let callee_id = callee.id(); + let forwarder_id = forwarder.deployed_account_id(); + let callee_id = callee.deployed_account_id(); let program_with_deps = ProgramWithDependencies::new( delegator, - [(forwarder_id.into(), forwarder), (callee_id.into(), callee)].into(), + [(forwarder_id, forwarder), (callee_id, callee)].into(), ); - let no_sibling: Option<(ProgramId, Option)> = None; + let no_sibling: Option<(AccountId, Option)> = None; execute_and_prove( vec![pre_state], @@ -646,16 +677,22 @@ fn holder_authorization_survives_across_sibling_calls() { let holder_npk = holder_keys.npk(); let seed = PdaSeed::new([77; 32]); - let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, &pda_keys.vpk(), 0); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &seed, + &npk, + &pda_keys.vpk(), + 0, + ); let holder_id = AccountId::for_regular_private_account(&holder_npk, &holder_keys.vpk(), 0); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let holder_pre_state = AccountWithMetadata::new(Account::default(), true, holder_id); - let callee_id = callee.id(); - let sibling_id = sibling.id(); + let callee_id = callee.deployed_account_id(); + let sibling_id = sibling.deployed_account_id(); let program_with_deps = ProgramWithDependencies::new( delegator, - [(callee_id.into(), callee), (sibling_id.into(), sibling)].into(), + [(callee_id, callee), (sibling_id, sibling)].into(), ); execute_and_prove( @@ -697,16 +734,22 @@ fn inherited_scope_passes_through_intermediate_calls() { let npk = keys.npk(); let seed = PdaSeed::new([77; 32]); - let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, &keys.vpk(), 0); + let account_id = AccountId::for_private_pda( + &delegator.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + 0, + ); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); - let forwarder_id = forwarder.id(); - let callee_id = callee.id(); + let forwarder_id = forwarder.deployed_account_id(); + let callee_id = callee.deployed_account_id(); let program_with_deps = ProgramWithDependencies::new( delegator, - [(forwarder_id.into(), forwarder), (callee_id.into(), callee)].into(), + [(forwarder_id, forwarder), (callee_id, callee)].into(), ); - let no_sibling: Option<(ProgramId, Option)> = None; + let no_sibling: Option<(AccountId, Option)> = None; let forward_through_undeclaring_call = Program::serialize_instruction(( forwarder_id, Program::serialize_instruction(( @@ -748,13 +791,12 @@ fn undeclaring_private_delegation( let npk = keys.npk(); let seed = PdaSeed::new([77; 32]); - let delegator_id = delegator.id(); + let delegator_id = delegator.deployed_account_id(); let account_id = AccountId::for_private_pda(&delegator_id, &seed, &npk, &keys.vpk(), 0); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); - let callee_id = callee.id(); - let program_with_deps = - ProgramWithDependencies::new(delegator, [(callee_id.into(), callee)].into()); + let callee_id = callee.deployed_account_id(); + let program_with_deps = ProgramWithDependencies::new(delegator, [(callee_id, callee)].into()); execute_and_prove( vec![pre_state], @@ -763,7 +805,7 @@ fn undeclaring_private_delegation( declare_authorized, callee_id, Program::serialize_instruction(()).unwrap(), - None::, + None::, )) .unwrap(), vec![init_pda_witness( @@ -826,18 +868,18 @@ fn undeclaring_public_delegation( let pre_state = AccountWithMetadata::new( Account { - program_owner: delegator.id().into(), + program_owner: delegator.deployed_account_id(), ..Account::default() }, false, account_id, ); - let callee_id = callee.id(); - let sibling_id = sibling.id(); + let callee_id = callee.deployed_account_id(); + let sibling_id = sibling.deployed_account_id(); let program_with_deps = ProgramWithDependencies::new( delegator, - [(callee_id.into(), callee), (sibling_id.into(), sibling)].into(), + [(callee_id, callee), (sibling_id, sibling)].into(), ); execute_and_prove( @@ -859,7 +901,7 @@ fn undeclaring_public_delegation( #[test] fn delegated_public_pda_first_seen_in_callee_is_authorized() { let seed = PdaSeed::new([77; 32]); - let delegator_id = crate::test_methods::undeclaring_pda_delegator().id(); + let delegator_id = crate::test_methods::undeclaring_pda_delegator().deployed_account_id(); let pda = AccountId::for_public_pda(&delegator_id, &seed); let output = undeclaring_public_delegation( @@ -881,7 +923,7 @@ fn delegated_public_pda_first_seen_in_callee_is_authorized() { #[test] fn granted_public_pda_may_not_be_declared_unauthorized_at_first_sight() { let seed = PdaSeed::new([77; 32]); - let delegator_id = crate::test_methods::undeclaring_pda_delegator().id(); + let delegator_id = crate::test_methods::undeclaring_pda_delegator().deployed_account_id(); let pda = AccountId::for_public_pda(&delegator_id, &seed); // `noop` tolerates unauthorized pre_states, so the only rejector left is the @@ -895,7 +937,7 @@ fn granted_public_pda_may_not_be_declared_unauthorized_at_first_sight() { #[test] fn public_pda_first_sight_grant_does_not_extend_to_sibling_calls() { let seed = PdaSeed::new([77; 32]); - let delegator_id = crate::test_methods::undeclaring_pda_delegator().id(); + let delegator_id = crate::test_methods::undeclaring_pda_delegator().deployed_account_id(); let pda = AccountId::for_public_pda(&delegator_id, &seed); let result = undeclaring_public_delegation( @@ -929,7 +971,7 @@ fn public_account_first_sight_authorization_is_exported_to_the_journal() { fn wrong_seed_public_pda_first_sight_is_exported_as_credential_claim() { let seed = PdaSeed::new([77; 32]); let wrong_seed = PdaSeed::new([88; 32]); - let delegator_id = crate::test_methods::undeclaring_pda_delegator().id(); + let delegator_id = crate::test_methods::undeclaring_pda_delegator().deployed_account_id(); let pda = AccountId::for_public_pda(&delegator_id, &seed); let output = undeclaring_public_delegation( @@ -962,14 +1004,14 @@ fn two_private_pda_claims_under_same_seed_are_rejected() { let seed = PdaSeed::new([55; 32]); let account_a = AccountId::for_private_pda( - &program.id(), + &program.deployed_account_id(), &seed, &keys_a.npk(), &keys_a.vpk(), u128::MAX, ); let account_b = AccountId::for_private_pda( - &program.id(), + &program.deployed_account_id(), &seed, &keys_b.npk(), &keys_b.vpk(), @@ -1004,10 +1046,16 @@ fn private_pda_top_level_reuse_rejected_by_binding_check() { let npk = keys.npk(); let seed = PdaSeed::new([99; 32]); - let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), u128::MAX); + let account_id = AccountId::for_private_pda( + &program.deployed_account_id(), + &seed, + &npk, + &keys.vpk(), + u128::MAX, + ); let owned_pre_state = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), ..Account::default() }, false, @@ -1030,14 +1078,16 @@ fn private_accounts_can_only_be_initialized_once() { let sender_nonce = Nonce(0xdead_beef); let sender_private_account = Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: 100, nonce: sender_nonce, data: Data::default(), }; let recipient_keys = test_private_account_keys_2(); - let mut state = V03State::new().with_private_account(&sender_keys, &sender_private_account); + let mut state = V03State::new() + .with_test_programs() + .with_private_account(&sender_keys, &sender_private_account); let balance_to_move = 37; let balance_to_move_2 = 30; @@ -1055,7 +1105,7 @@ fn private_accounts_can_only_be_initialized_once() { .unwrap(); let sender_private_account = Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: 100, nonce: sender_nonce, data: Data::default(), @@ -1085,7 +1135,7 @@ fn circuit_should_fail_if_there_are_repeated_ids() { let sender_keys = test_private_account_keys_1(); let private_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -1297,7 +1347,7 @@ fn private_account_claimed_then_used_without_init_flag_should_fail() { // Prepare new state of account let account_metadata = { let mut acc = authorized_account; - acc.account.program_owner = crate::test_methods::claimer().id().into(); + acc.account.program_owner = crate::test_methods::claimer().deployed_account_id(); acc }; @@ -1333,7 +1383,7 @@ fn two_private_pda_family_members_receive_and_spend() { let proxy = crate::test_methods::pda_spend_proxy(); let simple_transfer = crate::test_methods::simple_balance_transfer(); - let proxy_id = proxy.id(); + let proxy_id = proxy.deployed_account_id(); let simple_transfer_account_id = simple_transfer.deployed_account_id(); let seed = PdaSeed::new([42; 32]); let amount: u128 = 100; diff --git a/lee/state_machine/src/state/tests/claiming.rs b/lee/state_machine/src/state/tests/claiming.rs index 218ffcfc1..df12009d8 100644 --- a/lee/state_machine/src/state/tests/claiming.rs +++ b/lee/state_machine/src/state/tests/claiming.rs @@ -185,7 +185,7 @@ fn execution_fails_if_chained_calls_exceeds_depth() { fn execution_that_requires_authentication_of_a_program_derived_account_id_succeeds() { let chain_caller = crate::test_methods::chain_caller(); let pda_seed = PdaSeed::new([37; 32]); - let from = AccountId::for_public_pda(&chain_caller.id(), &pda_seed); + let from = AccountId::for_public_pda(&chain_caller.deployed_account_id(), &pda_seed); let to = AccountId::new([2; 32]); let initial_balance = 1000; let initial_data = [(from, initial_balance), (to, 0)]; @@ -300,10 +300,10 @@ fn unauthorized_public_account_claiming_fails_when_executed_privately() { #[test] fn authorized_public_account_claiming_succeeds_when_executed_privately() { let program = crate::test_methods::simple_balance_transfer(); - let program_id = program.id(); + let program_account_id = program.deployed_account_id(); let sender_keys = test_private_account_keys_1(); let sender_private_account = Account { - program_owner: program_id.into(), + program_owner: program_account_id, balance: 100, ..Account::default() }; @@ -311,8 +311,9 @@ fn authorized_public_account_claiming_succeeds_when_executed_privately() { AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account); let sender_init_nullifier = Nullifier::for_account_initialization(&sender_account_id); - let mut state = - V03State::new().with_private_accounts([(sender_commitment, sender_init_nullifier)]); + let mut state = V03State::new() + .with_private_accounts([(sender_commitment, sender_init_nullifier)]) + .with_test_programs(); let sender_pre = AccountWithMetadata::new( sender_private_account, true, @@ -365,7 +366,7 @@ fn authorized_public_account_claiming_succeeds_when_executed_privately() { assert_eq!( state.get_account_by_id(recipient_account_id), Account { - program_owner: program_id.into(), + program_owner: program_account_id, balance, nonce: Nonce(1), ..Account::default() diff --git a/lee/state_machine/src/state/tests/flash_swap.rs b/lee/state_machine/src/state/tests/flash_swap.rs index c3efdda56..0b5965829 100644 --- a/lee/state_machine/src/state/tests/flash_swap.rs +++ b/lee/state_machine/src/state/tests/flash_swap.rs @@ -6,8 +6,10 @@ fn flash_swap_successful() { let callback = crate::test_methods::flash_swap_callback(); let token = crate::test_methods::simple_balance_transfer(); - let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); - let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); + let vault_id = + AccountId::for_public_pda(&initiator.deployed_account_id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = + AccountId::for_public_pda(&callback.deployed_account_id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; let amount_out: u128 = 100; @@ -57,8 +59,10 @@ fn flash_swap_callback_keeps_funds_rollback() { let callback = crate::test_methods::flash_swap_callback(); let token = crate::test_methods::simple_balance_transfer(); - let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); - let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); + let vault_id = + AccountId::for_public_pda(&initiator.deployed_account_id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = + AccountId::for_public_pda(&callback.deployed_account_id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; let amount_out: u128 = 100; @@ -115,8 +119,10 @@ fn flash_swap_self_call_targets_correct_program() { let callback = crate::test_methods::flash_swap_callback(); let token = crate::test_methods::simple_balance_transfer(); - let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); - let receiver_id = AccountId::for_public_pda(&callback.id(), &PdaSeed::new([1_u8; 32])); + let vault_id = + AccountId::for_public_pda(&initiator.deployed_account_id(), &PdaSeed::new([0_u8; 32])); + let receiver_id = + AccountId::for_public_pda(&callback.deployed_account_id(), &PdaSeed::new([1_u8; 32])); let initial_balance: u128 = 1000; @@ -164,7 +170,8 @@ fn flash_swap_standalone_invariant_check_rejected() { let initiator = crate::test_methods::flash_swap_initiator(); let token = crate::test_methods::simple_balance_transfer(); - let vault_id = AccountId::for_public_pda(&initiator.id(), &PdaSeed::new([0_u8; 32])); + let vault_id = + AccountId::for_public_pda(&initiator.deployed_account_id(), &PdaSeed::new([0_u8; 32])); let vault_account = Account { program_owner: token.deployed_account_id(), diff --git a/lee/state_machine/src/state/tests/mod.rs b/lee/state_machine/src/state/tests/mod.rs index 0ee32d0ab..c70f86854 100644 --- a/lee/state_machine/src/state/tests/mod.rs +++ b/lee/state_machine/src/state/tests/mod.rs @@ -14,7 +14,7 @@ use lee_core::{ encryption::ViewingPublicKey, program::{ BlockValidityWindow, ExecutionValidationError, MAX_NUMBER_CHAINED_CALLS, PdaSeed, - ProgramId, TimestampValidityWindow, WrappedBalanceSum, + TimestampValidityWindow, WrappedBalanceSum, }, }; @@ -264,7 +264,7 @@ pub fn test_private_account_keys_2() -> TestPrivateKeys { pub fn init_pda_witness( keys: &TestPrivateKeys, identifier: Identifier, - binding: Option<(ProgramId, PdaSeed)>, + binding: Option<(AccountId, PdaSeed)>, ) -> InputAccountIdentity { InputAccountIdentity::Private(PrivateWitness { vpk: keys.vpk(), @@ -448,13 +448,15 @@ fn deshielded_balance_transfer_for_tests( fn valid_private_transfer_tx_and_state() -> (V03State, PrivacyPreservingTransaction) { let sender_keys = test_private_account_keys_1(); let sender_private_account = Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: 100, nonce: Nonce(0xdead_beef), ..Account::default() }; let recipient_keys = test_private_account_keys_2(); - let state = V03State::new().with_private_account(&sender_keys, &sender_private_account); + let state = V03State::new() + .with_test_programs() + .with_private_account(&sender_keys, &sender_private_account); let tx = private_balance_transfer_for_tests( &sender_keys, &sender_private_account, diff --git a/lee/state_machine/src/state/tests/privacy_preserving.rs b/lee/state_machine/src/state/tests/privacy_preserving.rs index cfeb78738..d31efb1ad 100644 --- a/lee/state_machine/src/state/tests/privacy_preserving.rs +++ b/lee/state_machine/src/state/tests/privacy_preserving.rs @@ -5,14 +5,16 @@ fn transition_from_privacy_preserving_transaction_shielded() { let sender_keys = test_public_account_keys_1(); let recipient_keys = test_private_account_keys_1(); - let mut state = V03State::new().with_public_accounts([( - sender_keys.account_id(), - Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), - balance: 200, - ..Account::default() - }, - )]); + let mut state = V03State::new() + .with_public_accounts([( + sender_keys.account_id(), + Account { + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), + balance: 200, + ..Account::default() + }, + )]) + .with_test_programs(); let balance_to_move = 37; @@ -49,14 +51,16 @@ fn transition_from_privacy_preserving_transaction_private() { let sender_nonce = Nonce(0xdead_beef); let sender_private_account = Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: 100, nonce: sender_nonce, data: Data::default(), }; let recipient_keys = test_private_account_keys_2(); - let mut state = V03State::new().with_private_account(&sender_keys, &sender_private_account); + let mut state = V03State::new() + .with_test_programs() + .with_private_account(&sender_keys, &sender_private_account); let balance_to_move = 37; @@ -75,7 +79,7 @@ fn transition_from_privacy_preserving_transaction_private() { let expected_new_commitment_1 = Commitment::new( &sender_account_id, &Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()), balance: sender_private_account.balance - balance_to_move, data: Data::default(), @@ -89,7 +93,7 @@ fn transition_from_privacy_preserving_transaction_private() { let expected_new_commitment_2 = Commitment::new( &recipient_account_id, &Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), nonce: Nonce::private_account_nonce_init(&recipient_account_id), balance: balance_to_move, ..Account::default() @@ -171,7 +175,7 @@ fn transition_from_privacy_preserving_transaction_deshielded() { let sender_nonce = Nonce(0xdead_beef); let sender_private_account = Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: 100, nonce: sender_nonce, data: Data::default(), @@ -182,12 +186,13 @@ fn transition_from_privacy_preserving_transaction_deshielded() { .with_public_accounts([( recipient_keys.account_id(), Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), balance: recipient_initial_balance, ..Account::default() }, )]) - .with_private_account(&sender_keys, &sender_private_account); + .with_private_account(&sender_keys, &sender_private_account) + .with_test_programs(); let balance_to_move = 37; @@ -210,7 +215,7 @@ fn transition_from_privacy_preserving_transaction_deshielded() { let expected_new_commitment = Commitment::new( &sender_account_id, &Account { - program_owner: crate::test_methods::simple_balance_transfer().id().into(), + program_owner: crate::test_methods::simple_balance_transfer().deployed_account_id(), nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()), balance: sender_private_account.balance - balance_to_move, data: Data::default(), @@ -245,7 +250,7 @@ fn burner_program_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::burner(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 100, ..Account::default() }, @@ -268,7 +273,7 @@ fn minter_program_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::minter(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -291,7 +296,7 @@ fn nonce_changer_program_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::nonce_changer(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -337,7 +342,7 @@ fn data_changer_program_should_fail_for_too_large_data_in_privacy_preserving_cir let program = crate::test_methods::data_changer(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -362,7 +367,7 @@ fn extra_output_program_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::extra_output(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -385,7 +390,7 @@ fn missing_output_program_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::missing_output(); let public_account_1 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -394,7 +399,7 @@ fn missing_output_program_should_fail_in_privacy_preserving_circuit() { ); let public_account_2 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -417,7 +422,7 @@ fn program_owner_changer_should_fail_in_privacy_preserving_circuit() { let program = crate::test_methods::program_owner_changer(); let public_account = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, @@ -449,7 +454,7 @@ fn transfer_from_non_owned_account_should_fail_in_privacy_preserving_circuit() { ); let public_account_2 = AccountWithMetadata::new( Account { - program_owner: program.id().into(), + program_owner: program.deployed_account_id(), balance: 0, ..Account::default() }, diff --git a/lee/state_machine/src/validated_state_diff/mod.rs b/lee/state_machine/src/validated_state_diff/mod.rs index 7729bd37e..8c75f7ba3 100644 --- a/lee/state_machine/src/validated_state_diff/mod.rs +++ b/lee/state_machine/src/validated_state_diff/mod.rs @@ -9,7 +9,7 @@ use lee_core::{ PublicAction, Timestamp, account::{Account, AccountId, AccountWithMetadata}, program::{ - ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, ProgramId, ProgramOutput, + ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, ProgramOutput, RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, compute_public_authorized_pdas, validate_execution, }, }; @@ -100,7 +100,6 @@ impl ValidatedStateDiff { let initial_caller_data = CallerData { caller_account_id: None, - caller_image_id: None, authorized_accounts: signer_account_ids.iter().copied().collect(), }; @@ -120,15 +119,11 @@ impl ValidatedStateDiff { chained_call.pre_states, chained_call.instruction_data ); - let (program_id, mut program_output) = if chained_call.program_account_id + let mut program_output = if chained_call.program_account_id == RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID { // Runs `Deploy` as native Rust instead of interpreting a guest ELF — see - // `RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID`'s doc comment for why. The - // loader's own identity is this fixed reserved `AccountId`, unlike an - // ordinary program's, so recovering it via the bijection is exact — there's - // no separate "real image id" to look up, Deploy isn't itself upgradeable. - let program_id = ProgramId::from(RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID); + // `RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID`'s doc comment for why. // `execute_deploy` validates its input via `assert!`/`.expect(...)`, exactly // like every guest program in this codebase, relying here on `catch_unwind` to // play the same role the zkVM executor plays for a real guest: converting a @@ -139,20 +134,21 @@ impl ValidatedStateDiff { })?; let deploy_pre_states = chained_call.pre_states.clone(); let post_states = std::panic::catch_unwind(|| { - program_loader_core::execute_deploy(program_id, deploy_pre_states, bytecode) + program_loader_core::execute_deploy( + chained_call.program_account_id, + deploy_pre_states, + bytecode, + ) }) .map_err(|_panic_payload| { LeeError::ProgramExecutionFailed("Deploy rejected the given input".into()) })?; - ( - program_id, - ProgramOutput::new( - chained_call.program_account_id, - caller_data.caller_account_id, - chained_call.instruction_data.clone(), - chained_call.pre_states.clone(), - post_states, - ), + ProgramOutput::new( + chained_call.program_account_id, + caller_data.caller_account_id, + chained_call.instruction_data.clone(), + chained_call.pre_states.clone(), + post_states, ) } else { // The real `image_id`, sourced from the program's own account rather than @@ -164,15 +160,12 @@ impl ValidatedStateDiff { return Err(LeeError::InvalidInput("Unknown program".into())); }; let program = Program::new_unchecked(program_id, Cow::Owned(elf)); - ( - program_id, - program.execute( - chained_call.program_account_id, - caller_data.caller_account_id, - &chained_call.pre_states, - &chained_call.instruction_data, - )?, - ) + program.execute( + chained_call.program_account_id, + caller_data.caller_account_id, + &chained_call.pre_states, + &chained_call.instruction_data, + )? }; debug!( "Program {:?} output: {:?}", @@ -180,7 +173,7 @@ impl ValidatedStateDiff { ); let authorized_pdas = compute_public_authorized_pdas( - caller_data.caller_image_id, + caller_data.caller_account_id, &chained_call.pda_seeds, ); @@ -284,7 +277,8 @@ impl ValidatedStateDiff { // The program can only claim accounts that correspond to the PDAs it is // authorized to claim. The public-execution path only sees public // accounts, so the public-PDA derivation is the correct formula here. - let pda = AccountId::for_public_pda(&program_id, &seed); + let pda = + AccountId::for_public_pda(&chained_call.program_account_id, &seed); ensure!( account_id == pda, InvalidProgramBehaviorError::MismatchedPdaClaim { @@ -328,7 +322,6 @@ impl ValidatedStateDiff { new_call, CallerData { caller_account_id: Some(chained_call.program_account_id), - caller_image_id: Some(program_id), authorized_accounts: authorized_accounts.clone(), }, )); @@ -519,11 +512,6 @@ impl ValidatedStateDiff { #[derive(Debug)] struct CallerData { caller_account_id: Option, - /// The caller's real `image_id`, recovered when the caller itself was dispatched (see - /// `V03State::get_program`) rather than guessed from `caller_account_id` via the bijection — - /// needed wherever PDA derivation requires the caller's actual identity, since a - /// `Deploy`-created caller's address doesn't encode it. - caller_image_id: Option, authorized_accounts: HashSet, } diff --git a/lee/state_machine/test_methods/guest/src/bin/non_delegating_forwarder.rs b/lee/state_machine/test_methods/guest/src/bin/non_delegating_forwarder.rs index 21d62718f..bf6835f10 100644 --- a/lee/state_machine/test_methods/guest/src/bin/non_delegating_forwarder.rs +++ b/lee/state_machine/test_methods/guest/src/bin/non_delegating_forwarder.rs @@ -1,9 +1,12 @@ -use lee_core::program::{ - AccountPostState, ChainedCall, InstructionData, ProgramId, ProgramInput, ProgramOutput, - read_lee_inputs, +use lee_core::{ + account::AccountId, + program::{ + AccountPostState, ChainedCall, InstructionData, ProgramInput, ProgramOutput, + read_lee_inputs, + }, }; -type Instruction = (ProgramId, InstructionData, bool); +type Instruction = (AccountId, InstructionData, bool); fn main() { let ( @@ -11,7 +14,7 @@ fn main() { self_account_id, caller_account_id, pre_states, - instruction: (callee_program_id, callee_instruction, declare_pre_states), + instruction: (callee_account_id, callee_instruction, declare_pre_states), }, instruction_words, ) = read_lee_inputs::(); @@ -36,7 +39,7 @@ fn main() { output_post_states, ) .with_chained_calls(vec![ChainedCall { - program_account_id: callee_program_id.into(), + program_account_id: callee_account_id, instruction_data: callee_instruction, pre_states, pda_seeds: vec![], diff --git a/lee/state_machine/test_methods/guest/src/bin/selective_pda_delegator.rs b/lee/state_machine/test_methods/guest/src/bin/selective_pda_delegator.rs index 59c3b773c..da148bc94 100644 --- a/lee/state_machine/test_methods/guest/src/bin/selective_pda_delegator.rs +++ b/lee/state_machine/test_methods/guest/src/bin/selective_pda_delegator.rs @@ -1,15 +1,18 @@ -use lee_core::program::{ - AccountPostState, ChainedCall, Claim, InstructionData, PdaSeed, ProgramId, ProgramInput, - ProgramOutput, read_lee_inputs, +use lee_core::{ + account::AccountId, + program::{ + AccountPostState, ChainedCall, Claim, InstructionData, PdaSeed, ProgramInput, + ProgramOutput, read_lee_inputs, + }, }; use risc0_zkvm::serde::to_vec; type Instruction = ( PdaSeed, PdaSeed, - ProgramId, + AccountId, InstructionData, - Option<(ProgramId, Option)>, + Option<(AccountId, Option)>, ); fn main() { @@ -19,7 +22,7 @@ fn main() { caller_account_id, pre_states, instruction: - (claim_seed, delegated_seed, callee_program_id, callee_instruction, sibling), + (claim_seed, delegated_seed, callee_account_id, callee_instruction, sibling), }, instruction_words, ) = read_lee_inputs::(); @@ -39,7 +42,7 @@ fn main() { // but authorized first PDA supplied. // Push all the delegated seeds. let mut chained_calls = vec![ChainedCall { - program_account_id: callee_program_id.into(), + program_account_id: callee_account_id, instruction_data: callee_instruction, pre_states: std::iter::once(pda_for_callee(true)) .chain(rest.iter().cloned()) @@ -50,9 +53,9 @@ fn main() { // If sibling is present in instruction, send out a call // with no seeds so that PDAs stay unauthorized in parallel // branches. - if let Some((sibling_program_id, sibling_pda)) = sibling { + if let Some((sibling_account_id, sibling_pda)) = sibling { chained_calls.push(ChainedCall { - program_account_id: sibling_program_id.into(), + program_account_id: sibling_account_id, instruction_data: to_vec(&()).unwrap(), pre_states: sibling_pda.map_or_else( || rest.to_vec(), diff --git a/lee/state_machine/test_methods/guest/src/bin/undeclaring_pda_delegator.rs b/lee/state_machine/test_methods/guest/src/bin/undeclaring_pda_delegator.rs index 9033ecdd6..660c02b65 100644 --- a/lee/state_machine/test_methods/guest/src/bin/undeclaring_pda_delegator.rs +++ b/lee/state_machine/test_methods/guest/src/bin/undeclaring_pda_delegator.rs @@ -1,14 +1,17 @@ -use lee_core::program::{ - ChainedCall, InstructionData, PdaSeed, ProgramId, ProgramInput, ProgramOutput, read_lee_inputs, +use lee_core::{ + account::AccountId, + program::{ + ChainedCall, InstructionData, PdaSeed, ProgramInput, ProgramOutput, read_lee_inputs, + }, }; use risc0_zkvm::serde::to_vec; type Instruction = ( Option, bool, - ProgramId, + AccountId, InstructionData, - Option, + Option, ); fn main() { @@ -17,7 +20,7 @@ fn main() { self_account_id, caller_account_id, mut pre_states, - instruction: (seed, declare_authorized, callee_program_id, callee_instruction, sibling), + instruction: (seed, declare_authorized, callee_account_id, callee_instruction, sibling), }, instruction_words, ) = read_lee_inputs::(); @@ -27,11 +30,11 @@ fn main() { }; first.is_authorized = declare_authorized; - let sibling_call = sibling.map(|sibling_program_id| { + let sibling_call = sibling.map(|sibling_account_id| { let mut sibling_pre = pre_states[0].clone(); sibling_pre.is_authorized = true; ChainedCall { - program_account_id: sibling_program_id.into(), + program_account_id: sibling_account_id, instruction_data: to_vec(&()).unwrap(), pre_states: vec![sibling_pre], pda_seeds: vec![], @@ -39,7 +42,7 @@ fn main() { }); let mut chained_calls = vec![ChainedCall { - program_account_id: callee_program_id.into(), + program_account_id: callee_account_id, instruction_data: callee_instruction, pre_states, pda_seeds: seed.into_iter().collect(), diff --git a/lez/cross_zone/src/lib.rs b/lez/cross_zone/src/lib.rs index 336f54e30..6e73ec048 100644 --- a/lez/cross_zone/src/lib.rs +++ b/lez/cross_zone/src/lib.rs @@ -127,9 +127,9 @@ fn build_inbox_dispatch_tx( ) -> lee::PublicTransaction { let inbox_account_id = program_loader_core::immutable_deploy_account_id(inbox_id); let mut account_ids = Vec::with_capacity(target_account_ids.len().saturating_add(3)); - account_ids.push(inbox_config_account_id(inbox_id)); + account_ids.push(inbox_config_account_id(inbox_account_id)); account_ids.push(inbox_seen_shard_account_id( - inbox_id, + inbox_account_id, &msg.src_zone, msg.src_block_id, )); @@ -149,7 +149,6 @@ fn build_inbox_dispatch_tx( vec![], Instruction::Dispatch { message: msg.clone(), - self_program_id: inbox_id, }, ) .expect("inbox dispatch instruction must serialize"); @@ -199,12 +198,12 @@ pub fn build_dispatch_from_emission( #[must_use] pub fn build_inbox_init_config_tx(self_zone: ZoneId) -> lee::PublicTransaction { let inbox_id = programs::cross_zone_inbox().id(); + let inbox_account_id = program_loader_core::immutable_deploy_account_id(inbox_id); genesis_public_tx( inbox_id, - vec![inbox_config_account_id(inbox_id)], + vec![inbox_config_account_id(inbox_account_id)], Instruction::InitConfig { config: InboxConfig { self_zone }, - self_program_id: inbox_id, }, ) } @@ -282,17 +281,22 @@ pub fn build_wrapped_token_init_config_tx( cross_zone: Option<&CrossZoneConfig>, ) -> lee::PublicTransaction { let wrapped_token_id = programs::wrapped_token().id(); + let wrapped_token_account_id = + program_loader_core::immutable_deploy_account_id(wrapped_token_id); let sources = sources_for_target(cross_zone, wrapped_token_id); genesis_public_tx( wrapped_token_id, - vec![wrapped_token_core::config_account_id(wrapped_token_id)], + vec![wrapped_token_core::config_account_id( + wrapped_token_account_id, + )], wrapped_token_core::Instruction::InitConfig { - self_program_id: wrapped_token_id, config: wrapped_token_core::WrappedTokenConfig { minter: program_loader_core::immutable_deploy_account_id( programs::cross_zone_inbox().id(), ), - governance: cross_zone.and_then(|cross_zone| cross_zone.source_governance), + governance: cross_zone + .and_then(|cross_zone| cross_zone.source_governance) + .map(program_loader_core::immutable_deploy_account_id), authority: cross_zone.and_then(|cross_zone| cross_zone.source_authority), sources, }, @@ -305,14 +309,13 @@ pub fn build_wrapped_token_init_config_tx( #[must_use] pub fn build_ping_sender_init_config_tx() -> lee::PublicTransaction { let ping_sender_id = programs::ping_sender().id(); + let ping_sender_account_id = program_loader_core::immutable_deploy_account_id(ping_sender_id); let outbox_id = programs::cross_zone_outbox().id(); genesis_public_tx( ping_sender_id, - vec![ping_core::sender_config_account_id(ping_sender_id)], + vec![ping_core::sender_config_account_id(ping_sender_account_id)], ping_core::SenderInstruction::InitConfig { - self_program_id: ping_sender_id, outbox_account_id: program_loader_core::immutable_deploy_account_id(outbox_id), - outbox_program_id: outbox_id, }, ) } @@ -322,14 +325,13 @@ pub fn build_ping_sender_init_config_tx() -> lee::PublicTransaction { #[must_use] pub fn build_bridge_lock_init_config_tx() -> lee::PublicTransaction { let bridge_lock_id = programs::bridge_lock().id(); + let bridge_lock_account_id = program_loader_core::immutable_deploy_account_id(bridge_lock_id); let outbox_id = programs::cross_zone_outbox().id(); genesis_public_tx( bridge_lock_id, - vec![bridge_lock_core::config_account_id(bridge_lock_id)], + vec![bridge_lock_core::config_account_id(bridge_lock_account_id)], bridge_lock_core::Instruction::InitConfig { - self_program_id: bridge_lock_id, outbox_account_id: program_loader_core::immutable_deploy_account_id(outbox_id), - outbox_program_id: outbox_id, target_program_id: programs::wrapped_token().id(), }, ) @@ -343,17 +345,19 @@ pub fn build_ping_receiver_init_config_tx( cross_zone: Option<&CrossZoneConfig>, ) -> lee::PublicTransaction { let receiver_id = programs::ping_receiver().id(); + let receiver_account_id = program_loader_core::immutable_deploy_account_id(receiver_id); let sources = sources_for_target(cross_zone, receiver_id); genesis_public_tx( receiver_id, - vec![ping_core::receiver_config_account_id(receiver_id)], + vec![ping_core::receiver_config_account_id(receiver_account_id)], ping_core::ReceiverInstruction::InitConfig { - self_program_id: receiver_id, config: ping_core::ReceiverConfig { deliverer: program_loader_core::immutable_deploy_account_id( programs::cross_zone_inbox().id(), ), - governance: cross_zone.and_then(|cross_zone| cross_zone.source_governance), + governance: cross_zone + .and_then(|cross_zone| cross_zone.source_governance) + .map(program_loader_core::immutable_deploy_account_id), authority: cross_zone.and_then(|cross_zone| cross_zone.source_authority), sources, }, diff --git a/lez/cross_zone/src/test_utils.rs b/lez/cross_zone/src/test_utils.rs index cdb637999..49b68e191 100644 --- a/lez/cross_zone/src/test_utils.rs +++ b/lez/cross_zone/src/test_utils.rs @@ -31,14 +31,13 @@ pub fn ping_emission( target_program_id: ProgramId, payload: &[u8], ) -> LeeTransaction { - let receiver_id = programs::ping_receiver().id(); + let receiver_account_id = programs::ping_receiver().deployed_account_id(); let send = SenderInstruction::Send { - self_program_id: programs::ping_sender().id(), target_zone, target_program_id, target_accounts: vec![ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload: payload.to_vec(), ordinal: 0, diff --git a/lez/indexer/core/src/cross_zone_verifier.rs b/lez/indexer/core/src/cross_zone_verifier.rs index 6237b5df6..b001b1ddb 100644 --- a/lez/indexer/core/src/cross_zone_verifier.rs +++ b/lez/indexer/core/src/cross_zone_verifier.rs @@ -1448,6 +1448,7 @@ mod tests { fn dispatch_naming_block_hash(payload: &[u8], src_block_hash: [u8; 32]) -> LeeTransaction { let receiver_id = programs::ping_receiver().id(); + let receiver_account_id = programs::ping_receiver().deployed_account_id(); LeeTransaction::Public(build_dispatch_from_emission( &EmissionSource { src_zone: PEER_ZONE, @@ -1460,8 +1461,8 @@ mod tests { }, receiver_id, &[ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload.to_vec(), )) diff --git a/lez/indexer/core/src/lib.rs b/lez/indexer/core/src/lib.rs index de1f859e8..60799eab4 100644 --- a/lez/indexer/core/src/lib.rs +++ b/lez/indexer/core/src/lib.rs @@ -764,6 +764,7 @@ mod tests { /// in these tests carry a decodable dispatch key. fn dispatch_tx() -> common::transaction::LeeTransaction { let receiver_id = programs::ping_receiver().id(); + let receiver_account_id = programs::ping_receiver().deployed_account_id(); common::transaction::LeeTransaction::Public(cross_zone::build_dispatch_from_emission( &cross_zone::EmissionSource { src_zone: [2; 32], @@ -776,8 +777,8 @@ mod tests { }, receiver_id, &[ - ping_core::receiver_config_account_id(receiver_id).into_value(), - ping_core::ping_record_pda(receiver_id).into_value(), + ping_core::receiver_config_account_id(receiver_account_id).into_value(), + ping_core::ping_record_pda(receiver_account_id).into_value(), ], b"hi".to_vec(), )) diff --git a/lez/programs/amm/core/src/lib.rs b/lez/programs/amm/core/src/lib.rs index 54f93ca4a..2ba1b17e7 100644 --- a/lez/programs/amm/core/src/lib.rs +++ b/lez/programs/amm/core/src/lib.rs @@ -3,7 +3,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use lee_core::{ account::{AccountId, Data}, - program::{PdaSeed, ProgramId}, + program::PdaSeed, }; use serde::{Deserialize, Serialize}; @@ -23,7 +23,6 @@ pub enum Instruction { NewDefinition { token_a_amount: u128, token_b_amount: u128, - amm_program_id: ProgramId, }, /// Adds liquidity to the Pool. @@ -131,12 +130,12 @@ impl From<&PoolDefinition> for Data { #[must_use] pub fn compute_pool_pda( - amm_program_id: ProgramId, + amm_account_id: AccountId, definition_token_a_id: AccountId, definition_token_b_id: AccountId, ) -> AccountId { AccountId::for_public_pda( - &amm_program_id, + &amm_account_id, &compute_pool_pda_seed(definition_token_a_id, definition_token_b_id), ) } @@ -171,12 +170,12 @@ pub fn compute_pool_pda_seed( #[must_use] pub fn compute_vault_pda( - amm_program_id: ProgramId, + amm_account_id: AccountId, pool_id: AccountId, definition_token_id: AccountId, ) -> AccountId { AccountId::for_public_pda( - &amm_program_id, + &amm_account_id, &compute_vault_pda_seed(pool_id, definition_token_id), ) } @@ -198,8 +197,8 @@ pub fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId } #[must_use] -pub fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId) -> AccountId { - AccountId::for_public_pda(&amm_program_id, &compute_liquidity_token_pda_seed(pool_id)) +pub fn compute_liquidity_token_pda(amm_account_id: AccountId, pool_id: AccountId) -> AccountId { + AccountId::for_public_pda(&amm_account_id, &compute_liquidity_token_pda_seed(pool_id)) } #[must_use] diff --git a/lez/programs/amm/src/main.rs b/lez/programs/amm/src/main.rs index df5a6d4e4..6c5209486 100644 --- a/lez/programs/amm/src/main.rs +++ b/lez/programs/amm/src/main.rs @@ -28,7 +28,6 @@ fn main() { Instruction::NewDefinition { token_a_amount, token_b_amount, - amm_program_id, } => { let [ pool, @@ -51,7 +50,7 @@ fn main() { user_holding_lp, NonZero::new(token_a_amount).expect("Token A should have a nonzero amount"), NonZero::new(token_b_amount).expect("Token B should have a nonzero amount"), - amm_program_id, + self_account_id, ) } Instruction::AddLiquidity { diff --git a/lez/programs/amm/src/new_definition.rs b/lez/programs/amm/src/new_definition.rs index a6111967e..31962feb6 100644 --- a/lez/programs/amm/src/new_definition.rs +++ b/lez/programs/amm/src/new_definition.rs @@ -5,8 +5,8 @@ use amm_core::{ compute_pool_pda, compute_pool_pda_seed, compute_vault_pda, compute_vault_pda_seed, }; use lee_core::{ - account::{Account, AccountWithMetadata, Data}, - program::{AccountPostState, ChainedCall, Claim, ProgramId}, + account::{Account, AccountId, AccountWithMetadata, Data}, + program::{AccountPostState, ChainedCall, Claim}, }; #[expect(clippy::too_many_arguments, reason = "TODO: Fix later")] @@ -21,7 +21,7 @@ pub fn new_definition( user_holding_lp: AccountWithMetadata, token_a_amount: NonZeroU128, token_b_amount: NonZeroU128, - amm_program_id: ProgramId, + amm_account_id: AccountId, ) -> (Vec, Vec) { // Verify token_a and token_b are different let definition_token_a_id = token_core::TokenHolding::try_from(&user_holding_a.account.data) @@ -44,22 +44,22 @@ pub fn new_definition( ); assert_eq!( pool.account_id, - compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id), + compute_pool_pda(amm_account_id, definition_token_a_id, definition_token_b_id), "Pool Definition Account ID does not match PDA" ); assert_eq!( vault_a.account_id, - compute_vault_pda(amm_program_id, pool.account_id, definition_token_a_id), + compute_vault_pda(amm_account_id, pool.account_id, definition_token_a_id), "Vault ID does not match PDA" ); assert_eq!( vault_b.account_id, - compute_vault_pda(amm_program_id, pool.account_id, definition_token_b_id), + compute_vault_pda(amm_account_id, pool.account_id, definition_token_b_id), "Vault ID does not match PDA" ); assert_eq!( pool_definition_lp.account_id, - compute_liquidity_token_pda(amm_program_id, pool.account_id), + compute_liquidity_token_pda(amm_account_id, pool.account_id), "Liquidity pool Token Definition Account ID does not match PDA" ); diff --git a/lez/programs/amm/src/tests.rs b/lez/programs/amm/src/tests.rs index c86a6526d..54ea57258 100644 --- a/lez/programs/amm/src/tests.rs +++ b/lez/programs/amm/src/tests.rs @@ -19,7 +19,7 @@ use crate::{ }; const TOKEN_PROGRAM_ID: ProgramId = [15; 8]; -const AMM_PROGRAM_ID: ProgramId = [42; 8]; +const AMM_PROGRAM_ID: AccountId = AccountId::new([42; 32]); struct BalanceForTests; struct ChainedCallForTests; @@ -1293,14 +1293,17 @@ impl BalanceForExeTests { impl IdForExeTests { fn pool_definition_id() -> AccountId { amm_core::compute_pool_pda( - programs::amm().id(), + programs::amm().deployed_account_id(), Self::token_a_definition_id(), Self::token_b_definition_id(), ) } fn token_lp_definition_id() -> AccountId { - amm_core::compute_liquidity_token_pda(programs::amm().id(), Self::pool_definition_id()) + amm_core::compute_liquidity_token_pda( + programs::amm().deployed_account_id(), + Self::pool_definition_id(), + ) } fn token_a_definition_id() -> AccountId { @@ -1331,7 +1334,7 @@ impl IdForExeTests { fn vault_a_id() -> AccountId { amm_core::compute_vault_pda( - programs::amm().id(), + programs::amm().deployed_account_id(), Self::pool_definition_id(), Self::token_a_definition_id(), ) @@ -1339,7 +1342,7 @@ impl IdForExeTests { fn vault_b_id() -> AccountId { amm_core::compute_vault_pda( - programs::amm().id(), + programs::amm().deployed_account_id(), Self::pool_definition_id(), Self::token_b_definition_id(), ) @@ -3186,7 +3189,6 @@ fn simple_amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() { let instruction = amm_core::Instruction::NewDefinition { token_a_amount: BalanceForExeTests::vault_a_balance_init(), token_b_amount: BalanceForExeTests::vault_b_balance_init(), - amm_program_id: programs::amm().id(), }; let message = public_transaction::Message::try_new( @@ -3271,7 +3273,6 @@ fn simple_amm_new_definition_inactive_initialized_pool_init_user_lp() { let instruction = amm_core::Instruction::NewDefinition { token_a_amount: BalanceForExeTests::vault_a_balance_init(), token_b_amount: BalanceForExeTests::vault_b_balance_init(), - amm_program_id: programs::amm().id(), }; let message = public_transaction::Message::try_new( @@ -3343,7 +3344,6 @@ fn simple_amm_new_definition_uninitialized_pool() { let instruction = amm_core::Instruction::NewDefinition { token_a_amount: BalanceForExeTests::vault_a_balance_init(), token_b_amount: BalanceForExeTests::vault_b_balance_init(), - amm_program_id: programs::amm().id(), }; let message = public_transaction::Message::try_new( diff --git a/lez/programs/associated_token_account/core/src/lib.rs b/lez/programs/associated_token_account/core/src/lib.rs index 95dfe5168..fec70beec 100644 --- a/lez/programs/associated_token_account/core/src/lib.rs +++ b/lez/programs/associated_token_account/core/src/lib.rs @@ -1,8 +1,5 @@ +use lee_core::account::{AccountId, AccountWithMetadata}; pub use lee_core::program::PdaSeed; -use lee_core::{ - account::{AccountId, AccountWithMetadata}, - program::ProgramId, -}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -16,7 +13,7 @@ pub enum Instruction { /// - Associated token account (default/uninitialized, or already initialized) /// /// `token_program_id` is derived from `token_definition.account.program_owner`. - Create { ata_program_id: ProgramId }, + Create, /// Transfer tokens FROM owner's ATA to a recipient holding account. /// Uses PDA seeds to authorize the ATA in the chained Token::Transfer call. @@ -27,10 +24,7 @@ pub enum Instruction { /// - Recipient token holding (any account; auto-created if default) /// /// `token_program_id` is derived from `sender_ata.account.program_owner`. - Transfer { - ata_program_id: ProgramId, - amount: u128, - }, + Transfer { amount: u128 }, /// Burn tokens FROM owner's ATA. /// Uses PDA seeds to authorize the ATA in the chained Token::Burn call. @@ -41,10 +35,7 @@ pub enum Instruction { /// - Token definition account /// /// `token_program_id` is derived from `holder_ata.account.program_owner`. - Burn { - ata_program_id: ProgramId, - amount: u128, - }, + Burn { amount: u128 }, } pub fn compute_ata_seed(owner_id: AccountId, definition_id: AccountId) -> PdaSeed { @@ -60,7 +51,7 @@ pub fn compute_ata_seed(owner_id: AccountId, definition_id: AccountId) -> PdaSee ) } -pub fn get_associated_token_account_id(ata_program_id: &ProgramId, seed: &PdaSeed) -> AccountId { +pub fn get_associated_token_account_id(ata_program_id: &AccountId, seed: &PdaSeed) -> AccountId { AccountId::for_public_pda(ata_program_id, seed) } @@ -70,7 +61,7 @@ pub fn verify_ata_and_get_seed( ata_account: &AccountWithMetadata, owner: &AccountWithMetadata, definition_id: AccountId, - ata_program_id: ProgramId, + ata_program_id: AccountId, ) -> PdaSeed { let seed = compute_ata_seed(owner.account_id, definition_id); let expected_id = get_associated_token_account_id(&ata_program_id, &seed); diff --git a/lez/programs/associated_token_account/src/burn.rs b/lez/programs/associated_token_account/src/burn.rs index 09d1645a1..cab33fcb0 100644 --- a/lez/programs/associated_token_account/src/burn.rs +++ b/lez/programs/associated_token_account/src/burn.rs @@ -1,6 +1,6 @@ use lee_core::{ - account::AccountWithMetadata, - program::{AccountPostState, ChainedCall, ProgramId}, + account::{AccountId, AccountWithMetadata}, + program::{AccountPostState, ChainedCall}, }; use token_core::TokenHolding; @@ -8,7 +8,7 @@ pub fn burn_from_associated_token_account( owner: AccountWithMetadata, holder_ata: AccountWithMetadata, token_definition: AccountWithMetadata, - ata_program_id: ProgramId, + ata_program_id: AccountId, amount: u128, ) -> (Vec, Vec) { let token_program_id = holder_ata.account.program_owner; diff --git a/lez/programs/associated_token_account/src/create.rs b/lez/programs/associated_token_account/src/create.rs index 4e1b2074d..81a77ab45 100644 --- a/lez/programs/associated_token_account/src/create.rs +++ b/lez/programs/associated_token_account/src/create.rs @@ -1,13 +1,13 @@ use lee_core::{ - account::{Account, AccountWithMetadata}, - program::{AccountPostState, ChainedCall, Claim, ProgramId}, + account::{Account, AccountId, AccountWithMetadata}, + program::{AccountPostState, ChainedCall, Claim}, }; pub fn create_associated_token_account( owner: AccountWithMetadata, token_definition: AccountWithMetadata, ata_account: AccountWithMetadata, - ata_program_id: ProgramId, + ata_program_id: AccountId, ) -> (Vec, Vec) { // No authorization check needed: create is idempotent, so anyone can call it safely. let token_program_id = token_definition.account.program_owner; diff --git a/lez/programs/associated_token_account/src/main.rs b/lez/programs/associated_token_account/src/main.rs index 7797242c6..00da8c293 100644 --- a/lez/programs/associated_token_account/src/main.rs +++ b/lez/programs/associated_token_account/src/main.rs @@ -15,7 +15,7 @@ fn main() { let pre_states_clone = pre_states.clone(); let (post_states, chained_calls) = match instruction { - Instruction::Create { ata_program_id } => { + Instruction::Create => { let [owner, token_definition, ata_account] = pre_states .try_into() .expect("Create instruction requires exactly three accounts"); @@ -23,13 +23,10 @@ fn main() { owner, token_definition, ata_account, - ata_program_id, + self_account_id, ) } - Instruction::Transfer { - ata_program_id, - amount, - } => { + Instruction::Transfer { amount } => { let [owner, sender_ata, recipient] = pre_states .try_into() .expect("Transfer instruction requires exactly three accounts"); @@ -37,14 +34,11 @@ fn main() { owner, sender_ata, recipient, - ata_program_id, + self_account_id, amount, ) } - Instruction::Burn { - ata_program_id, - amount, - } => { + Instruction::Burn { amount } => { let [owner, holder_ata, token_definition] = pre_states .try_into() .expect("Burn instruction requires exactly three accounts"); @@ -52,7 +46,7 @@ fn main() { owner, holder_ata, token_definition, - ata_program_id, + self_account_id, amount, ) } diff --git a/lez/programs/associated_token_account/src/tests.rs b/lez/programs/associated_token_account/src/tests.rs index 749cff417..3bfdfb2c7 100644 --- a/lez/programs/associated_token_account/src/tests.rs +++ b/lez/programs/associated_token_account/src/tests.rs @@ -4,7 +4,7 @@ use associated_token_account_core::{compute_ata_seed, get_associated_token_accou use lee_core::account::{Account, AccountId, AccountWithMetadata, Data}; use token_core::{TokenDefinition, TokenHolding}; -const ATA_PROGRAM_ID: lee_core::program::ProgramId = [1u32; 8]; +const ATA_PROGRAM_ID: AccountId = AccountId::new([1u8; 32]); const TOKEN_PROGRAM_ID: lee_core::program::ProgramId = [2u32; 8]; fn owner_id() -> AccountId { diff --git a/lez/programs/associated_token_account/src/transfer.rs b/lez/programs/associated_token_account/src/transfer.rs index dbe388038..ca8786fb0 100644 --- a/lez/programs/associated_token_account/src/transfer.rs +++ b/lez/programs/associated_token_account/src/transfer.rs @@ -1,6 +1,6 @@ use lee_core::{ - account::AccountWithMetadata, - program::{AccountPostState, ChainedCall, ProgramId}, + account::{AccountId, AccountWithMetadata}, + program::{AccountPostState, ChainedCall}, }; use token_core::TokenHolding; @@ -8,7 +8,7 @@ pub fn transfer_from_associated_token_account( owner: AccountWithMetadata, sender_ata: AccountWithMetadata, recipient: AccountWithMetadata, - ata_program_id: ProgramId, + ata_program_id: AccountId, amount: u128, ) -> (Vec, Vec) { let token_program_id = sender_ata.account.program_owner; diff --git a/lez/programs/bridge/core/src/lib.rs b/lez/programs/bridge/core/src/lib.rs index 828a623c0..e6d6506e9 100644 --- a/lez/programs/bridge/core/src/lib.rs +++ b/lez/programs/bridge/core/src/lib.rs @@ -1,5 +1,5 @@ +use lee_core::account::AccountId; pub use lee_core::program::PdaSeed; -use lee_core::{account::AccountId, program::ProgramId}; use serde::{Deserialize, Serialize}; const BRIDGE_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/BridgeSeed/0000000000/"; @@ -20,14 +20,8 @@ pub enum Instruction { /// Deposit OP ID from L1, stored here to pin each [`Deposit`](Instruction::Deposit) to a /// Deposit Event on L1. l1_deposit_op_id: [u8; 32], - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the bridge and receipt PDAs; a wrong value only fails - /// the guest's own self-consistency assertions, since real authorization is - /// independently enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, - /// The vault program's own image id, used to derive the expected recipient vault PDA. - vault_program_id: ProgramId, - /// The vault program's real dispatch address, used as the chained-call target. + /// The vault program's real dispatch address, used both to derive the expected recipient + /// vault PDA and as the chained-call target. vault_account_id: AccountId, recipient_id: AccountId, amount: u64, @@ -53,8 +47,8 @@ pub const fn compute_bridge_seed() -> PdaSeed { } #[must_use] -pub fn compute_bridge_account_id(bridge_program_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&bridge_program_id, &compute_bridge_seed()) +pub fn compute_bridge_account_id(bridge_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&bridge_account_id, &compute_bridge_seed()) } /// Seed of the deposit-receipt PDA for `l1_deposit_op_id`, exposed so the guest @@ -77,17 +71,17 @@ pub fn deposit_receipt_seed(l1_deposit_op_id: [u8; 32]) -> PdaSeed { /// The deposit-receipt PDA whose existence marks `l1_deposit_op_id` as minted. #[must_use] pub fn deposit_receipt_account_id( - bridge_program_id: ProgramId, + bridge_account_id: AccountId, l1_deposit_op_id: [u8; 32], ) -> AccountId { - AccountId::for_public_pda(&bridge_program_id, &deposit_receipt_seed(l1_deposit_op_id)) + AccountId::for_public_pda(&bridge_account_id, &deposit_receipt_seed(l1_deposit_op_id)) } #[cfg(test)] mod tests { use super::*; - const BRIDGE_ID: ProgramId = [7; 8]; + const BRIDGE_ID: AccountId = AccountId::new([7; 32]); #[test] fn receipt_id_is_deterministic_per_op_id() { diff --git a/lez/programs/bridge/src/main.rs b/lez/programs/bridge/src/main.rs index 7cfe530c4..fe7268a74 100644 --- a/lez/programs/bridge/src/main.rs +++ b/lez/programs/bridge/src/main.rs @@ -34,8 +34,6 @@ fn main() { let (post_states, chained_calls) = match instruction { Instruction::Deposit { l1_deposit_op_id, - self_program_id, - vault_program_id, vault_account_id, recipient_id, amount, @@ -46,19 +44,19 @@ fn main() { assert_eq!( bridge.account_id, - bridge_core::compute_bridge_account_id(self_program_id), + bridge_core::compute_bridge_account_id(self_account_id), "First account must be bridge PDA" ); assert_eq!( recipient_vault.account_id, - vault_core::compute_vault_account_id(vault_program_id, recipient_id), + vault_core::compute_vault_account_id(vault_account_id, recipient_id), "Second account must be recipient vault PDA" ); assert_eq!( receipt.account_id, - bridge_core::deposit_receipt_account_id(self_program_id, l1_deposit_op_id), + bridge_core::deposit_receipt_account_id(self_account_id, l1_deposit_op_id), "Third account must be the deposit-receipt PDA" ); @@ -117,7 +115,7 @@ fn main() { // assert_eq!( // bridge.account_id, - // bridge_core::compute_bridge_account_id(self_account_id.into()), + // bridge_core::compute_bridge_account_id(self_account_id), // "Second account must be bridge PDA" // ); diff --git a/lez/programs/bridge_lock/Cargo.toml b/lez/programs/bridge_lock/Cargo.toml index 8a4d6c20b..93d2bb1d6 100644 --- a/lez/programs/bridge_lock/Cargo.toml +++ b/lez/programs/bridge_lock/Cargo.toml @@ -11,5 +11,6 @@ workspace = true lee_core.workspace = true bridge_lock_core.workspace = true cross_zone_outbox_core.workspace = true +program_loader_core.workspace = true wrapped_token_core.workspace = true risc0-zkvm.workspace = true diff --git a/lez/programs/bridge_lock/core/src/lib.rs b/lez/programs/bridge_lock/core/src/lib.rs index edb06f4dc..acb67b33a 100644 --- a/lez/programs/bridge_lock/core/src/lib.rs +++ b/lez/programs/bridge_lock/core/src/lib.rs @@ -28,11 +28,6 @@ pub enum Instruction { /// Required accounts (4): config PDA, holder holding (authorized), escrow /// PDA, outbox PDA. Lock { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the config and escrow PDAs; a wrong value only fails - /// the guest's own self-consistency assertions, since real authorization is - /// independently enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, amount: u128, target_zone: [u8; 32], target_program_id: ProgramId, @@ -46,21 +41,16 @@ pub enum Instruction { /// /// Required accounts (1): the config PDA. InitConfig { - /// See [`Lock::self_program_id`](Instruction::Lock). - self_program_id: ProgramId, /// The outbox program's real dispatch address. outbox_account_id: AccountId, - /// The outbox program's own image id, supplied back to it as its `self_program_id` when - /// dispatching `cross_zone_outbox_core::Instruction::Emit`. - outbox_program_id: ProgramId, target_program_id: ProgramId, }, } /// PDA accumulating all locked balance on this zone. #[must_use] -pub fn escrow_account_id(bridge_lock_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&bridge_lock_id, &escrow_seed()) +pub fn escrow_account_id(bridge_lock_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&bridge_lock_account_id, &escrow_seed()) } #[must_use] @@ -71,8 +61,8 @@ pub const fn escrow_seed() -> PdaSeed { /// PDA holding the outbox's dispatch address and the mint target, seeded at /// genesis so the guest can pin both without importing their image ids. #[must_use] -pub fn config_account_id(bridge_lock_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&bridge_lock_id, &config_seed()) +pub fn config_account_id(bridge_lock_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&bridge_lock_account_id, &config_seed()) } #[must_use] @@ -80,19 +70,14 @@ pub const fn config_seed() -> PdaSeed { PdaSeed::new(CONFIG_SEED_DOMAIN) } -/// Encodes the pinned outbox dispatch address, outbox image id, and mint target -/// for the config account's data. +/// Encodes the pinned outbox dispatch address and mint target for the config +/// account's data. #[must_use] -pub fn config_bytes( - outbox_account_id: AccountId, - outbox_program_id: ProgramId, - target_program_id: ProgramId, -) -> [u8; 96] { - let mut bytes = [0_u8; 96]; +pub fn config_bytes(outbox_account_id: AccountId, target_program_id: ProgramId) -> [u8; 64] { + let mut bytes = [0_u8; 64]; bytes[..32].copy_from_slice(outbox_account_id.value()); - for (word, chunk) in outbox_program_id + for (word, chunk) in target_program_id .iter() - .chain(target_program_id.iter()) .zip(bytes[32..].chunks_exact_mut(4)) { chunk.copy_from_slice(&word.to_le_bytes()); @@ -100,30 +85,24 @@ pub fn config_bytes( bytes } -/// Decodes the pinned outbox dispatch address, outbox image id, and mint target -/// from the config account's data. +/// Decodes the pinned outbox dispatch address and mint target from the config +/// account's data. #[must_use] -pub fn read_config(data: &[u8]) -> Option<(AccountId, ProgramId, ProgramId)> { - if data.len() < 96 { +pub fn read_config(data: &[u8]) -> Option<(AccountId, ProgramId)> { + if data.len() < 64 { return None; } - assert!(data.len() >= 96); + assert!(data.len() >= 64); let outbox_account_id = AccountId::new(data[..32].try_into().unwrap_or_else(|_| unreachable!())); - let mut ids = [0_u32; 16]; - for (word, chunk) in ids.iter_mut().zip(data[32..96].chunks_exact(4)) { + let mut target_program_id = [0_u32; 8]; + for (word, chunk) in target_program_id + .iter_mut() + .zip(data[32..64].chunks_exact(4)) + { *word = u32::from_le_bytes(chunk.try_into().unwrap_or_else(|_| unreachable!())); } - let (outbox_program_id, target_program_id) = ids.split_at(8); - Some(( - outbox_account_id, - outbox_program_id - .try_into() - .unwrap_or_else(|_| unreachable!()), - target_program_id - .try_into() - .unwrap_or_else(|_| unreachable!()), - )) + Some((outbox_account_id, target_program_id)) } #[cfg(test)] @@ -132,18 +111,17 @@ mod tests { #[test] fn escrow_is_stable() { - let id: ProgramId = [4; 8]; + let id = AccountId::new([4; 32]); assert_eq!(escrow_account_id(id), escrow_account_id(id)); } #[test] fn config_ids_round_trip() { let outbox_account_id = AccountId::new([3; 32]); - let outbox_program_id: ProgramId = [4; 8]; let target: ProgramId = [5; 8]; assert_eq!( - read_config(&config_bytes(outbox_account_id, outbox_program_id, target)), - Some((outbox_account_id, outbox_program_id, target)) + read_config(&config_bytes(outbox_account_id, target)), + Some((outbox_account_id, target)) ); } @@ -153,7 +131,6 @@ mod tests { #[test] fn lock_is_the_first_variant() { let lock = Instruction::Lock { - self_program_id: [2; 8], amount: 1, target_zone: [7; 32], target_program_id: [1; 8], diff --git a/lez/programs/bridge_lock/src/main.rs b/lez/programs/bridge_lock/src/main.rs index 1777f1593..1f7830226 100644 --- a/lez/programs/bridge_lock/src/main.rs +++ b/lez/programs/bridge_lock/src/main.rs @@ -30,7 +30,6 @@ fn main() { match instruction { Instruction::Lock { - self_program_id, amount, target_zone, target_program_id, @@ -42,7 +41,6 @@ fn main() { caller_account_id, pre_states, instruction_words, - self_program_id, amount, target_zone, target_program_id, @@ -51,18 +49,14 @@ fn main() { ordinal, ), Instruction::InitConfig { - self_program_id, outbox_account_id, - outbox_program_id, target_program_id, } => init_config( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, outbox_account_id, - outbox_program_id, target_program_id, ), } @@ -77,7 +71,6 @@ fn lock( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, amount: u128, target_zone: [u8; 32], target_program_id: ProgramId, @@ -93,10 +86,10 @@ fn lock( // and leave no record of what it was for. assert_eq!( config.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "first account must be the bridge-lock config PDA" ); - let (outbox_account_id, outbox_program_id, pinned_target) = read_config(&config.account.data) + let (outbox_account_id, pinned_target) = read_config(&config.account.data) .expect("config account holds an outbox and a mint target"); // Value conservation: the forwarded payload must mint exactly what is locked. @@ -120,11 +113,13 @@ fn lock( target_program_id, pinned_target, "bridge_lock only mints through the wrapped token it is pinned to" ); + let pinned_target_account_id = program_loader_core::immutable_deploy_account_id(pinned_target); assert_eq!( target_accounts, vec![ - wrapped_token_core::config_account_id(pinned_target).into_value(), - wrapped_token_core::holding_account_id(pinned_target, &recipient).into_value(), + wrapped_token_core::config_account_id(pinned_target_account_id).into_value(), + wrapped_token_core::holding_account_id(pinned_target_account_id, &recipient) + .into_value(), ], "target accounts must be the mint's config and the recipient's holding" ); @@ -144,7 +139,7 @@ fn lock( ); assert_eq!( escrow.account_id, - escrow_account_id(self_program_id), + escrow_account_id(self_account_id), "third account must be the escrow PDA" ); @@ -175,7 +170,6 @@ fn lock( outbox_account_id, vec![outbox.clone()], &OutboxInstruction::Emit { - self_program_id: outbox_program_id, target_zone, target_program_id, target_accounts, @@ -204,18 +198,12 @@ fn lock( /// Writes the outbox program and the mint target into the config PDA exactly once /// at genesis. -#[expect( - clippy::too_many_arguments, - reason = "the pinned fields are passed through verbatim" -)] fn init_config( self_account_id: AccountId, caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, outbox_account_id: AccountId, - outbox_program_id: ProgramId, target_program_id: ProgramId, ) { // pre_states: [config PDA]. @@ -223,7 +211,7 @@ fn init_config( .expect("InitConfig requires the config account"); assert_eq!( config.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "account must be the bridge-lock config PDA" ); // Init-once, idempotent under genesis replay: a `default` config is a first @@ -237,13 +225,13 @@ fn init_config( ); assert_eq!( config.account.data.clone().into_inner(), - config_bytes(outbox_account_id, outbox_program_id, target_program_id).to_vec(), + config_bytes(outbox_account_id, target_program_id).to_vec(), "bridge-lock config already pins a different outbox or mint target" ); } let mut config_account = config.account.clone(); - config_account.data = config_bytes(outbox_account_id, outbox_program_id, target_program_id) + config_account.data = config_bytes(outbox_account_id, target_program_id) .to_vec() .try_into() .expect("pinned ids fit in account data"); diff --git a/lez/programs/cross_zone_inbox/core/src/lib.rs b/lez/programs/cross_zone_inbox/core/src/lib.rs index f7de6db93..844b052f3 100644 --- a/lez/programs/cross_zone_inbox/core/src/lib.rs +++ b/lez/programs/cross_zone_inbox/core/src/lib.rs @@ -234,23 +234,11 @@ impl SeenShard { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Instruction { /// Delivers a finalized peer message to its target program. - Dispatch { - message: CrossZoneMessage, - /// This inbox's own image id. The guest cannot learn this at runtime, so the trusted - /// caller (the watcher/verifier) supplies it to recompute the inbox's own PDAs; a wrong - /// value only fails the guest's own self-consistency assertions, since real - /// authorization is independently enforced by the state layer against the account's - /// `program_owner`. - self_program_id: ProgramId, - }, + Dispatch { message: CrossZoneMessage }, /// Initializes the inbox config account at genesis. Written once, into a /// default (unclaimed) config PDA; the guest refuses a non-default pre-state, /// so it cannot be re-run to overwrite the allowlists. - InitConfig { - config: InboxConfig, - /// See [`Dispatch::self_program_id`](Instruction::Dispatch). - self_program_id: ProgramId, - }, + InitConfig { config: InboxConfig }, } /// Content-addressed replay key for a delivered message. @@ -276,8 +264,8 @@ pub fn message_key(src_zone: &ZoneId, src_block_id: u64, src_tx_index: u32) -> M /// The config account holding the allowlists. #[must_use] -pub fn inbox_config_account_id(inbox_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&inbox_id, &inbox_config_seed()) +pub fn inbox_config_account_id(inbox_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&inbox_account_id, &inbox_config_seed()) } /// Seed of the config PDA, exposed so the guest can claim the account when it @@ -290,11 +278,14 @@ pub const fn inbox_config_seed() -> PdaSeed { /// The seen-set shard for the peer block the message came from. #[must_use] pub fn inbox_seen_shard_account_id( - inbox_id: ProgramId, + inbox_account_id: AccountId, src_zone: &ZoneId, src_block_id: u64, ) -> AccountId { - AccountId::for_public_pda(&inbox_id, &inbox_seen_shard_seed(src_zone, src_block_id)) + AccountId::for_public_pda( + &inbox_account_id, + &inbox_seen_shard_seed(src_zone, src_block_id), + ) } /// Seed of the seen-shard PDA, exposed so the guest can claim the account. @@ -372,7 +363,7 @@ mod tests { #[test] fn every_peer_block_gets_its_own_seen_shard() { - let id: ProgramId = [9; 8]; + let id = AccountId::new([9; 32]); assert_eq!( inbox_seen_shard_account_id(id, &zone(1), 7), inbox_seen_shard_account_id(id, &zone(1), 7), diff --git a/lez/programs/cross_zone_inbox/src/main.rs b/lez/programs/cross_zone_inbox/src/main.rs index ff3022375..55781c9c2 100644 --- a/lez/programs/cross_zone_inbox/src/main.rs +++ b/lez/programs/cross_zone_inbox/src/main.rs @@ -5,10 +5,7 @@ use cross_zone_inbox_core::{ }; use lee_core::{ account::{Account, AccountId, AccountWithMetadata}, - program::{ - AccountPostState, ChainedCall, Claim, ProgramId, ProgramInput, ProgramOutput, - read_lee_inputs, - }, + program::{AccountPostState, ChainedCall, Claim, ProgramInput, ProgramOutput, read_lee_inputs}, }; fn unchanged(pre: &AccountWithMetadata) -> AccountPostState { @@ -32,27 +29,19 @@ fn main() { ); match instruction { - Instruction::Dispatch { - message, - self_program_id, - } => dispatch( + Instruction::Dispatch { message } => dispatch( self_account_id, caller_account_id, pre_states, instruction_words, &message, - self_program_id, ), - Instruction::InitConfig { - config, - self_program_id, - } => init_config( + Instruction::InitConfig { config } => init_config( self_account_id, caller_account_id, pre_states, instruction_words, &config, - self_program_id, ), } } @@ -78,7 +67,6 @@ fn dispatch( pre_states: Vec, instruction_words: Vec, msg: &CrossZoneMessage, - self_program_id: ProgramId, ) { assert!( msg.l1_inclusion_witness.is_none(), @@ -94,12 +82,12 @@ fn dispatch( assert_eq!( config.account_id, - inbox_config_account_id(self_program_id), + inbox_config_account_id(self_account_id), "First account must be the inbox config PDA" ); assert_eq!( seen.account_id, - inbox_seen_shard_account_id(self_program_id, &msg.src_zone, msg.src_block_id), + inbox_seen_shard_account_id(self_account_id, &msg.src_zone, msg.src_block_id), "Second account must be the seen-shard PDA" ); // The one value the chained call carries about where the message came from. @@ -196,14 +184,13 @@ fn init_config( pre_states: Vec, instruction_words: Vec, config: &InboxConfig, - self_program_id: ProgramId, ) { // pre_states: [config PDA]. let [config_meta] = <[AccountWithMetadata; 1]>::try_from(pre_states) .expect("InitConfig requires the config account"); assert_eq!( config_meta.account_id, - inbox_config_account_id(self_program_id), + inbox_config_account_id(self_account_id), "account must be the inbox config PDA" ); // Init-once, idempotent under genesis replay: a `default` config is a first diff --git a/lez/programs/cross_zone_marker/src/lib.rs b/lez/programs/cross_zone_marker/src/lib.rs index 4490a950c..5203f4c8c 100644 --- a/lez/programs/cross_zone_marker/src/lib.rs +++ b/lez/programs/cross_zone_marker/src/lib.rs @@ -32,12 +32,12 @@ pub type ZoneId = [u8; 32]; /// pinning its caller to the inbox, and only the inbox can be that caller. #[must_use] pub fn inbox_source_marker_account_id( - inbox_id: ProgramId, + inbox_account_id: AccountId, src_zone: &ZoneId, src_program_id: ProgramId, ) -> AccountId { AccountId::for_public_pda( - &inbox_id, + &inbox_account_id, &inbox_source_marker_seed(src_zone, src_program_id), ) } @@ -69,7 +69,7 @@ mod tests { /// must not land on the same account. #[test] fn the_marker_separates_every_source() { - let inbox: ProgramId = [1; 8]; + let inbox = AccountId::new([1; 32]); let base = inbox_source_marker_account_id(inbox, &[7; 32], [9; 8]); assert_eq!( base, @@ -85,7 +85,7 @@ mod tests { ); assert_ne!( base, - inbox_source_marker_account_id([2; 8], &[7; 32], [9; 8]) + inbox_source_marker_account_id(AccountId::new([2; 32]), &[7; 32], [9; 8]) ); } } diff --git a/lez/programs/cross_zone_outbox/core/src/lib.rs b/lez/programs/cross_zone_outbox/core/src/lib.rs index 70c0aa314..562c9da72 100644 --- a/lez/programs/cross_zone_outbox/core/src/lib.rs +++ b/lez/programs/cross_zone_outbox/core/src/lib.rs @@ -25,11 +25,6 @@ pub enum Instruction { /// Required accounts (1): /// - Outbox PDA account Emit { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the outbox slot PDA; a wrong value only fails the - /// guest's own self-consistency assertion, since real authorization is independently - /// enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, target_zone: ZoneId, target_program_id: ProgramId, /// Accounts the destination inbox must hand to the target program's @@ -83,12 +78,15 @@ impl OutboxRecord { /// other. #[must_use] pub fn outbox_pda( - outbox_id: ProgramId, + outbox_account_id: AccountId, emitter: AccountId, target_zone: &ZoneId, ordinal: u32, ) -> AccountId { - AccountId::for_public_pda(&outbox_id, &outbox_pda_seed(emitter, target_zone, ordinal)) + AccountId::for_public_pda( + &outbox_account_id, + &outbox_pda_seed(emitter, target_zone, ordinal), + ) } /// Seed of an outbox message PDA, exposed so the guest can claim the account. @@ -113,7 +111,7 @@ pub fn outbox_pda_seed(emitter: AccountId, target_zone: &ZoneId, ordinal: u32) - mod tests { use super::*; - const OUTBOX: ProgramId = [3; 8]; + const OUTBOX: AccountId = AccountId::new([3; 32]); const EMITTER: AccountId = AccountId::new([4; 32]); #[test] diff --git a/lez/programs/cross_zone_outbox/src/main.rs b/lez/programs/cross_zone_outbox/src/main.rs index 8c0e9199c..bf76d12c7 100644 --- a/lez/programs/cross_zone_outbox/src/main.rs +++ b/lez/programs/cross_zone_outbox/src/main.rs @@ -24,31 +24,28 @@ fn main() { panic!("Outbox is only callable through a chain call from a user program"); }; - let (self_program_id, target_zone, target_program_id, target_accounts, payload, ordinal) = - match instruction { - Instruction::Emit { - self_program_id, - target_zone, - target_program_id, - target_accounts, - payload, - ordinal, - } => ( - self_program_id, - target_zone, - target_program_id, - target_accounts, - payload, - ordinal, - ), - }; + let (target_zone, target_program_id, target_accounts, payload, ordinal) = match instruction { + Instruction::Emit { + target_zone, + target_program_id, + target_accounts, + payload, + ordinal, + } => ( + target_zone, + target_program_id, + target_accounts, + payload, + ordinal, + ), + }; let [outbox] = <[AccountWithMetadata; 1]>::try_from(pre_states).expect("Emit requires exactly 1 account"); assert_eq!( outbox.account_id, - outbox_pda(self_program_id, emitter, &target_zone, ordinal), + outbox_pda(self_account_id, emitter, &target_zone, ordinal), "Account must be the outbox PDA for (emitter, target_zone, ordinal)" ); diff --git a/lez/programs/faucet/core/src/lib.rs b/lez/programs/faucet/core/src/lib.rs index 43ca172fe..2c987a78d 100644 --- a/lez/programs/faucet/core/src/lib.rs +++ b/lez/programs/faucet/core/src/lib.rs @@ -1,5 +1,5 @@ +use lee_core::account::AccountId; pub use lee_core::program::PdaSeed; -use lee_core::{account::AccountId, program::ProgramId}; use serde::{Deserialize, Serialize}; const FAUCET_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/FaucetSeed/0000000000/"; @@ -14,12 +14,6 @@ pub enum Instruction { /// - Faucet PDA account /// - Recipient vault PDA account GenesisTransferVault { - /// This program's own image id. The guest cannot learn this at runtime (a RISC0 guest - /// has no way to read its own image id), so the trusted genesis caller supplies it to - /// recompute the faucet PDA; a wrong value only fails the guest's own self-consistency - /// assertion, since real authorization is independently enforced by the state layer - /// against the account's `program_owner`. - self_program_id: ProgramId, /// The vault program's real dispatch address. vault_account_id: AccountId, recipient_id: AccountId, @@ -33,11 +27,7 @@ pub enum Instruction { /// Required accounts (2): /// - Faucet PDA account /// - Recipient account - GenesisTransferDirect { - /// See `GenesisTransferVault::self_program_id`. - self_program_id: ProgramId, - amount: u128, - }, + GenesisTransferDirect { amount: u128 }, } #[must_use] @@ -46,6 +36,6 @@ pub const fn compute_faucet_seed() -> PdaSeed { } #[must_use] -pub fn compute_faucet_account_id(faucet_program_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&faucet_program_id, &compute_faucet_seed()) +pub fn compute_faucet_account_id(faucet_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&faucet_account_id, &compute_faucet_seed()) } diff --git a/lez/programs/faucet/src/main.rs b/lez/programs/faucet/src/main.rs index 6132e8909..1b4689ce2 100644 --- a/lez/programs/faucet/src/main.rs +++ b/lez/programs/faucet/src/main.rs @@ -33,7 +33,6 @@ fn main() { let chained_calls = match instruction { Instruction::GenesisTransferVault { - self_program_id, vault_account_id, recipient_id, amount, @@ -44,7 +43,7 @@ fn main() { assert_eq!( faucet.account_id, - faucet_core::compute_faucet_account_id(self_program_id), + faucet_core::compute_faucet_account_id(self_account_id), "First account must be faucet PDA" ); @@ -63,17 +62,14 @@ fn main() { .with_pda_seeds(vec![faucet_core::compute_faucet_seed()]), ] } - Instruction::GenesisTransferDirect { - self_program_id, - amount, - } => { + Instruction::GenesisTransferDirect { amount } => { let [faucet, recipient] = pre_states .try_into() .expect("TransferDirect requires exactly 2 accounts"); assert_eq!( faucet.account_id, - faucet_core::compute_faucet_account_id(self_program_id), + faucet_core::compute_faucet_account_id(self_account_id), "First account must be faucet PDA" ); diff --git a/lez/programs/ping_core/src/lib.rs b/lez/programs/ping_core/src/lib.rs index a9af1d007..a1cb68091 100644 --- a/lez/programs/ping_core/src/lib.rs +++ b/lez/programs/ping_core/src/lib.rs @@ -21,24 +21,13 @@ pub enum ReceiverInstruction { /// /// Required accounts (3): the source marker, the receiver config PDA, then /// the record PDA. - Record { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the config and record PDAs; a wrong value only fails - /// the guest's own self-consistency assertions, since real authorization is - /// independently enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, - payload: Vec, - }, + Record { payload: Vec }, /// Pins the deliverer and the peer sources it may deliver from, written once /// into a default config PDA at genesis. A re-run holding anything different /// is refused; an identical one is a no-op, which is what genesis replay does. /// /// Required accounts (1): the receiver config PDA. - InitConfig { - /// See [`Record::self_program_id`](ReceiverInstruction::Record). - self_program_id: ProgramId, - config: ReceiverConfig, - }, + InitConfig { config: ReceiverConfig }, /// Replaces the authorized sources. Refused unless the config names an /// authority and that account authorized the transaction. /// @@ -66,9 +55,10 @@ pub enum ReceiverInstruction { pub struct ReceiverConfig { /// The dispatch address of the program allowed to call `Record`: the cross-zone inbox. pub deliverer: AccountId, - /// The program allowed to reach the authority instructions through a chained - /// call, or `None` for top-level only. See `WrappedTokenConfig::governance`. - pub governance: Option, + /// The dispatch address of the program allowed to reach the authority + /// instructions through a chained call, or `None` for top-level only. See + /// `WrappedTokenConfig::governance`. + pub governance: Option, /// The account allowed to change `sources`, or `None` for a list fixed at /// genesis. Seeded unset; see `WrappedTokenConfig::authority` for why. pub authority: Option, @@ -99,11 +89,6 @@ pub enum SenderInstruction { /// /// Required accounts (2): the sender config PDA, then the outbox PDA. Send { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the sender's own config PDA; a wrong value only fails - /// the guest's own self-consistency assertion, since real authorization is independently - /// enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, target_zone: [u8; 32], target_program_id: ProgramId, target_accounts: Vec<[u8; 32]>, @@ -116,20 +101,15 @@ pub enum SenderInstruction { /// /// Required accounts (1): the sender config PDA. InitConfig { - /// See [`Send::self_program_id`](SenderInstruction::Send). - self_program_id: ProgramId, /// The outbox program's real dispatch address, used as the chained-call target. outbox_account_id: AccountId, - /// The outbox program's own image id, supplied back to it as its `self_program_id` when - /// dispatching `cross_zone_outbox_core::Instruction::Emit`. - outbox_program_id: ProgramId, }, } /// The account a `ping_receiver` records the latest delivered payload into. #[must_use] -pub fn ping_record_pda(receiver_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&receiver_id, &ping_record_seed()) +pub fn ping_record_pda(receiver_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&receiver_account_id, &ping_record_seed()) } /// Seed of the record PDA, exposed so the guest can claim the account. @@ -141,8 +121,8 @@ pub const fn ping_record_seed() -> PdaSeed { /// PDA holding the outbox program id, seeded at genesis so the guest can pin the /// program it chains into without importing the outbox image id. #[must_use] -pub fn sender_config_account_id(sender_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&sender_id, &sender_config_seed()) +pub fn sender_config_account_id(sender_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&sender_account_id, &sender_config_seed()) } #[must_use] @@ -152,8 +132,8 @@ pub const fn sender_config_seed() -> PdaSeed { /// PDA holding the sources `ping_receiver` accepts a delivery from. #[must_use] -pub fn receiver_config_account_id(receiver_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&receiver_id, &receiver_config_seed()) +pub fn receiver_config_account_id(receiver_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&receiver_account_id, &receiver_config_seed()) } #[must_use] @@ -161,39 +141,21 @@ pub const fn receiver_config_seed() -> PdaSeed { PdaSeed::new(RECEIVER_CONFIG_SEED) } -/// Encodes the pinned outbox's dispatch address and image id for the config -/// account's data. +/// Encodes the pinned outbox's dispatch address for the config account's data. #[must_use] -pub fn outbox_bytes(outbox_account_id: AccountId, outbox_program_id: ProgramId) -> [u8; 64] { - let mut bytes = [0_u8; 64]; - bytes[..32].copy_from_slice(outbox_account_id.value()); - for (word, chunk) in outbox_program_id - .iter() - .zip(bytes[32..].chunks_exact_mut(4)) - { - chunk.copy_from_slice(&word.to_le_bytes()); - } - bytes +pub const fn outbox_bytes(outbox_account_id: AccountId) -> [u8; 32] { + *outbox_account_id.value() } -/// Decodes the pinned outbox's dispatch address and image id from the config -/// account's data. +/// Decodes the pinned outbox's dispatch address from the config account's data. #[must_use] -pub fn read_outbox(data: &[u8]) -> Option<(AccountId, ProgramId)> { - if data.len() < 64 { +pub fn read_outbox(data: &[u8]) -> Option { + if data.len() < 32 { return None; } - assert!(data.len() >= 64); - let outbox_account_id = - AccountId::new(data[..32].try_into().unwrap_or_else(|_| unreachable!())); - let mut outbox_program_id = [0_u32; 8]; - for (word, chunk) in outbox_program_id - .iter_mut() - .zip(data[32..64].chunks_exact(4)) - { - *word = u32::from_le_bytes(chunk.try_into().unwrap_or_else(|_| unreachable!())); - } - Some((outbox_account_id, outbox_program_id)) + Some(AccountId::new( + data[..32].try_into().unwrap_or_else(|_| unreachable!()), + )) } #[cfg(test)] @@ -206,7 +168,6 @@ mod tests { #[test] fn send_is_the_first_variant() { let send = SenderInstruction::Send { - self_program_id: [2; 8], target_zone: [7; 32], target_program_id: [1; 8], target_accounts: vec![], @@ -221,10 +182,7 @@ mod tests { /// decoded by the destination, so its tag word is wire format. #[test] fn record_is_the_first_variant() { - let record = ReceiverInstruction::Record { - self_program_id: [1; 8], - payload: vec![], - }; + let record = ReceiverInstruction::Record { payload: vec![] }; let words = risc0_zkvm::serde::to_vec(&record).expect("Record serializes"); assert_eq!(words[0], 0); } @@ -248,10 +206,9 @@ mod tests { #[test] fn outbox_id_round_trips() { let outbox_account_id = AccountId::new([9; 32]); - let outbox_program_id: ProgramId = [3; 8]; assert_eq!( - read_outbox(&outbox_bytes(outbox_account_id, outbox_program_id)), - Some((outbox_account_id, outbox_program_id)) + read_outbox(&outbox_bytes(outbox_account_id)), + Some(outbox_account_id) ); } } diff --git a/lez/programs/ping_receiver/src/main.rs b/lez/programs/ping_receiver/src/main.rs index fecc6a39b..274f9a7b7 100644 --- a/lez/programs/ping_receiver/src/main.rs +++ b/lez/programs/ping_receiver/src/main.rs @@ -23,26 +23,18 @@ fn main() { ) = read_lee_inputs::(); match instruction { - ReceiverInstruction::Record { - self_program_id, - payload, - } => record( + ReceiverInstruction::Record { payload } => record( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, payload, ), - ReceiverInstruction::InitConfig { - self_program_id, - config, - } => init_config( + ReceiverInstruction::InitConfig { config } => init_config( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, &config, ), ReceiverInstruction::RenounceAuthority => renounce_authority( @@ -66,7 +58,6 @@ fn record( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, payload: Vec, ) { // pre_states: [source marker, config PDA, record PDA]. @@ -75,7 +66,7 @@ fn record( assert_eq!( config.account_id, - receiver_config_account_id(self_program_id), + receiver_config_account_id(self_account_id), "second account must be the receiver config PDA" ); let cfg = ReceiverConfig::from_bytes(&config.account.data) @@ -97,7 +88,7 @@ fn record( assert_eq!( record.account_id, - ping_record_pda(self_program_id), + ping_record_pda(self_account_id), "third account must be the ping record PDA" ); @@ -127,10 +118,6 @@ fn renounce_authority( pre_states: Vec, instruction_words: Vec, ) { - // See `record`'s doc comment: exact round-trip to the actual image id, needed by the - // PDA-derivation helpers below. - let self_program_id = ProgramId::from(self_account_id); - // The config is read before the account list is validated, so who may call // is decided first; an inbox-delivered call fails here on its prepended marker. let config_meta = pre_states @@ -138,7 +125,7 @@ fn renounce_authority( .expect("RenounceAuthority requires the config account"); assert_eq!( config_meta.account_id, - receiver_config_account_id(self_program_id), + receiver_config_account_id(self_account_id), "first account must be the receiver config PDA" ); let mut cfg = ReceiverConfig::from_bytes(&config_meta.account.data) @@ -146,7 +133,7 @@ fn renounce_authority( // Top-level, or the governance program the config names; see // `ReceiverConfig::governance` for why the escape hatch exists. assert!( - caller_account_id.is_none() || caller_account_id == cfg.governance.map(Into::into), + caller_account_id.is_none() || caller_account_id == cfg.governance, "the authority acts at top level, or through the configured governance program" ); @@ -203,10 +190,6 @@ fn update_sources( instruction_words: Vec, sources: Vec<([u8; 32], ProgramId)>, ) { - // See `record`'s doc comment: exact round-trip to the actual image id, needed by the - // PDA-derivation helpers below. - let self_program_id = ProgramId::from(self_account_id); - // The config is read before the account list is validated, so who may call // is decided first; an inbox-delivered call fails here on its prepended marker. let config_meta = pre_states @@ -214,7 +197,7 @@ fn update_sources( .expect("UpdateSources requires the config account"); assert_eq!( config_meta.account_id, - receiver_config_account_id(self_program_id), + receiver_config_account_id(self_account_id), "first account must be the receiver config PDA" ); let mut cfg = ReceiverConfig::from_bytes(&config_meta.account.data) @@ -222,7 +205,7 @@ fn update_sources( // Top-level, or the governance program the config names; see // `ReceiverConfig::governance` for why the escape hatch exists. assert!( - caller_account_id.is_none() || caller_account_id == cfg.governance.map(Into::into), + caller_account_id.is_none() || caller_account_id == cfg.governance, "the authority acts at top level, or through the configured governance program" ); @@ -285,7 +268,6 @@ fn init_config( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, config_value: &ReceiverConfig, ) { assert!( @@ -298,7 +280,7 @@ fn init_config( .expect("InitConfig requires the config account"); assert_eq!( config.account_id, - receiver_config_account_id(self_program_id), + receiver_config_account_id(self_account_id), "account must be the receiver config PDA" ); // Init-once, idempotent under genesis replay: a `default` config is a first diff --git a/lez/programs/ping_sender/src/main.rs b/lez/programs/ping_sender/src/main.rs index 04a220b45..c9365989c 100644 --- a/lez/programs/ping_sender/src/main.rs +++ b/lez/programs/ping_sender/src/main.rs @@ -28,7 +28,6 @@ fn main() { match instruction { SenderInstruction::Send { - self_program_id, target_zone, target_program_id, target_accounts, @@ -39,25 +38,18 @@ fn main() { caller_account_id, pre_states, instruction_words, - self_program_id, target_zone, target_program_id, target_accounts, payload, ordinal, ), - SenderInstruction::InitConfig { - self_program_id, - outbox_account_id, - outbox_program_id, - } => init_config( + SenderInstruction::InitConfig { outbox_account_id } => init_config( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, outbox_account_id, - outbox_program_id, ), } } @@ -71,7 +63,6 @@ fn send( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, target_zone: [u8; 32], target_program_id: ProgramId, target_accounts: Vec<[u8; 32]>, @@ -87,17 +78,16 @@ fn send( // skip the real outbox and leave no record of itself. assert_eq!( config.account_id, - sender_config_account_id(self_program_id), + sender_config_account_id(self_account_id), "first account must be the ping-sender config PDA" ); - let (outbox_account_id, outbox_program_id) = read_outbox(&config.account.data) - .expect("config account holds an outbox dispatch address and image id"); + let outbox_account_id = + read_outbox(&config.account.data).expect("config account holds an outbox dispatch address"); let call = ChainedCall::new( outbox_account_id, vec![outbox.clone()], &OutboxInstruction::Emit { - self_program_id: outbox_program_id, target_zone, target_program_id, target_accounts, @@ -119,22 +109,20 @@ fn send( .write(); } -/// Writes the outbox program id into the config PDA exactly once at genesis. +/// Writes the outbox dispatch address into the config PDA exactly once at genesis. fn init_config( self_account_id: AccountId, caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: ProgramId, outbox_account_id: AccountId, - outbox_program_id: ProgramId, ) { // pre_states: [config PDA]. let [config] = <[AccountWithMetadata; 1]>::try_from(pre_states) .expect("InitConfig requires the config account"); assert_eq!( config.account_id, - sender_config_account_id(self_program_id), + sender_config_account_id(self_account_id), "account must be the ping-sender config PDA" ); // Init-once, idempotent under genesis replay: a `default` config is a first @@ -148,13 +136,13 @@ fn init_config( ); assert_eq!( config.account.data.clone().into_inner(), - outbox_bytes(outbox_account_id, outbox_program_id).to_vec(), + outbox_bytes(outbox_account_id).to_vec(), "ping-sender config already pins a different outbox" ); } let mut config_account = config.account.clone(); - config_account.data = outbox_bytes(outbox_account_id, outbox_program_id) + config_account.data = outbox_bytes(outbox_account_id) .to_vec() .try_into() .expect("outbox id fits in account data"); diff --git a/lez/programs/program_loader/core/src/lib.rs b/lez/programs/program_loader/core/src/lib.rs index 12f2815ec..e889ee515 100644 --- a/lez/programs/program_loader/core/src/lib.rs +++ b/lez/programs/program_loader/core/src/lib.rs @@ -90,26 +90,26 @@ fn deploy_seed( #[must_use] pub fn deploy_header_account_id( - loader_program_id: ProgramId, + loader_account_id: AccountId, image_id: ProgramId, segment_number: u32, update_auth: AccountId, ) -> AccountId { AccountId::for_public_pda( - &loader_program_id, + &loader_account_id, &deploy_header_pda_seed(image_id, segment_number, update_auth), ) } #[must_use] pub fn deploy_segment_account_id( - loader_program_id: ProgramId, + loader_account_id: AccountId, image_id: ProgramId, segment_number: u32, update_auth: AccountId, ) -> AccountId { AccountId::for_public_pda( - &loader_program_id, + &loader_account_id, &deploy_segment_pda_seed(image_id, segment_number, update_auth), ) } @@ -121,8 +121,12 @@ pub fn deploy_segment_account_id( /// and any `Deploy` submitted with a default `update_auth`, dispatches at. #[must_use] pub fn immutable_deploy_account_id(image_id: ProgramId) -> AccountId { - let loader_id = ProgramId::from(lee_core::program::RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID); - deploy_header_account_id(loader_id, image_id, 0, AccountId::default()) + deploy_header_account_id( + lee_core::program::RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, + image_id, + 0, + AccountId::default(), + ) } /// Executes the `Deploy` instruction. @@ -132,7 +136,7 @@ pub fn immutable_deploy_account_id(image_id: ProgramId) -> AccountId { /// shortcut, which runs this instead of interpreting a guest ELF. #[must_use] pub fn execute_deploy( - self_program_id: ProgramId, + self_account_id: AccountId, pre_states: Vec, bytecode: Vec, ) -> Vec { @@ -143,8 +147,8 @@ pub fn execute_deploy( let update_auth = AccountId::default(); let header_seed = deploy_header_pda_seed(image_id, segment_number, update_auth); let segment_seed = deploy_segment_pda_seed(image_id, segment_number, update_auth); - let header_pda = AccountId::for_public_pda(&self_program_id, &header_seed); - let segment_pda = AccountId::for_public_pda(&self_program_id, &segment_seed); + let header_pda = AccountId::for_public_pda(&self_account_id, &header_seed); + let segment_pda = AccountId::for_public_pda(&self_account_id, &segment_seed); let [header_target, segment_target] = pre_states .try_into() diff --git a/lez/programs/sequencer_stake/core/src/lib.rs b/lez/programs/sequencer_stake/core/src/lib.rs index 0e1d384bc..49d9614b2 100644 --- a/lez/programs/sequencer_stake/core/src/lib.rs +++ b/lez/programs/sequencer_stake/core/src/lib.rs @@ -3,10 +3,7 @@ use std::collections::BTreeMap; pub use lee_core::program::PdaSeed; -use lee_core::{ - account::AccountId, - program::{InstructionData, ProgramId}, -}; +use lee_core::{account::AccountId, program::InstructionData}; use serde::{Deserialize, Serialize}; const INVALID_KEY: &str = "invalid Ed25519 public key"; @@ -70,11 +67,6 @@ pub enum Instruction { /// Locks `amount` into the ownership account for `sequencer_key`. First /// use claims the account; later calls top up the same account. Stake { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the config PDA; a wrong value only fails the guest's - /// own self-consistency assertion, since real authorization is independently enforced by - /// the state layer against the account's `program_owner`. - self_program_id: ProgramId, sequencer_key: SequencerKey, amount: u128, mover_account_id: AccountId, @@ -87,18 +79,13 @@ pub enum Instruction { /// Records a request to release `amount` to `destination`; no balance /// moves yet. Must leave the account at zero or at/above the minimum. UnstakeRequest { - /// See [`Instruction::Stake::self_program_id`]. - self_program_id: ProgramId, amount: u128, destination: AccountId, }, /// Unsigned, permissionless: releases a pending `UnstakeRequest`. /// Block-inclusion validity is enforced outside this program. - FinalizeUnstake { - /// See [`Instruction::Stake::self_program_id`]. - self_program_id: ProgramId, - }, + FinalizeUnstake, } /// Tag written into a claimed ownership account: which key it backs, plus any pending unstake. @@ -189,15 +176,15 @@ pub const fn sequencer_stake_config_seed() -> PdaSeed { } #[must_use] -pub fn sequencer_stake_config_account_id(program_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&program_id, &sequencer_stake_config_seed()) +pub fn sequencer_stake_config_account_id(program_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&program_account_id, &sequencer_stake_config_seed()) } #[cfg(test)] mod tests { use super::*; - const PROGRAM_ID: ProgramId = [9; 8]; + const PROGRAM_ID: AccountId = AccountId::new([9; 32]); fn test_destination() -> AccountId { AccountId::new([3; 32]) diff --git a/lez/programs/sequencer_stake/src/main.rs b/lez/programs/sequencer_stake/src/main.rs index 9d2d487d0..155f38c54 100644 --- a/lez/programs/sequencer_stake/src/main.rs +++ b/lez/programs/sequencer_stake/src/main.rs @@ -3,8 +3,8 @@ use std::collections::btree_map::Entry; use lee_core::{ account::{AccountId, AccountWithMetadata}, program::{ - AccountPostState, ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, InstructionData, ProgramId, - ProgramInput, ProgramOutput, read_lee_inputs, + AccountPostState, ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, InstructionData, ProgramInput, + ProgramOutput, read_lee_inputs, }, }; use sequencer_stake_core::{ @@ -25,7 +25,6 @@ fn main() { let (post_states, chained_calls) = match instruction { Instruction::Stake { - self_program_id, sequencer_key, amount, mover_account_id, @@ -36,7 +35,6 @@ fn main() { "Stake is only invoked as a top-level user transaction" ); stake( - self_program_id, self_account_id, pre_states.clone(), sequencer_key, @@ -57,7 +55,6 @@ fn main() { (post, Vec::new()) } Instruction::UnstakeRequest { - self_program_id, amount, destination, } => { @@ -65,21 +62,15 @@ fn main() { caller_account_id.is_none(), "UnstakeRequest is only invoked as a top-level user transaction" ); - let post = unstake_request( - self_program_id, - self_account_id, - pre_states.clone(), - amount, - destination, - ); + let post = unstake_request(self_account_id, pre_states.clone(), amount, destination); (post, Vec::new()) } - Instruction::FinalizeUnstake { self_program_id } => { + Instruction::FinalizeUnstake => { assert!( caller_account_id.is_none(), "FinalizeUnstake is only invoked as a top-level user transaction" ); - let post = finalize_unstake(self_program_id, self_account_id, pre_states.clone()); + let post = finalize_unstake(self_account_id, pre_states.clone()); (post, Vec::new()) } }; @@ -97,14 +88,13 @@ fn main() { fn decode_config( config_account: &AccountWithMetadata, - self_program_id: ProgramId, self_account_id: AccountId, ) -> SequencerStakeConfig { // By id, not just by owner: every ownership account is owned by this // program too, and its data is caller-influenced. assert_eq!( config_account.account_id, - sequencer_stake_config_account_id(self_program_id), + sequencer_stake_config_account_id(self_account_id), "not the sequencer_stake config account" ); assert_eq!( @@ -116,7 +106,6 @@ fn decode_config( } fn stake( - self_program_id: ProgramId, self_account_id: AccountId, pre_states: Vec, sequencer_key: SequencerKey, @@ -134,7 +123,7 @@ fn stake( "must sign for the ownership account" ); - let mut config = decode_config(&config_account, self_program_id, self_account_id); + let mut config = decode_config(&config_account, self_account_id); let minimum_sequencer_stake = config.minimum_sequencer_stake; let balance_before = ownership_account.account.balance; @@ -265,7 +254,6 @@ fn confirm_stake( } fn unstake_request( - self_program_id: ProgramId, self_account_id: AccountId, pre_states: Vec, amount: u128, @@ -290,7 +278,7 @@ fn unstake_request( "an unstake request is already pending" ); - let mut config = decode_config(&config_account, self_program_id, self_account_id); + let mut config = decode_config(&config_account, self_account_id); let minimum_sequencer_stake = config.minimum_sequencer_stake; let entry = config .entries @@ -338,7 +326,6 @@ fn unstake_request( } fn finalize_unstake( - self_program_id: ProgramId, self_account_id: AccountId, pre_states: Vec, ) -> Vec { @@ -380,7 +367,7 @@ fn finalize_unstake( .checked_add(pending.amount) .expect("finalize unstake amount overflow"); - let mut config = decode_config(&config_account, self_program_id, self_account_id); + let mut config = decode_config(&config_account, self_account_id); let entry = config .entries .get_mut(&record.sequencer_key) diff --git a/lez/programs/vault/core/src/lib.rs b/lez/programs/vault/core/src/lib.rs index 30cf09e6c..3c965fd90 100644 --- a/lez/programs/vault/core/src/lib.rs +++ b/lez/programs/vault/core/src/lib.rs @@ -1,5 +1,5 @@ +use lee_core::account::AccountId; pub use lee_core::program::PdaSeed; -use lee_core::{account::AccountId, program::ProgramId}; use serde::{Deserialize, Serialize}; const VAULT_SEED_DOMAIN_SEPARATOR: &[u8] = b"/LEZ/v0.3/VaultSeed/00000000000/"; @@ -47,7 +47,7 @@ pub fn compute_vault_seed(owner_id: AccountId) -> PdaSeed { } #[must_use] -pub fn compute_vault_account_id(vault_program_id: ProgramId, owner_id: AccountId) -> AccountId { +pub fn compute_vault_account_id(vault_account_id: AccountId, owner_id: AccountId) -> AccountId { let seed = compute_vault_seed(owner_id); - AccountId::for_public_pda(&vault_program_id, &seed) + AccountId::for_public_pda(&vault_account_id, &seed) } diff --git a/lez/programs/wrapped_token/core/src/lib.rs b/lez/programs/wrapped_token/core/src/lib.rs index 5b808ca9a..3aacb06d4 100644 --- a/lez/programs/wrapped_token/core/src/lib.rs +++ b/lez/programs/wrapped_token/core/src/lib.rs @@ -33,25 +33,13 @@ pub enum Instruction { /// /// Required accounts (3): the source marker, the wrapped-token config PDA, /// then the recipient's holding PDA. - Mint { - /// This program's own image id. The guest cannot learn this at runtime, so the trusted - /// caller supplies it to recompute the config and holding PDAs; a wrong value only fails - /// the guest's own self-consistency assertions, since real authorization is - /// independently enforced by the state layer against the account's `program_owner`. - self_program_id: ProgramId, - recipient: [u8; 32], - amount: u128, - }, + Mint { recipient: [u8; 32], amount: u128 }, /// Pins the minter and the peer sources it may mint for, written once into a /// default config PDA at genesis. A re-run holding anything different is /// refused; an identical one is a no-op, which is what genesis replay does. /// /// Required accounts (1): the wrapped-token config PDA. - InitConfig { - /// See [`Mint::self_program_id`](Instruction::Mint). - self_program_id: ProgramId, - config: WrappedTokenConfig, - }, + InitConfig { config: WrappedTokenConfig }, /// Replaces the authorized sources. Refused unless the config names an /// authority and that account authorized the transaction. /// @@ -78,13 +66,13 @@ pub enum Instruction { pub struct WrappedTokenConfig { /// The dispatch address of the program allowed to call `Mint`: the cross-zone inbox. pub minter: AccountId, - /// The program allowed to reach `UpdateSources` and `RenounceAuthority` - /// through a chained call, or `None` for top-level only. + /// The dispatch address of the program allowed to reach `UpdateSources` and + /// `RenounceAuthority` through a chained call, or `None` for top-level only. /// /// Exists because a PDA cannot sign: a program-held authority acts only by /// its own program delegating it on a chained call. Unset closes the ambient /// path where any program the authority signed for could rewrite the list. - pub governance: Option, + pub governance: Option, /// The account allowed to change `sources`, or `None` for a list fixed at /// genesis. /// @@ -113,8 +101,8 @@ impl WrappedTokenConfig { /// PDA holding the authorized minter program id (the cross-zone inbox), seeded at /// genesis so the guest can pin its caller without importing the inbox image id. #[must_use] -pub fn config_account_id(wrapped_token_id: ProgramId) -> AccountId { - AccountId::for_public_pda(&wrapped_token_id, &config_seed()) +pub fn config_account_id(wrapped_token_account_id: AccountId) -> AccountId { + AccountId::for_public_pda(&wrapped_token_account_id, &config_seed()) } #[must_use] @@ -124,8 +112,8 @@ pub const fn config_seed() -> PdaSeed { /// PDA holding one recipient's wrapped-token balance. #[must_use] -pub fn holding_account_id(wrapped_token_id: ProgramId, recipient: &[u8; 32]) -> AccountId { - AccountId::for_public_pda(&wrapped_token_id, &holding_seed(recipient)) +pub fn holding_account_id(wrapped_token_account_id: AccountId, recipient: &[u8; 32]) -> AccountId { + AccountId::for_public_pda(&wrapped_token_account_id, &holding_seed(recipient)) } #[must_use] @@ -164,7 +152,7 @@ mod tests { fn config_round_trips() { let config = WrappedTokenConfig { minter: AccountId::new([1; 32]), - governance: Some([2; 8]), + governance: Some(AccountId::new([2; 32])), authority: Some(AccountId::new([5; 32])), sources: vec![ ([7; 32], AccountId::new([9; 32])), @@ -189,7 +177,6 @@ mod tests { #[test] fn mint_is_the_first_variant() { let mint = Instruction::Mint { - self_program_id: [2; 8], recipient: [3; 32], amount: 1, }; @@ -205,7 +192,7 @@ mod tests { #[test] fn holding_is_unique_per_recipient() { - let id: ProgramId = [9; 8]; + let id = AccountId::new([9; 32]); assert_ne!( holding_account_id(id, &[1; 32]), holding_account_id(id, &[2; 32]) diff --git a/lez/programs/wrapped_token/src/main.rs b/lez/programs/wrapped_token/src/main.rs index 14c04f4ba..1417a20de 100644 --- a/lez/programs/wrapped_token/src/main.rs +++ b/lez/programs/wrapped_token/src/main.rs @@ -23,28 +23,19 @@ fn main() { ) = read_lee_inputs::(); match instruction { - Instruction::Mint { - self_program_id, - recipient, - amount, - } => mint( + Instruction::Mint { recipient, amount } => mint( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, recipient, amount, ), - Instruction::InitConfig { - self_program_id, - config, - } => init_config( + Instruction::InitConfig { config } => init_config( self_account_id, caller_account_id, pre_states, instruction_words, - self_program_id, &config, ), Instruction::RenounceAuthority => renounce_authority( @@ -68,7 +59,6 @@ fn mint( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: lee_core::program::ProgramId, recipient: [u8; 32], amount: u128, ) { @@ -80,7 +70,7 @@ fn mint( // inbox). Pin the caller to it, since the guest cannot import the inbox id. assert_eq!( config.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "second account must be the wrapped-token config PDA" ); let cfg = WrappedTokenConfig::from_bytes(&config.account.data) @@ -104,7 +94,7 @@ fn mint( assert_eq!( holding.account_id, - holding_account_id(self_program_id, &recipient), + holding_account_id(self_account_id, &recipient), "third account must be the recipient holding PDA" ); @@ -148,10 +138,6 @@ fn renounce_authority( pre_states: Vec, instruction_words: Vec, ) { - // See `mint`'s doc comment: exact round-trip to the actual image id, needed by the - // PDA-derivation helpers below. - let self_program_id = lee_core::program::ProgramId::from(self_account_id); - // The config is read before the account list is validated, so who may call // is decided first; an inbox-delivered call fails here on its prepended marker. let config_meta = pre_states @@ -159,7 +145,7 @@ fn renounce_authority( .expect("RenounceAuthority requires the config account"); assert_eq!( config_meta.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "first account must be the wrapped-token config PDA" ); let mut cfg = WrappedTokenConfig::from_bytes(&config_meta.account.data) @@ -167,7 +153,7 @@ fn renounce_authority( // Top-level, or the governance program the config names; see // `WrappedTokenConfig::governance` for why the escape hatch exists. assert!( - caller_account_id.is_none() || caller_account_id == cfg.governance.map(Into::into), + caller_account_id.is_none() || caller_account_id == cfg.governance, "the authority acts at top level, or through the configured governance program" ); @@ -224,10 +210,6 @@ fn update_sources( instruction_words: Vec, sources: Vec<([u8; 32], lee_core::program::ProgramId)>, ) { - // See `mint`'s doc comment: exact round-trip to the actual image id, needed by the - // PDA-derivation helpers below. - let self_program_id = lee_core::program::ProgramId::from(self_account_id); - // The config is read before the account list is validated, so who may call // is decided first; an inbox-delivered call fails here on its prepended marker. let config_meta = pre_states @@ -235,7 +217,7 @@ fn update_sources( .expect("UpdateSources requires the config account"); assert_eq!( config_meta.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "first account must be the wrapped-token config PDA" ); let mut cfg = WrappedTokenConfig::from_bytes(&config_meta.account.data) @@ -243,7 +225,7 @@ fn update_sources( // Top-level, or the governance program the config names; see // `WrappedTokenConfig::governance` for why the escape hatch exists. assert!( - caller_account_id.is_none() || caller_account_id == cfg.governance.map(Into::into), + caller_account_id.is_none() || caller_account_id == cfg.governance, "the authority acts at top level, or through the configured governance program" ); @@ -306,7 +288,6 @@ fn init_config( caller_account_id: Option, pre_states: Vec, instruction_words: Vec, - self_program_id: lee_core::program::ProgramId, config_value: &WrappedTokenConfig, ) { assert!( @@ -319,7 +300,7 @@ fn init_config( .expect("InitConfig requires the config account"); assert_eq!( config.account_id, - config_account_id(self_program_id), + config_account_id(self_account_id), "account must be the wrapped-token config PDA" ); // Init-once, idempotent under genesis replay: a `default` config is a first diff --git a/lez/sequencer/core/src/lib.rs b/lez/sequencer/core/src/lib.rs index a60bace05..589a2fc9e 100644 --- a/lez/sequencer/core/src/lib.rs +++ b/lez/sequencer/core/src/lib.rs @@ -1489,8 +1489,10 @@ struct BlockWithMeta { /// Whether `deposit_op_id`'s mint is already reflected in `state` — its receipt /// PDA exists. The receipt is the exactly-once ledger the bridge program keeps. fn deposit_already_minted(state: &lee::V03State, deposit_op_id: HashType) -> bool { - let receipt_id = - bridge_core::deposit_receipt_account_id(programs::bridge().id(), deposit_op_id.0); + let receipt_id = bridge_core::deposit_receipt_account_id( + programs::bridge().deployed_account_id(), + deposit_op_id.0, + ); state .get_account_by_id_ref(receipt_id) .is_some_and(|receipt| *receipt != lee::Account::default()) @@ -1508,7 +1510,7 @@ fn deposit_already_minted(state: &lee::V03State, deposit_op_id: HashType) -> boo /// delivered would drop the record instead of dead-lettering it. fn dispatch_already_delivered(state: &lee::V03State, message: &CrossZoneMessage) -> bool { let shard_id = cross_zone_inbox_core::inbox_seen_shard_account_id( - programs::cross_zone_inbox().id(), + programs::cross_zone_inbox().deployed_account_id(), &message.src_zone, message.src_block_id, ); @@ -1975,7 +1977,6 @@ fn genesis_stake_message( lee_core::account::Nonce(0), ], sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key, amount, mover_account_id: program_loader_core::immutable_deploy_account_id( @@ -2022,10 +2023,7 @@ fn build_stake_genesis_transactions(staked: &[FoundingStake]) -> Vec PublicTransaction { let faucet_program_id = programs::faucet().id(); let vault_program_id = programs::vault().id(); - let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, *account_id); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); + let recipient_vault_id = vault_core::compute_vault_account_id(vault_account_id, *account_id); let message = Message::try_new( program_loader_core::immutable_deploy_account_id(faucet_program_id), vec![system_accounts::faucet_account_id(), recipient_vault_id], Vec::new(), faucet_core::Instruction::GenesisTransferVault { - self_program_id: faucet_program_id, - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id: *account_id, amount: balance, }, @@ -2115,10 +2113,7 @@ fn build_supply_bridge_account_genesis_transaction(balance: u128) -> PublicTrans program_loader_core::immutable_deploy_account_id(faucet_program_id), vec![system_accounts::faucet_account_id(), bridge_account_id], Vec::new(), - faucet_core::Instruction::GenesisTransferDirect { - self_program_id: faucet_program_id, - amount: balance, - }, + faucet_core::Instruction::GenesisTransferDirect { amount: balance }, ) .expect("Failed to serialize bridge genesis transfer instruction"); let witness_set = lee::public_transaction::WitnessSet::from_raw_parts(Vec::new()); @@ -2140,16 +2135,18 @@ fn build_bridge_deposit_tx_from_event(event: &PendingDepositEventRecord) -> Resu .context("Failed to decode finalized Bedrock deposit metadata")?; let bridge_program_id = programs::bridge().id(); + let bridge_account_id = program_loader_core::immutable_deploy_account_id(bridge_program_id); let vault_program_id = programs::vault().id(); + let vault_account_id = program_loader_core::immutable_deploy_account_id(vault_program_id); let recipient_vault_id = - vault_core::compute_vault_account_id(vault_program_id, metadata.recipient_id); + vault_core::compute_vault_account_id(vault_account_id, metadata.recipient_id); // The receipt PDA carries the exactly-once check: the program reads it to // detect a replay, so it must be in the tx's account list. let receipt_id = - bridge_core::deposit_receipt_account_id(bridge_program_id, event.deposit_op_id.0); + bridge_core::deposit_receipt_account_id(bridge_account_id, event.deposit_op_id.0); let message = Message::try_new( - program_loader_core::immutable_deploy_account_id(bridge_program_id), + bridge_account_id, vec![ system_accounts::bridge_account_id(), recipient_vault_id, @@ -2158,9 +2155,7 @@ fn build_bridge_deposit_tx_from_event(event: &PendingDepositEventRecord) -> Resu Vec::new(), bridge_core::Instruction::Deposit { l1_deposit_op_id: event.deposit_op_id.0, - self_program_id: bridge_program_id, - vault_program_id, - vault_account_id: program_loader_core::immutable_deploy_account_id(vault_program_id), + vault_account_id, recipient_id: metadata.recipient_id, amount: event.amount, }, @@ -2211,7 +2206,7 @@ fn finalize_unstake_ownership_account(tx: &LeeTransaction) -> Option match risc0_zkvm::serde::from_slice::( &message.instruction_data, ) { - Ok(sequencer_stake_core::Instruction::FinalizeUnstake { .. }) => { + Ok(sequencer_stake_core::Instruction::FinalizeUnstake) => { message.account_ids.first().copied() } Ok(_) | Err(_) => None, @@ -2245,9 +2240,7 @@ fn build_finalize_unstake_tx( system_accounts::sequencer_stake_config_account_id(), ], vec![], - sequencer_stake_core::Instruction::FinalizeUnstake { - self_program_id: programs::sequencer_stake().id(), - }, + sequencer_stake_core::Instruction::FinalizeUnstake, ) .context("Failed to build FinalizeUnstake message")?; diff --git a/lez/sequencer/core/src/tests.rs b/lez/sequencer/core/src/tests.rs index b04c08461..8e6547010 100644 --- a/lez/sequencer/core/src/tests.rs +++ b/lez/sequencer/core/src/tests.rs @@ -279,7 +279,6 @@ fn cross_zone_test_config() -> SequencerConfig { /// form an emitter on the peer zone puts in the message payload. fn ping_payload(payload: &[u8]) -> Vec { risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record { - self_program_id: programs::ping_receiver().id(), payload: payload.to_vec(), }) .expect("ping instruction serializes") @@ -293,6 +292,7 @@ fn ping_payload(payload: &[u8]) -> Vec { /// to the encoding shows up here rather than passing silently. fn dispatch_tx(src_block_id: u64, payload: Vec) -> LeeTransaction { let receiver_id = programs::ping_receiver().id(); + let receiver_account_id = programs::ping_receiver().deployed_account_id(); LeeTransaction::Public(cross_zone::build_dispatch_from_emission( &cross_zone::EmissionSource { src_zone: PEER_ZONE, @@ -305,8 +305,8 @@ fn dispatch_tx(src_block_id: u64, payload: Vec) -> LeeTransaction { }, receiver_id, &[ - receiver_config_account_id(receiver_id).into_value(), - ping_record_pda(receiver_id).into_value(), + receiver_config_account_id(receiver_account_id).into_value(), + ping_record_pda(receiver_account_id).into_value(), ], payload, )) @@ -632,7 +632,8 @@ async fn an_orphaned_deposit_is_reminted_exactly_once_in_the_replacement() { mints, 1, "the deposit is re-minted exactly once after the orphan" ); - let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient_id); + let vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), recipient_id); assert_eq!( sequencer .with_state(|s| s.get_account_by_id(vault_id).balance) @@ -668,7 +669,8 @@ async fn a_replayed_deposit_mint_no_ops_in_the_guest() { panic!("bridge deposit tx is public"); }; - let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient_id); + let vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), recipient_id); let mut state = sequencer.chain().lock().await.head_state().clone(); // First mint: claims the receipt and credits the recipient vault. @@ -736,7 +738,7 @@ async fn recorded_dispatches_are_drained_from_the_store_on_production() { "the drained delivery should be included in the produced block" ); - let record_id = ping_record_pda(programs::ping_receiver().id()); + let record_id = ping_record_pda(programs::ping_receiver().deployed_account_id()); assert_eq!( sequencer .with_state(|state| state.get_account_by_id(record_id).data.into_inner()) @@ -2127,7 +2129,7 @@ fn pda_mechanism_with_pinata_token_program() { let pinata_token_definition_id = AccountId::new([2; 32]); // Total supply of pinata token will be in an account under a PDA. let pinata_token_holding_id = - AccountId::for_public_pda(&pinata_token.id(), &PdaSeed::new([0; 32])); + AccountId::for_public_pda(&pinata_token.deployed_account_id(), &PdaSeed::new([0; 32])); let winner_token_holding_id = AccountId::new([3; 32]); let expected_winner_account_holding = token_core::TokenHolding::Fungible { @@ -3194,7 +3196,6 @@ fn diag_sequencer_stake_claims_ownership_account() { vec![funding_id, ownership_id, config_id], vec![Nonce(0), Nonce(0)], sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key, amount, mover_account_id: program_loader_core::immutable_deploy_account_id( @@ -3250,7 +3251,6 @@ fn stake_transaction( state.get_account_by_id(ownership_id).nonce, ], sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key, amount, mover_account_id: program_loader_core::immutable_deploy_account_id( @@ -3320,7 +3320,6 @@ fn unstake_request_transaction( vec![ownership_id, config_slot], vec![state.get_account_by_id(ownership_id).nonce], sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount, destination, }, @@ -3562,7 +3561,6 @@ fn a_fully_exited_ownership_account_can_stake_again() { ], vec![state.get_account_by_id(ownership_id).nonce], sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount, destination: funding_id, }, @@ -3667,7 +3665,6 @@ fn the_bootstrap_sequencer_can_request_an_unstake_of_its_genesis_stake() { // The genesis Stake transaction already signed once with this account. vec![Nonce(1)], sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount: system_accounts::DEFAULT_MINIMUM_SEQUENCER_STAKE, destination, }, @@ -3695,12 +3692,15 @@ fn the_bootstrap_sequencer_can_request_an_unstake_of_its_genesis_stake() { /// Derives the `(header, segment)` account pair `bytecode` would deploy to. fn deploy_targets(bytecode: &[u8]) -> (AccountId, AccountId) { - let loader_id: ProgramId = RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID.into(); let image_id: ProgramId = risc0_binfmt::compute_image_id(bytecode).unwrap().into(); - let header = - program_loader_core::deploy_header_account_id(loader_id, image_id, 0, AccountId::default()); + let header = program_loader_core::deploy_header_account_id( + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, + image_id, + 0, + AccountId::default(), + ); let segment = program_loader_core::deploy_segment_account_id( - loader_id, + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, image_id, 0, AccountId::default(), @@ -3713,9 +3713,8 @@ fn deploy_transaction( segment: AccountId, bytecode: Vec, ) -> PublicTransaction { - let loader_id: ProgramId = RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID.into(); let message = lee::public_transaction::Message::try_new( - loader_id.into(), + RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID, vec![header, segment], vec![], program_loader_core::Instruction::Deploy { bytecode }, diff --git a/lez/sequencer/core/src/tests/reconstruction.rs b/lez/sequencer/core/src/tests/reconstruction.rs index d61d12480..3444d1f7c 100644 --- a/lez/sequencer/core/src/tests/reconstruction.rs +++ b/lez/sequencer/core/src/tests/reconstruction.rs @@ -663,8 +663,8 @@ fn deposit_event_record( // seq_b.run_production_turn().await.unwrap(); -// let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient); -// let bridge_id = system_accounts::bridge_account_id(); +// let vault_id = vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), +// recipient); let bridge_id = system_accounts::bridge_account_id(); // let state_b = seq_b.chain().lock().await.head_state().clone(); // let state_a = seq_a.chain().lock().await.head_state().clone(); // for account in [vault_id, bridge_id, recipient] { @@ -812,7 +812,8 @@ async fn reconstruction_reconciles_already_finished_deposit() { .expect("reconstruct"); // The mint was applied exactly once. - let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient); + let vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), recipient); assert_eq!( chain_b .lock() @@ -919,7 +920,7 @@ async fn reconstructed_delivery_settles_its_pending_record() { ); // The delivery landed exactly once, and the next turn does not re-emit it. - let record_id = ping_record_pda(programs::ping_receiver().id()); + let record_id = ping_record_pda(programs::ping_receiver().deployed_account_id()); assert_eq!( seq_b .with_state(|state| state.get_account_by_id(record_id).data.into_inner()) diff --git a/lez/sequencer/service/src/bin/submit_stake.rs b/lez/sequencer/service/src/bin/submit_stake.rs index 009e1e308..58bd26bdf 100644 --- a/lez/sequencer/service/src/bin/submit_stake.rs +++ b/lez/sequencer/service/src/bin/submit_stake.rs @@ -88,7 +88,6 @@ async fn main() -> Result<()> { .context("Failed to serialize mover instruction")?; let instruction_data = Program::serialize_instruction(sequencer_stake_core::Instruction::Stake { - self_program_id: programs::sequencer_stake().id(), sequencer_key, amount, mover_account_id: program_loader_core::immutable_deploy_account_id( @@ -118,7 +117,6 @@ async fn main() -> Result<()> { } => { let instruction_data = Program::serialize_instruction(sequencer_stake_core::Instruction::UnstakeRequest { - self_program_id: programs::sequencer_stake().id(), amount, destination, }) diff --git a/lez/system_accounts/src/lib.rs b/lez/system_accounts/src/lib.rs index 3577c918c..d4d549f00 100644 --- a/lez/system_accounts/src/lib.rs +++ b/lez/system_accounts/src/lib.rs @@ -42,7 +42,7 @@ pub fn pinata_account() -> Account { #[must_use] pub fn faucet_account_id() -> AccountId { - faucet_core::compute_faucet_account_id(programs::faucet().id()) + faucet_core::compute_faucet_account_id(programs::faucet().deployed_account_id()) } #[must_use] @@ -58,7 +58,7 @@ pub fn faucet_account() -> Account { #[must_use] pub fn bridge_account_id() -> AccountId { - bridge_core::compute_bridge_account_id(programs::bridge().id()) + bridge_core::compute_bridge_account_id(programs::bridge().deployed_account_id()) } #[must_use] @@ -78,7 +78,9 @@ pub const fn clock_account_ids() -> [AccountId; 3] { #[must_use] pub fn sequencer_stake_config_account_id() -> AccountId { - sequencer_stake_core::sequencer_stake_config_account_id(programs::sequencer_stake().id()) + sequencer_stake_core::sequencer_stake_config_account_id( + programs::sequencer_stake().deployed_account_id(), + ) } /// Starts with no entries; every stake, including the bootstrap sequencer's diff --git a/lez/wallet-ffi/src/pda.rs b/lez/wallet-ffi/src/pda.rs index 35a9482ba..181f4c94c 100644 --- a/lez/wallet-ffi/src/pda.rs +++ b/lez/wallet-ffi/src/pda.rs @@ -2,29 +2,29 @@ use lee::AccountId; use crate::{ error::WalletFfiError, FfiBytes32, FfiNullifierPublicKey, FfiPdaSeed, FfiPrivateAccountKeys, - FfiProgramId, FfiU128, + FfiU128, }; /// Produce account id for public PDA. /// /// # Parameters -/// - `program_id`: Id of the owner program +/// - `program_account_id`: dispatch address of the owner program /// - `pda_seed`: 32 byte seed /// /// # Returns /// - `FfiBytes32` representing account id bytes #[no_mangle] pub extern "C" fn wallet_ffi_account_id_for_public_pda( - program_id: FfiProgramId, + program_account_id: FfiBytes32, pda_seed: FfiPdaSeed, ) -> FfiBytes32 { - AccountId::for_public_pda(&program_id.data, &pda_seed.into()).into() + AccountId::for_public_pda(&program_account_id.into(), &pda_seed.into()).into() } /// Produce account id for private PDA. /// /// # Parameters -/// - `program_id`: Id of the owner program +/// - `program_account_id`: dispatch address of the owner program /// - `pda_seed`: 32 byte seed /// - `npk`: 32 byte nullifier public key (can be obtained from /// `wallet_ffi_get_private_account_keys`) @@ -44,7 +44,7 @@ pub extern "C" fn wallet_ffi_account_id_for_public_pda( /// - `account_id` must be a valid pointer to a `FfiBytes32` struct #[no_mangle] pub unsafe extern "C" fn wallet_ffi_account_id_for_private_pda( - program_id: FfiProgramId, + program_account_id: FfiBytes32, pda_seed: FfiPdaSeed, npk: FfiNullifierPublicKey, viewing_public_key: *const u8, @@ -70,7 +70,7 @@ pub unsafe extern "C" fn wallet_ffi_account_id_for_private_pda( unsafe { *account_id = AccountId::for_private_pda( - &program_id.data, + &program_account_id.into(), &pda_seed.into(), &ffi_private_keys.npk(), &vpk.unwrap(), @@ -96,24 +96,26 @@ mod tests { #[test] fn public_pda_consistent_derivation() { - let program_id = [100_u32, 101, 102, 103, 104, 105, 106, 107]; + let program_account_id = AccountId::new([100; 32]); let pda_seed = PdaSeed::new([42; 32]); - let pda_id = AccountId::for_public_pda(&program_id, &pda_seed); - let ffi_pda_id = wallet_ffi_account_id_for_public_pda(program_id.into(), pda_seed.into()); + let pda_id = AccountId::for_public_pda(&program_account_id, &pda_seed); + let ffi_pda_id = + wallet_ffi_account_id_for_public_pda(program_account_id.into(), pda_seed.into()); assert_eq!(pda_id.into_value(), ffi_pda_id.data); } #[test] fn private_pda_consistent_derivation() { - let program_id = [100_u32, 101, 102, 103, 104, 105, 106, 107]; + let program_account_id = AccountId::new([100; 32]); let pda_seed = PdaSeed::new([42; 32]); let vpk = ViewingPublicKey::from_bytes(vec![43; 1184]).unwrap(); let npk = NullifierPublicKey([44; 32]); let identifier = 100_000_u128; - let pda_id = AccountId::for_private_pda(&program_id, &pda_seed, &npk, &vpk, identifier); + let pda_id = + AccountId::for_private_pda(&program_account_id, &pda_seed, &npk, &vpk, identifier); let vpk_ptr = Box::into_raw(vpk.to_bytes().to_vec().into_boxed_slice()) as *const u8; @@ -122,7 +124,7 @@ mod tests { let err = unsafe { wallet_ffi_account_id_for_private_pda( - program_id.into(), + program_account_id.into(), pda_seed.into(), npk.into(), vpk_ptr, diff --git a/lez/wallet-ffi/src/types.rs b/lez/wallet-ffi/src/types.rs index aefa30e05..2168263dc 100644 --- a/lez/wallet-ffi/src/types.rs +++ b/lez/wallet-ffi/src/types.rs @@ -716,7 +716,7 @@ mod tests { &npk, &vpk, &PrivateAccountKind::Pda { - program_id: [46; 8], + program_account_id: lee::AccountId::new([46; 32]), seed: PdaSeed::new([47; 32]), identifier, }, diff --git a/lez/wallet-ffi/src/vault.rs b/lez/wallet-ffi/src/vault.rs index 6db1fa669..2049f4faf 100644 --- a/lez/wallet-ffi/src/vault.rs +++ b/lez/wallet-ffi/src/vault.rs @@ -57,7 +57,8 @@ pub unsafe extern "C" fn wallet_ffi_get_vault_balance( }; let owner_id = AccountId::new(unsafe { (*owner).data }); - let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), owner_id); + let vault_id = + vault_core::compute_vault_account_id(programs::vault().deployed_account_id(), owner_id); let balance = match block_on(wallet.get_account_balance(vault_id)) { Ok(b) => b, diff --git a/lez/wallet-ffi/wallet_ffi.h b/lez/wallet-ffi/wallet_ffi.h index 0245b4144..3dd6f5bac 100644 --- a/lez/wallet-ffi/wallet_ffi.h +++ b/lez/wallet-ffi/wallet_ffi.h @@ -941,20 +941,20 @@ enum WalletFfiError wallet_ffi_free_label_list(struct LabelList *label_list); * Produce account id for public PDA. * * # Parameters - * - `program_id`: Id of the owner program + * - `program_account_id`: dispatch address of the owner program * - `pda_seed`: 32 byte seed * * # Returns * - `FfiBytes32` representing account id bytes */ -struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiProgramId program_id, +struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiBytes32 program_account_id, FfiPdaSeed pda_seed); /** * Produce account id for private PDA. * * # Parameters - * - `program_id`: Id of the owner program + * - `program_account_id`: dispatch address of the owner program * - `pda_seed`: 32 byte seed * - `npk`: 32 byte nullifier public key (can be obtained from * `wallet_ffi_get_private_account_keys`) @@ -973,7 +973,7 @@ struct FfiBytes32 wallet_ffi_account_id_for_public_pda(struct FfiProgramId progr * - `viewing_public_key` must be a valid pointer to a `u8` * - `account_id` must be a valid pointer to a `FfiBytes32` struct */ -enum WalletFfiError wallet_ffi_account_id_for_private_pda(struct FfiProgramId program_id, +enum WalletFfiError wallet_ffi_account_id_for_private_pda(struct FfiBytes32 program_account_id, FfiPdaSeed pda_seed, FfiNullifierPublicKey npk, const uint8_t *viewing_public_key, diff --git a/lez/wallet/src/cli/account.rs b/lez/wallet/src/cli/account.rs index d6e5f52ea..a2afe816f 100644 --- a/lez/wallet/src/cli/account.rs +++ b/lez/wallet/src/cli/account.rs @@ -103,14 +103,15 @@ pub enum NewSubcommand { /// Label to assign to the new account. label: Option