From b3f14e7fcaf7f2f1f69d48dc83559042401f536a Mon Sep 17 00:00:00 2001 From: ananas-block Date: Sun, 8 Jun 2025 05:46:46 +0100 Subject: [PATCH 01/21] feat: add pinocchio-sdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: add cpi_signer_macro feat: add light-program-test, light-client docs Co-authored-by: Swen Schäferjohann <42959314+SwenSchaeferjohann@users.noreply.github.com> --- .github/workflows/light-examples-tests.yml | 2 +- Cargo.lock | 57 ++++ Cargo.toml | 5 + examples/anchor/counter/src/lib.rs | 20 +- examples/anchor/counter/tests/test.rs | 3 +- examples/anchor/memo/tests/test.rs | 4 +- .../name-service-without-macros/tests/test.rs | 4 +- examples/anchor/name-service/tests/test.rs | 4 +- .../src/escrow_with_compressed_pda/escrow.rs | 6 +- .../src/escrow_with_compressed_pda/sdk.rs | 3 +- .../escrow_with_compressed_pda/withdrawal.rs | 6 +- .../token-escrow/src/escrow_with_pda/sdk.rs | 4 +- examples/anchor/token-escrow/src/lib.rs | 4 + .../src/instructions/address_batch_update.rs | 5 +- forester-utils/src/registry.rs | 2 +- forester/src/indexer_type.rs | 2 +- program-libs/batched-merkle-tree/Cargo.toml | 2 +- .../src/instruction_data/compressed_proof.rs | 39 +++ .../tests/address_merkle_tree_tests.rs | 10 +- .../tests/merkle_tree_tests.rs | 11 +- program-tests/client-test/Cargo.toml | 3 + .../client-test/tests/light_client.rs | 3 +- .../client-test/tests/light_program_test.rs | 3 +- program-tests/client-test/tests/sdk_compat.rs | 110 ++++++++ .../create-address-test-program/src/lib.rs | 11 +- .../programs/sdk-anchor-test/src/lib.rs | 26 +- .../programs/sdk-anchor-test/tests/test.rs | 83 +++--- program-tests/sdk-pinocchio-test/Cargo.toml | 41 +++ program-tests/sdk-pinocchio-test/Xargo.toml | 2 + .../sdk-pinocchio-test/src/create_pda.rs | 91 +++++++ program-tests/sdk-pinocchio-test/src/lib.rs | 48 ++++ .../sdk-pinocchio-test/src/update_pda.rs | 68 +++++ .../sdk-pinocchio-test/tests/test.rs | 206 +++++++++++++++ program-tests/sdk-test/Cargo.toml | 3 +- program-tests/sdk-test/src/create_pda.rs | 33 +-- program-tests/sdk-test/src/lib.rs | 4 +- program-tests/sdk-test/src/update_pda.rs | 27 +- program-tests/sdk-test/tests/test.rs | 3 +- program-tests/system-cpi-test/tests/test.rs | 20 +- .../system-cpi-v2-test/tests/event.rs | 2 +- .../tests/invoke_cpi_with_read_only.rs | 34 +-- program-tests/system-test/tests/test.rs | 18 +- program-tests/utils/src/e2e_test_env.rs | 32 +-- program-tests/utils/src/pack.rs | 16 +- program-tests/utils/src/spl.rs | 19 +- program-tests/utils/src/system_program.rs | 2 +- programs/account-compression/Cargo.toml | 1 + programs/account-compression/src/errors.rs | 40 +++ .../rollover_address_merkle_tree_and_queue.rs | 5 +- .../rollover_batched_state_merkle_tree.rs | 6 +- .../rollover_state_merkle_tree_and_queue.rs | 10 +- .../src/processor/initialize_address_queue.rs | 11 +- .../initialize_concurrent_merkle_tree.rs | 11 +- programs/compressed-token/src/freeze.rs | 2 +- .../compressed-token/src/process_transfer.rs | 6 +- programs/system/Cargo.toml | 1 + programs/system/src/errors.rs | 19 ++ .../src/processor/create_inputs_cpi_data.rs | 4 +- .../src/processor/create_outputs_cpi_data.rs | 4 +- programs/system/src/processor/process.rs | 2 +- programs/system/src/processor/verify_proof.rs | 14 +- scripts/format.sh | 3 + sdk-libs/client/Cargo.toml | 2 +- sdk-libs/client/src/indexer/mod.rs | 4 +- sdk-libs/client/src/indexer/types.rs | 54 +++- sdk-libs/client/src/lib.rs | 78 ++++++ sdk-libs/macros/Cargo.toml | 4 +- sdk-libs/macros/src/cpi_signer.rs | 95 +++++++ sdk-libs/macros/src/discriminator.rs | 2 +- sdk-libs/macros/src/hasher/light_hasher.rs | 14 +- sdk-libs/macros/src/lib.rs | 48 ++++ sdk-libs/macros/tests/pda.rs | 77 ++++++ .../program-test/src/accounts/initialize.rs | 2 +- .../program-test/src/accounts/state_tree.rs | 2 +- .../src/accounts/state_tree_v2.rs | 3 +- .../src/accounts/test_accounts.rs | 8 +- .../program-test/src/indexer/address_tree.rs | 2 +- .../program-test/src/indexer/test_indexer.rs | 13 +- sdk-libs/program-test/src/lib.rs | 114 ++++++++ .../src/program_test/light_program_test.rs | 7 - sdk-libs/program-test/src/program_test/rpc.rs | 60 +---- .../src/utils/setup_light_programs.rs | 17 +- sdk-libs/sdk-pinocchio/Cargo.toml | 24 ++ sdk-libs/sdk-pinocchio/src/account.rs | 196 ++++++++++++++ sdk-libs/sdk-pinocchio/src/address.rs | 47 ++++ sdk-libs/sdk-pinocchio/src/cpi/accounts.rs | 249 ++++++++++++++++++ sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 206 +++++++++++++++ sdk-libs/sdk-pinocchio/src/cpi/mod.rs | 5 + sdk-libs/sdk-pinocchio/src/error.rs | 147 +++++++++++ sdk-libs/sdk-pinocchio/src/instruction/mod.rs | 1 + sdk-libs/sdk-pinocchio/src/lib.rs | 17 ++ sdk-libs/sdk-types/Cargo.toml | 25 ++ sdk-libs/sdk-types/src/address.rs | 160 +++++++++++ sdk-libs/sdk-types/src/constants.rs | 38 +++ sdk-libs/sdk-types/src/error.rs | 45 ++++ .../src/instruction}/account_info.rs | 66 ++--- .../src/instruction/account_meta.rs | 3 +- sdk-libs/sdk-types/src/instruction/mod.rs | 4 + .../sdk-types/src/instruction/tree_info.rs | 30 +++ sdk-libs/sdk-types/src/lib.rs | 19 ++ sdk-libs/sdk/Cargo.toml | 37 ++- sdk-libs/sdk/src/account.rs | 74 +++++- sdk-libs/sdk/src/address.rs | 147 ++++------- sdk-libs/sdk/src/constants.rs | 29 -- sdk-libs/sdk/src/cpi/accounts.rs | 43 ++- sdk-libs/sdk/src/cpi/accounts_small_ix.rs | 17 +- sdk-libs/sdk/src/cpi/invoke.rs | 33 +-- sdk-libs/sdk/src/cpi/mod.rs | 55 ++++ sdk-libs/sdk/src/error.rs | 67 ++++- sdk-libs/sdk/src/instruction/mod.rs | 188 ++++++++++++- sdk-libs/sdk/src/instruction/pack_accounts.rs | 4 +- .../{accounts.rs => system_accounts.rs} | 20 +- sdk-libs/sdk/src/instruction/tree_info.rs | 31 +-- sdk-libs/sdk/src/lib.rs | 165 ++++++++---- sdk-libs/sdk/src/utils.rs | 20 +- 115 files changed, 3373 insertions(+), 685 deletions(-) create mode 100644 program-tests/client-test/tests/sdk_compat.rs create mode 100644 program-tests/sdk-pinocchio-test/Cargo.toml create mode 100644 program-tests/sdk-pinocchio-test/Xargo.toml create mode 100644 program-tests/sdk-pinocchio-test/src/create_pda.rs create mode 100644 program-tests/sdk-pinocchio-test/src/lib.rs create mode 100644 program-tests/sdk-pinocchio-test/src/update_pda.rs create mode 100644 program-tests/sdk-pinocchio-test/tests/test.rs create mode 100644 sdk-libs/macros/src/cpi_signer.rs create mode 100644 sdk-libs/macros/tests/pda.rs create mode 100644 sdk-libs/sdk-pinocchio/Cargo.toml create mode 100644 sdk-libs/sdk-pinocchio/src/account.rs create mode 100644 sdk-libs/sdk-pinocchio/src/address.rs create mode 100644 sdk-libs/sdk-pinocchio/src/cpi/accounts.rs create mode 100644 sdk-libs/sdk-pinocchio/src/cpi/invoke.rs create mode 100644 sdk-libs/sdk-pinocchio/src/cpi/mod.rs create mode 100644 sdk-libs/sdk-pinocchio/src/error.rs create mode 100644 sdk-libs/sdk-pinocchio/src/instruction/mod.rs create mode 100644 sdk-libs/sdk-pinocchio/src/lib.rs create mode 100644 sdk-libs/sdk-types/Cargo.toml create mode 100644 sdk-libs/sdk-types/src/address.rs create mode 100644 sdk-libs/sdk-types/src/constants.rs create mode 100644 sdk-libs/sdk-types/src/error.rs rename sdk-libs/{sdk/src => sdk-types/src/instruction}/account_info.rs (76%) rename sdk-libs/{sdk => sdk-types}/src/instruction/account_meta.rs (98%) create mode 100644 sdk-libs/sdk-types/src/instruction/mod.rs create mode 100644 sdk-libs/sdk-types/src/instruction/tree_info.rs create mode 100644 sdk-libs/sdk-types/src/lib.rs delete mode 100644 sdk-libs/sdk/src/constants.rs rename sdk-libs/sdk/src/instruction/{accounts.rs => system_accounts.rs} (88%) diff --git a/.github/workflows/light-examples-tests.yml b/.github/workflows/light-examples-tests.yml index 03de67e190..2c0d1d355a 100644 --- a/.github/workflows/light-examples-tests.yml +++ b/.github/workflows/light-examples-tests.yml @@ -56,7 +56,7 @@ jobs: - program: sdk-test-program sub-tests: '["cargo-test-sbf -p sdk-test"]' - program: sdk-anchor-test-program - sub-tests: '["cargo-test-sbf -p sdk-anchor-test"]' + sub-tests: '["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk-pinocchio-test"]' steps: - name: Checkout sources diff --git a/Cargo.lock b/Cargo.lock index f110459e05..2f12161641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "rand 0.8.5", "solana-sdk", "solana-security-txt", + "thiserror 2.0.12", "zerocopy 0.8.25", ] @@ -1235,10 +1236,13 @@ dependencies = [ "light-client", "light-compressed-account", "light-compressed-token", + "light-hasher", "light-program-test", "light-prover-client", "light-sdk", + "light-sdk-pinocchio", "light-test-utils", + "light-zero-copy", "rand 0.8.5", "solana-account", "solana-account-decoder-client-types", @@ -3629,6 +3633,8 @@ dependencies = [ "light-hasher", "light-macros", "light-sdk-macros", + "light-sdk-types", + "light-zero-copy", "num-bigint 0.4.6", "solana-account-info", "solana-cpi", @@ -3646,7 +3652,9 @@ dependencies = [ "borsh 0.10.4", "light-compressed-account", "light-hasher", + "light-macros", "light-poseidon 0.3.0", + "light-sdk-types", "prettyplease", "proc-macro2", "quote", @@ -3654,6 +3662,37 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "light-sdk-pinocchio" +version = "0.12.0" +dependencies = [ + "borsh 0.10.4", + "light-account-checks", + "light-compressed-account", + "light-hasher", + "light-macros", + "light-sdk-macros", + "light-sdk-types", + "light-zero-copy", + "pinocchio", + "solana-pubkey", + "thiserror 2.0.12", +] + +[[package]] +name = "light-sdk-types" +version = "0.9.1" +dependencies = [ + "anchor-lang", + "borsh 0.10.4", + "light-compressed-account", + "light-hasher", + "light-macros", + "light-zero-copy", + "solana-pubkey", + "thiserror 2.0.12", +] + [[package]] name = "light-sparse-merkle-tree" version = "0.1.0" @@ -3699,6 +3738,7 @@ dependencies = [ "pinocchio", "pinocchio-system", "rand 0.8.5", + "solana-msg", "solana-pubkey", "solana-security-txt", "thiserror 2.0.12", @@ -5282,6 +5322,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "sdk-pinocchio-test" +version = "1.0.0" +dependencies = [ + "borsh 0.10.4", + "light-compressed-account", + "light-hasher", + "light-macros", + "light-program-test", + "light-sdk", + "light-sdk-pinocchio", + "pinocchio", + "solana-sdk", + "tokio", +] + [[package]] name = "sdk-test" version = "1.0.0" @@ -5292,6 +5348,7 @@ dependencies = [ "light-macros", "light-program-test", "light-sdk", + "light-sdk-types", "solana-program", "solana-sdk", "tokio", diff --git a/Cargo.toml b/Cargo.toml index cd00d86c91..5a98246284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ members = [ "sdk-libs/client", "sdk-libs/macros", "sdk-libs/sdk", + "sdk-libs/sdk-pinocchio", + "sdk-libs/sdk-types", "sdk-libs/photon-api", "sdk-libs/program-test", "xtask", @@ -38,6 +40,7 @@ members = [ # Issue is that anchor discriminator now returns a slice instead of an array "program-tests/sdk-anchor-test/programs/sdk-anchor-test", "program-tests/sdk-test", + "program-tests/sdk-pinocchio-test", "program-tests/create-address-test-program", "program-tests/utils", "program-tests/merkle-tree", @@ -153,7 +156,9 @@ light-merkle-tree-reference = { path = "program-tests/merkle-tree", version = "2 light-heap = { path = "program-libs/heap", version = "1.1.0" } light-prover-client = { path = "prover/client", version = "1.3.0" } light-sdk = { path = "sdk-libs/sdk", version = "0.12.0" } +light-sdk-pinocchio = { path = "sdk-libs/sdk-pinocchio", version = "0.12.0" } light-sdk-macros = { path = "sdk-libs/macros", version = "0.6.0" } +light-sdk-types = { path = "sdk-libs/sdk-types", version = "0.9.1" } light-compressed-account = { path = "program-libs/compressed-account", version = "0.2.0" } light-account-checks = { path = "program-libs/account-checks", version = "0.2.0" } light-verifier = { path = "program-libs/verifier", version = "2.0.0" } diff --git a/examples/anchor/counter/src/lib.rs b/examples/anchor/counter/src/lib.rs index f0b26e4670..b38bc2ad1e 100644 --- a/examples/anchor/counter/src/lib.rs +++ b/examples/anchor/counter/src/lib.rs @@ -4,16 +4,20 @@ use anchor_lang::{prelude::*, AnchorDeserialize, Discriminator}; use light_sdk::{ account::LightAccount, address::v1::derive_address, - cpi::{CpiAccounts, CpiInputs}, + cpi::{CpiAccounts, CpiInputs, CpiSigner}, + derive_light_cpi_signer, instruction::{ account_meta::{CompressedAccountMeta, CompressedAccountMetaClose}, - tree_info::PackedAddressTreeInfo, + PackedAddressTreeInfo, ValidityProof, }, - LightDiscriminator, LightHasher, ValidityProof, + LightDiscriminator, LightHasher, }; declare_id!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX"); + #[program] pub mod counter { @@ -33,7 +37,7 @@ pub mod counter { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; @@ -94,7 +98,7 @@ pub mod counter { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; @@ -133,7 +137,7 @@ pub mod counter { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; @@ -170,7 +174,7 @@ pub mod counter { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; let cpi_inputs = CpiInputs::new( @@ -207,7 +211,7 @@ pub mod counter { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; diff --git a/examples/anchor/counter/tests/test.rs b/examples/anchor/counter/tests/test.rs index ea5da12d73..c7832dd86a 100644 --- a/examples/anchor/counter/tests/test.rs +++ b/examples/anchor/counter/tests/test.rs @@ -10,8 +10,7 @@ use light_sdk::{ address::v1::derive_address, instruction::{ account_meta::{CompressedAccountMeta, CompressedAccountMetaClose}, - accounts::SystemAccountMetaConfig, - pack_accounts::PackedAccounts, + PackedAccounts, SystemAccountMetaConfig, }, }; use solana_sdk::{ diff --git a/examples/anchor/memo/tests/test.rs b/examples/anchor/memo/tests/test.rs index 8180322070..cf151b2c47 100644 --- a/examples/anchor/memo/tests/test.rs +++ b/examples/anchor/memo/tests/test.rs @@ -17,7 +17,7 @@ use light_sdk::{ address::derive_address, instruction_data::LightInstructionData, tree_info::{AddressTreeInfo, PackedAccounts}, - utils::get_cpi_authority_pda, + find_cpi_signer_macro, verify::find_cpi_signer, PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, PROGRAM_ID_NOOP, }; @@ -66,7 +66,7 @@ async fn test_memo_program() { &memo::ID, ); - let account_compression_authority = get_cpi_authority_pda(&PROGRAM_ID_LIGHT_SYSTEM); + let account_compression_authority = find_cpi_signer_macro!(&PROGRAM_ID_LIGHT_SYSTEM); let registered_program_pda = Pubkey::find_program_address( &[PROGRAM_ID_LIGHT_SYSTEM.to_bytes().as_slice()], &PROGRAM_ID_ACCOUNT_COMPRESSION, diff --git a/examples/anchor/name-service-without-macros/tests/test.rs b/examples/anchor/name-service-without-macros/tests/test.rs index fc4ac44163..689aafa9bb 100644 --- a/examples/anchor/name-service-without-macros/tests/test.rs +++ b/examples/anchor/name-service-without-macros/tests/test.rs @@ -20,7 +20,7 @@ use light_sdk::{ error::LightSdkError, instruction_data::LightInstructionData, tree_info::{AddressTreeInfo, PackedAccounts}, - utils::get_cpi_authority_pda, + find_cpi_signer_macro, verify::find_cpi_signer, PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, PROGRAM_ID_NOOP, }; @@ -76,7 +76,7 @@ async fn test_name_service() { &name_service_without_macros::ID, ); - let account_compression_authority = get_cpi_authority_pda(&PROGRAM_ID_LIGHT_SYSTEM); + let account_compression_authority = find_cpi_signer_macro!(&PROGRAM_ID_LIGHT_SYSTEM); let registered_program_pda = Pubkey::find_program_address( &[PROGRAM_ID_LIGHT_SYSTEM.to_bytes().as_slice()], &PROGRAM_ID_ACCOUNT_COMPRESSION, diff --git a/examples/anchor/name-service/tests/test.rs b/examples/anchor/name-service/tests/test.rs index e37eacc6bb..35070e673f 100644 --- a/examples/anchor/name-service/tests/test.rs +++ b/examples/anchor/name-service/tests/test.rs @@ -20,7 +20,7 @@ use light_sdk::{ pack_address_merkle_context, pack_merkle_context, AddressTreeInfo, MerkleContext, PackedAddressTreeInfo, PackedMerkleContext, PackedAccounts, }, - utils::get_cpi_authority_pda, + find_cpi_signer_macro, verify::find_cpi_signer, PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, PROGRAM_ID_NOOP, }; @@ -83,7 +83,7 @@ async fn test_name_service() { let address_merkle_context = pack_address_merkle_context(&address_merkle_context, &mut remaining_accounts); - let account_compression_authority = get_cpi_authority_pda(&PROGRAM_ID_LIGHT_SYSTEM); + let account_compression_authority = find_cpi_signer_macro!(&PROGRAM_ID_LIGHT_SYSTEM); let registered_program_pda = Pubkey::find_program_address( &[PROGRAM_ID_LIGHT_SYSTEM.to_bytes().as_slice()], &PROGRAM_ID_ACCOUNT_COMPRESSION, diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs index 88f3c33619..66f2ab65e7 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs @@ -136,11 +136,7 @@ fn cpi_compressed_pda_transfer<'info>( let light_accounts = CpiAccounts::new_with_config( ctx.accounts.signer.as_ref(), &system_accounts, - CpiAccountsConfig { - self_program: crate::ID, - cpi_context: true, - ..Default::default() - }, + CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), ) .unwrap(); diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/sdk.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/sdk.rs index de69bf115d..f89f9362c0 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/sdk.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/sdk.rs @@ -13,6 +13,7 @@ use light_compressed_token::process_transfer::{ transfer_sdk::{create_inputs_and_remaining_accounts_checked, to_account_metas}, TokenTransferOutputData, }; +use light_sdk::{constants::CPI_AUTHORITY_PDA_SEED, find_cpi_signer_macro}; use light_test_utils::pack::{ add_and_get_remaining_account_indices, pack_merkle_context, pack_new_address_params, }; @@ -98,7 +99,7 @@ pub fn create_escrow_instruction( let compressed_token_cpi_authority_pda = get_cpi_authority_pda().0; let account_compression_authority = light_system_program::utils::get_cpi_authority_pda(&light_system_program::ID); - let cpi_authority_pda = light_sdk::utils::get_cpi_authority_pda(&crate::ID); + let cpi_authority_pda = find_cpi_signer_macro!(&crate::ID).0; let accounts = crate::accounts::EscrowCompressedTokensWithCompressedPda { signer: *input_params.signer, diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs index 8b92845cdc..65d37fe0b8 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs @@ -160,11 +160,7 @@ fn cpi_compressed_pda_withdrawal<'info>( let light_accounts = CpiAccounts::new_with_config( ctx.accounts.signer.as_ref(), &system_accounts, - CpiAccountsConfig { - self_program: crate::ID, - cpi_context: true, - ..Default::default() - }, + CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), ) .unwrap(); verify_borsh(&light_accounts, &inputs_struct).unwrap(); diff --git a/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs b/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs index 724c07f8b8..11998515c1 100644 --- a/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs +++ b/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs @@ -133,7 +133,9 @@ pub fn create_withdrawal_escrow_instruction( ); let merkle_tree_indices = add_and_get_remaining_account_indices( - input_params.output_compressed_account_merkle_tree_pubkeys, + input_params.output_compressed_account_merkle_tree_pubkeys, // .iter() + // .map(|pubkey| anchor_lang::prelude::Pubkey::from(pubkey)) + // .collect::>() &mut remaining_accounts, ); diff --git a/examples/anchor/token-escrow/src/lib.rs b/examples/anchor/token-escrow/src/lib.rs index 5f978cbbed..268ded15fa 100644 --- a/examples/anchor/token-escrow/src/lib.rs +++ b/examples/anchor/token-escrow/src/lib.rs @@ -3,6 +3,7 @@ use anchor_lang::{prelude::*, solana_program::pubkey::Pubkey}; use light_compressed_token::process_transfer::{ InputTokenDataWithContext, PackedTokenTransferOutputData, }; +use light_sdk::{cpi::CpiSigner, derive_light_cpi_signer}; pub mod escrow_with_compressed_pda; pub mod escrow_with_pda; @@ -23,6 +24,9 @@ pub enum EscrowError { declare_id!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX"); + #[program] pub mod token_escrow { diff --git a/forester-utils/src/instructions/address_batch_update.rs b/forester-utils/src/instructions/address_batch_update.rs index 31eaa4db62..fc1d790626 100644 --- a/forester-utils/src/instructions/address_batch_update.rs +++ b/forester-utils/src/instructions/address_batch_update.rs @@ -8,13 +8,14 @@ use light_batched_merkle_tree::{ }, }; use light_client::{indexer::Indexer, rpc::Rpc}; -use light_compressed_account::hash_chain::create_hash_chain_from_slice; +use light_compressed_account::{ + hash_chain::create_hash_chain_from_slice, instruction_data::compressed_proof::CompressedProof, +}; use light_hasher::{bigint::bigint_to_be_bytes_array, Poseidon}; use light_prover_client::{ proof_client::ProofClient, proof_types::batch_address_append::get_batch_address_append_circuit_inputs, }; -use light_sdk::light_compressed_account::instruction_data::compressed_proof::CompressedProof; use light_sparse_merkle_tree::{ changelog::ChangelogEntry, indexed_changelog::IndexedChangelogEntry, SparseMerkleTree, }; diff --git a/forester-utils/src/registry.rs b/forester-utils/src/registry.rs index f62e31a95f..130c7e3017 100644 --- a/forester-utils/src/registry.rs +++ b/forester-utils/src/registry.rs @@ -324,7 +324,7 @@ pub async fn create_rollover_state_merkle_tree_instructions( rpc.get_minimum_balance_for_rent_exemption(account_size) .await .unwrap(), - &light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), Some(new_cpi_context_keypair), ); let instruction = create_rollover_state_merkle_tree_instruction( diff --git a/forester/src/indexer_type.rs b/forester/src/indexer_type.rs index d510e3c468..f4d69b7850 100644 --- a/forester/src/indexer_type.rs +++ b/forester/src/indexer_type.rs @@ -15,7 +15,7 @@ use light_merkle_tree_reference::MerkleTree; use light_program_test::indexer::{ state_tree::StateMerkleTreeBundle, TestIndexer, TestIndexerExtensions, }; -use light_sdk::{STATE_MERKLE_TREE_CANOPY_DEPTH, STATE_MERKLE_TREE_HEIGHT}; +use light_sdk::constants::{STATE_MERKLE_TREE_CANOPY_DEPTH, STATE_MERKLE_TREE_HEIGHT}; use solana_program::pubkey::Pubkey; use solana_sdk::{signature::Keypair, signer::Signer}; use tokio::sync::Mutex; diff --git a/program-libs/batched-merkle-tree/Cargo.toml b/program-libs/batched-merkle-tree/Cargo.toml index 92f98ba869..414ee91400 100644 --- a/program-libs/batched-merkle-tree/Cargo.toml +++ b/program-libs/batched-merkle-tree/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" edition = "2021" [features] -default = ["solana"] +default = [] test-only = [] solana = [ "solana-program-error", diff --git a/program-libs/compressed-account/src/instruction_data/compressed_proof.rs b/program-libs/compressed-account/src/instruction_data/compressed_proof.rs index 3f789363a8..9c79f9ca24 100644 --- a/program-libs/compressed-account/src/instruction_data/compressed_proof.rs +++ b/program-libs/compressed-account/src/instruction_data/compressed_proof.rs @@ -40,3 +40,42 @@ impl<'a> Deserialize<'a> for CompressedProof { Ok(Ref::<&[u8], CompressedProof>::from_prefix(bytes)?) } } + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)] +pub struct ValidityProof(pub Option); + +impl ValidityProof { + pub fn new(proof: Option) -> Self { + Self(proof) + } +} + +impl From for ValidityProof { + fn from(proof: CompressedProof) -> Self { + Self(Some(proof)) + } +} + +impl From> for ValidityProof { + fn from(proof: Option) -> Self { + Self(proof) + } +} +impl From<&CompressedProof> for ValidityProof { + fn from(proof: &CompressedProof) -> Self { + Self(Some(*proof)) + } +} + +impl From<&Option> for ValidityProof { + fn from(proof: &Option) -> Self { + Self(*proof) + } +} + +#[allow(clippy::from_over_into)] +impl Into> for ValidityProof { + fn into(self) -> Option { + self.0 + } +} diff --git a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs index 1a67f70052..8d44feb49f 100644 --- a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs @@ -18,7 +18,6 @@ use light_concurrent_merkle_tree::errors::ConcurrentMerkleTreeError; use light_hash_set::{HashSet, HashSetError}; use light_hasher::{bigint::bigint_to_be_bytes_array, Poseidon}; use light_indexed_merkle_tree::errors::IndexedMerkleTreeError; -use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use light_program_test::{ accounts::address_tree::create_initialize_address_merkle_tree_and_queue_instruction, indexer::address_tree::AddressMerkleTreeBundle, program_test::LightProgramTest, @@ -1285,7 +1284,8 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -1304,7 +1304,8 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -1356,7 +1357,8 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), ) .unwrap(); } diff --git a/program-tests/account-compression-test/tests/merkle_tree_tests.rs b/program-tests/account-compression-test/tests/merkle_tree_tests.rs index 13c6b09a5b..e10ed079f4 100644 --- a/program-tests/account-compression-test/tests/merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/merkle_tree_tests.rs @@ -21,7 +21,7 @@ use light_hash_set::HashSetError; use light_hasher::{ bigint::bigint_to_be_bytes_array, zero_bytes::poseidon::ZERO_BYTES, Hasher, Poseidon, }; -use light_merkle_tree_metadata::{errors::MerkleTreeMetadataError, QueueType}; +use light_merkle_tree_metadata::QueueType; use light_merkle_tree_reference::MerkleTree; use light_program_test::{ accounts::state_tree::{ @@ -731,7 +731,8 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -750,7 +751,8 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -806,7 +808,8 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), + AccountCompressionErrorCode::MerkleTreeMetadataError.into(), + // MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), ) .unwrap(); } diff --git a/program-tests/client-test/Cargo.toml b/program-tests/client-test/Cargo.toml index 4283fb8e71..05b173aad4 100644 --- a/program-tests/client-test/Cargo.toml +++ b/program-tests/client-test/Cargo.toml @@ -19,6 +19,9 @@ light-program-test = { workspace = true, features = ["devenv"] } light-prover-client = { workspace = true, features = ["devenv"] } light-test-utils = { workspace = true } light-sdk = { workspace = true } +light-sdk-pinocchio = { workspace = true } +light-zero-copy = { workspace = true } +light-hasher = { workspace = true } light-compressed-account = { workspace = true } light-compressed-token = { workspace = true } diff --git a/program-tests/client-test/tests/light_client.rs b/program-tests/client-test/tests/light_client.rs index ba86a6b791..0ee41972ca 100644 --- a/program-tests/client-test/tests/light_client.rs +++ b/program-tests/client-test/tests/light_client.rs @@ -13,9 +13,8 @@ use light_compressed_token::mint_sdk::{ use light_program_test::accounts::test_accounts::TestAccounts; use light_prover_client::prover::ProverConfig; use light_sdk::{ - address::v1::derive_address, + address::{v1::derive_address, NewAddressParams}, token::{AccountState, TokenData}, - NewAddressParams, }; use light_test_utils::{system_program::create_invoke_instruction, Rpc, RpcError}; use solana_compute_budget_interface::ComputeBudgetInstruction; diff --git a/program-tests/client-test/tests/light_program_test.rs b/program-tests/client-test/tests/light_program_test.rs index 374dd6a21c..087674193a 100644 --- a/program-tests/client-test/tests/light_program_test.rs +++ b/program-tests/client-test/tests/light_program_test.rs @@ -14,9 +14,8 @@ use light_program_test::{ accounts::test_accounts::TestAccounts, program_test::LightProgramTest, ProgramTestConfig, }; use light_sdk::{ - address::v1::derive_address, + address::{v1::derive_address, NewAddressParams}, token::{AccountState, TokenData}, - NewAddressParams, }; use light_test_utils::{system_program::create_invoke_instruction, RpcError}; use solana_sdk::{ diff --git a/program-tests/client-test/tests/sdk_compat.rs b/program-tests/client-test/tests/sdk_compat.rs new file mode 100644 index 0000000000..615ba49749 --- /dev/null +++ b/program-tests/client-test/tests/sdk_compat.rs @@ -0,0 +1,110 @@ +use light_hasher::HasherError; +use light_sdk::error::LightSdkError as SolanaLightSdkError; +use light_sdk_pinocchio::error::LightSdkError as PinocchioLightSdkError; +use light_zero_copy::errors::ZeroCopyError; + +fn generate_all_solana_errors() -> Vec { + vec![ + SolanaLightSdkError::ConstraintViolation, + SolanaLightSdkError::InvalidLightSystemProgram, + SolanaLightSdkError::ExpectedAccounts, + SolanaLightSdkError::ExpectedAddressTreeInfo, + SolanaLightSdkError::ExpectedAddressRootIndex, + SolanaLightSdkError::ExpectedData, + SolanaLightSdkError::ExpectedDiscriminator, + SolanaLightSdkError::ExpectedHash, + SolanaLightSdkError::ExpectedLightSystemAccount("test".to_string()), + SolanaLightSdkError::ExpectedMerkleContext, + SolanaLightSdkError::ExpectedRootIndex, + SolanaLightSdkError::TransferFromNoInput, + SolanaLightSdkError::TransferFromNoLamports, + SolanaLightSdkError::TransferFromInsufficientLamports, + SolanaLightSdkError::TransferIntegerOverflow, + SolanaLightSdkError::Borsh, + SolanaLightSdkError::FewerAccountsThanSystemAccounts, + SolanaLightSdkError::InvalidCpiSignerAccount, + SolanaLightSdkError::MissingField("test".to_string()), + SolanaLightSdkError::OutputStateTreeIndexIsNone, + SolanaLightSdkError::InitAddressIsNone, + SolanaLightSdkError::InitWithAddressIsNone, + SolanaLightSdkError::InitWithAddressOutputIsNone, + SolanaLightSdkError::MetaMutAddressIsNone, + SolanaLightSdkError::MetaMutInputIsNone, + SolanaLightSdkError::MetaMutOutputLamportsIsNone, + SolanaLightSdkError::MetaMutOutputIsNone, + SolanaLightSdkError::MetaCloseAddressIsNone, + SolanaLightSdkError::MetaCloseInputIsNone, + SolanaLightSdkError::Hasher(HasherError::IntegerOverflow), + SolanaLightSdkError::ZeroCopy(ZeroCopyError::Full), + ] +} + +fn generate_all_pinocchio_errors() -> Vec { + vec![ + PinocchioLightSdkError::ConstraintViolation, + PinocchioLightSdkError::InvalidLightSystemProgram, + PinocchioLightSdkError::ExpectedAccounts, + PinocchioLightSdkError::ExpectedAddressTreeInfo, + PinocchioLightSdkError::ExpectedAddressRootIndex, + PinocchioLightSdkError::ExpectedData, + PinocchioLightSdkError::ExpectedDiscriminator, + PinocchioLightSdkError::ExpectedHash, + PinocchioLightSdkError::ExpectedLightSystemAccount("test".to_string()), + PinocchioLightSdkError::ExpectedMerkleContext, + PinocchioLightSdkError::ExpectedRootIndex, + PinocchioLightSdkError::TransferFromNoInput, + PinocchioLightSdkError::TransferFromNoLamports, + PinocchioLightSdkError::TransferFromInsufficientLamports, + PinocchioLightSdkError::TransferIntegerOverflow, + PinocchioLightSdkError::Borsh, + PinocchioLightSdkError::FewerAccountsThanSystemAccounts, + PinocchioLightSdkError::InvalidCpiSignerAccount, + PinocchioLightSdkError::MissingField("test".to_string()), + PinocchioLightSdkError::OutputStateTreeIndexIsNone, + PinocchioLightSdkError::InitAddressIsNone, + PinocchioLightSdkError::InitWithAddressIsNone, + PinocchioLightSdkError::InitWithAddressOutputIsNone, + PinocchioLightSdkError::MetaMutAddressIsNone, + PinocchioLightSdkError::MetaMutInputIsNone, + PinocchioLightSdkError::MetaMutOutputLamportsIsNone, + PinocchioLightSdkError::MetaMutOutputIsNone, + PinocchioLightSdkError::MetaCloseAddressIsNone, + PinocchioLightSdkError::MetaCloseInputIsNone, + PinocchioLightSdkError::Hasher(HasherError::IntegerOverflow), + PinocchioLightSdkError::ZeroCopy(ZeroCopyError::Full), + ] +} + +#[test] +fn test_error_compatibility() { + let solana_errors = generate_all_solana_errors(); + let pinocchio_errors = generate_all_pinocchio_errors(); + + // Ensure both SDKs have the same number of error variants + assert_eq!( + solana_errors.len(), + pinocchio_errors.len(), + "SDKs have different number of error variants" + ); + + // Test string representations + for (solana_error, pinocchio_error) in solana_errors.iter().zip(pinocchio_errors.iter()) { + assert_eq!( + solana_error.to_string(), + pinocchio_error.to_string(), + "String representations differ for error variants" + ); + } + + // Test error codes (consuming the values) + for (solana_error, pinocchio_error) in + solana_errors.into_iter().zip(pinocchio_errors.into_iter()) + { + let solana_code: u32 = solana_error.into(); + let pinocchio_code: u32 = pinocchio_error.into(); + assert_eq!( + solana_code, pinocchio_code, + "Error codes differ for error variants" + ); + } +} diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index 4f223a7203..04f322cedb 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -7,6 +7,7 @@ use anchor_lang::{ solana_program::{instruction::Instruction, pubkey::Pubkey}, InstructionData, }; +use light_sdk::{cpi::CpiSigner, derive_light_cpi_signer}; use light_system_program::utils::get_registered_program_pda; pub mod create_pda; pub use create_pda::*; @@ -16,10 +17,13 @@ use light_compressed_account::instruction_data::{ use light_sdk::cpi::CpiAccountsConfig; declare_id!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); + #[program] pub mod system_cpi_test { - use light_sdk::{cpi::invoke_light_system_program, PROGRAM_ID_LIGHT_SYSTEM}; + use light_sdk::{constants::PROGRAM_ID_LIGHT_SYSTEM, cpi::invoke_light_system_program}; use super::*; @@ -73,11 +77,12 @@ pub mod system_cpi_test { (account_infos, account_metas) }; let instruction = Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM, + program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), accounts: account_metas, data: inputs, }; - invoke_light_system_program(&crate::ID, &account_infos, instruction) + let cpi_config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); + invoke_light_system_program(&account_infos, instruction, cpi_config.bump()) .map_err(ProgramError::from)?; Ok(()) } diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs index 72f9dabcb3..26d67ee252 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs @@ -4,19 +4,23 @@ use anchor_lang::{prelude::*, Discriminator}; use light_sdk::{ account::LightAccount, address::v1::derive_address, - cpi::{CpiAccounts, CpiInputs}, - instruction::{account_meta::CompressedAccountMeta, tree_info::PackedAddressTreeInfo}, - LightDiscriminator, LightHasher, NewAddressParamsPacked, ValidityProof, + cpi::{CpiAccounts, CpiInputs, CpiSigner}, + derive_light_cpi_signer, + instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo, ValidityProof}, + LightDiscriminator, LightHasher, }; declare_id!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); + #[program] pub mod sdk_anchor_test { use super::*; - pub fn with_nested_data<'info>( + pub fn create_compressed_account<'info>( ctx: Context<'_, '_, '_, 'info, WithNestedData<'info>>, proof: ValidityProof, address_tree_info: PackedAddressTreeInfo, @@ -26,7 +30,7 @@ pub mod sdk_anchor_test { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; @@ -37,12 +41,7 @@ pub mod sdk_anchor_test { .key(), &crate::ID, ); - let new_address_params = NewAddressParamsPacked { - seed: address_seed, - address_queue_account_index: address_tree_info.address_queue_pubkey_index, - address_merkle_tree_root_index: address_tree_info.root_index, - address_merkle_tree_account_index: address_tree_info.address_merkle_tree_pubkey_index, - }; + let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_init( &crate::ID, @@ -68,7 +67,7 @@ pub mod sdk_anchor_test { Ok(()) } - pub fn update_nested_data<'info>( + pub fn update_compressed_account<'info>( ctx: Context<'_, '_, '_, 'info, UpdateNestedData<'info>>, proof: ValidityProof, my_compressed_account: MyCompressedAccount, @@ -87,7 +86,7 @@ pub mod sdk_anchor_test { let light_cpi_accounts = CpiAccounts::new( ctx.accounts.signer.as_ref(), ctx.remaining_accounts, - crate::ID, + crate::LIGHT_CPI_SIGNER, ) .map_err(ProgramError::from)?; @@ -117,6 +116,7 @@ pub mod sdk_anchor_test { #[event] #[derive(Clone, Debug, Default, LightHasher, LightDiscriminator)] pub struct MyCompressedAccount { + #[hash] pub name: String, pub nested: NestedData, } diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs index 8429af67e3..e49867558b 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs @@ -1,6 +1,6 @@ #![cfg(feature = "test-sbf")] -use anchor_lang::{AnchorDeserialize, InstructionData, ToAccountMetas}; +use anchor_lang::AnchorDeserialize; use light_client::indexer::CompressedAccount; use light_program_test::{ indexer::TestIndexerExtensions, program_test::LightProgramTest, AddressWithTree, Indexer, @@ -8,15 +8,12 @@ use light_program_test::{ }; use light_sdk::{ address::v1::derive_address, - instruction::{ - account_meta::CompressedAccountMeta, accounts::SystemAccountMetaConfig, - pack_accounts::PackedAccounts, - }, + instruction::{account_meta::CompressedAccountMeta, PackedAccounts, SystemAccountMetaConfig}, }; use light_test_utils::{Rpc, RpcError}; use sdk_anchor_test::{MyCompressedAccount, NestedData}; use solana_sdk::{ - instruction::Instruction, + instruction::{AccountMeta, Instruction}, signature::{Keypair, Signature, Signer}, }; @@ -29,15 +26,13 @@ async fn test_sdk_test() { let address_tree_info = rpc.get_address_tree_v1(); - rpc.get_state_merkle_tree_account(); - let (address, _) = derive_address( &[b"compressed", b"test".as_slice()], &address_tree_info.tree, &sdk_anchor_test::ID, ); - with_nested_data("test".to_string(), &mut rpc, &payer, &address) + create_compressed_account("test".to_string(), &mut rpc, &payer, &address) .await .unwrap(); @@ -52,7 +47,7 @@ async fn test_sdk_test() { let record = MyCompressedAccount::deserialize(&mut &record[..]).unwrap(); assert_eq!(record.nested.one, 1); - update_nested_data( + update_compressed_account( &mut rpc, NestedData { one: 2, @@ -89,7 +84,7 @@ async fn test_sdk_test() { assert_eq!(record.nested.one, 2); } -async fn with_nested_data( +async fn create_compressed_account( name: String, rpc: &mut LightProgramTest, payer: &Keypair, @@ -121,28 +116,30 @@ async fn with_nested_data( let (remaining_accounts, _, _) = remaining_accounts.to_account_metas(); - let instruction_data = sdk_anchor_test::instruction::WithNestedData { - proof: rpc_result.proof, - address_tree_info: packed_accounts.address_trees[0], - name, - output_tree_index, - }; - - let accounts = sdk_anchor_test::accounts::WithNestedData { - signer: payer.pubkey(), - }; - let instruction = Instruction { program_id: sdk_anchor_test::ID, - accounts: [accounts.to_account_metas(Some(true)), remaining_accounts].concat(), - data: instruction_data.data(), + accounts: [ + vec![AccountMeta::new(payer.pubkey(), true)], + remaining_accounts, + ] + .concat(), + data: { + use anchor_lang::InstructionData; + sdk_anchor_test::instruction::CreateCompressedAccount { + proof: rpc_result.proof, + address_tree_info: packed_accounts.address_trees[0], + output_tree_index, + name, + } + .data() + }, }; rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) .await } -async fn update_nested_data( +async fn update_compressed_account( rpc: &mut LightProgramTest, nested_data: NestedData, payer: &Keypair, @@ -170,25 +167,27 @@ async fn update_nested_data( &mut compressed_account.data.as_mut().unwrap().data.as_slice(), ) .unwrap(); - let instruction_data = sdk_anchor_test::instruction::UpdateNestedData { - proof: rpc_result.proof, - my_compressed_account, - account_meta: CompressedAccountMeta { - tree_info: packed_tree_accounts.packed_tree_infos[0], - address: compressed_account.address.unwrap(), - output_state_tree_index: packed_tree_accounts.output_tree_index, - }, - nested_data, - }; - - let accounts = sdk_anchor_test::accounts::UpdateNestedData { - signer: payer.pubkey(), - }; - let instruction = Instruction { program_id: sdk_anchor_test::ID, - accounts: [accounts.to_account_metas(Some(true)), remaining_accounts].concat(), - data: instruction_data.data(), + accounts: [ + vec![AccountMeta::new(payer.pubkey(), true)], + remaining_accounts, + ] + .concat(), + data: { + use anchor_lang::InstructionData; + sdk_anchor_test::instruction::UpdateCompressedAccount { + proof: rpc_result.proof, + my_compressed_account, + account_meta: CompressedAccountMeta { + tree_info: packed_tree_accounts.packed_tree_infos[0], + address: compressed_account.address.unwrap(), + output_state_tree_index: packed_tree_accounts.output_tree_index, + }, + nested_data, + } + .data() + }, }; rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) diff --git a/program-tests/sdk-pinocchio-test/Cargo.toml b/program-tests/sdk-pinocchio-test/Cargo.toml new file mode 100644 index 0000000000..1cc149c56c --- /dev/null +++ b/program-tests/sdk-pinocchio-test/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "sdk-pinocchio-test" +version = "1.0.0" +description = "Test program using generalized account compression" +repository = "https://github.com/Lightprotocol/light-protocol" +license = "Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "sdk_pinocchio_test" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +test-sbf = [] +default = [] + +[dependencies] +light-sdk-pinocchio = { workspace = true, features = ["v2"] } +light-hasher = { workspace = true } +pinocchio = { workspace = true } +light-macros = { workspace = true } +borsh = { workspace = true } + +[dev-dependencies] +light-program-test = { workspace = true, features = ["devenv"] } +tokio = { workspace = true } +solana-sdk = { workspace = true } +light-hasher = { workspace = true, features = ["solana"] } +light-compressed-account = { workspace = true, features = ["solana"] } +light-sdk = { workspace = true } + +[lints.rust.unexpected_cfgs] +level = "allow" +check-cfg = [ + 'cfg(target_os, values("solana"))', + 'cfg(feature, values("frozen-abi", "no-entrypoint"))', +] diff --git a/program-tests/sdk-pinocchio-test/Xargo.toml b/program-tests/sdk-pinocchio-test/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/program-tests/sdk-pinocchio-test/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/program-tests/sdk-pinocchio-test/src/create_pda.rs b/program-tests/sdk-pinocchio-test/src/create_pda.rs new file mode 100644 index 0000000000..d15fdaf3eb --- /dev/null +++ b/program-tests/sdk-pinocchio-test/src/create_pda.rs @@ -0,0 +1,91 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use light_sdk_pinocchio::{ + account::LightAccount, + cpi::{CpiAccounts, CpiAccountsConfig, CpiInputs}, + error::LightSdkError, + instruction::PackedAddressTreeInfo, + light_hasher::hash_to_field_size::hashv_to_bn254_field_size_be_const_array, + LightDiscriminator, LightHasher, ValidityProof, +}; +use pinocchio::account_info::AccountInfo; + +/// CU usage: +/// - sdk pre system program cpi 10,942 CU +/// - total with V1 tree: 307,784 CU +/// - total with V2 tree: 138,876 CU +pub fn create_pda( + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> Result<(), LightSdkError> { + let mut instruction_data = instruction_data; + let instruction_data = CreatePdaInstructionData::deserialize(&mut instruction_data) + .map_err(|_| LightSdkError::Borsh)?; + let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); + let cpi_accounts = CpiAccounts::new_with_config( + &accounts[0], + &accounts[instruction_data.system_accounts_offset as usize..], + config, + )?; + + let address_tree_info = instruction_data.address_tree_info; + let (address, address_seed) = if BATCHED { + let tree_acounts = cpi_accounts.tree_accounts(); + let index = tree_acounts[instruction_data + .address_tree_info + .address_merkle_tree_pubkey_index as usize] + .key(); + let address_seed = hashv_to_bn254_field_size_be_const_array::<3>(&[ + b"compressed", + instruction_data.data.as_slice(), + ])?; + let address = light_sdk_pinocchio::light_compressed_account::address::derive_address( + &address_seed, + index, + &crate::ID, + ); + (address, address_seed) + } else { + light_sdk_pinocchio::address::v1::derive_address( + &[b"compressed", instruction_data.data.as_slice()], + cpi_accounts.tree_accounts() + [address_tree_info.address_merkle_tree_pubkey_index as usize] + .key(), + &crate::ID, + ) + }; + + let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); + + let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_init( + &crate::ID, + Some(address), + instruction_data.output_merkle_tree_index, + ); + + my_compressed_account.data = instruction_data.data; + + let cpi_inputs = CpiInputs::new_with_address( + instruction_data.proof, + vec![my_compressed_account.to_account_info()?], + vec![new_address_params], + ); + cpi_inputs.invoke_light_system_program(cpi_accounts)?; + Ok(()) +} + +#[derive( + Clone, Debug, Default, LightHasher, LightDiscriminator, BorshDeserialize, BorshSerialize, +)] +pub struct MyCompressedAccount { + pub data: [u8; 31], +} + +#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +pub struct CreatePdaInstructionData { + pub proof: ValidityProof, + pub address_tree_info: PackedAddressTreeInfo, + pub output_merkle_tree_index: u8, + pub data: [u8; 31], + pub system_accounts_offset: u8, + pub tree_accounts_offset: u8, +} diff --git a/program-tests/sdk-pinocchio-test/src/lib.rs b/program-tests/sdk-pinocchio-test/src/lib.rs new file mode 100644 index 0000000000..6f17905374 --- /dev/null +++ b/program-tests/sdk-pinocchio-test/src/lib.rs @@ -0,0 +1,48 @@ +use light_macros::pubkey_array; +use light_sdk_pinocchio::{derive_light_cpi_signer, error::LightSdkError, CpiSigner}; +use pinocchio::{ + account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey, +}; +pub mod create_pda; +pub mod update_pda; + +pub const ID: Pubkey = pubkey_array!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); + +entrypoint!(process_instruction); + +#[repr(u8)] +pub enum InstructionType { + CreatePdaBorsh = 0, + UpdatePdaBorsh = 1, +} + +impl TryFrom for InstructionType { + type Error = LightSdkError; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(InstructionType::CreatePdaBorsh), + 1 => Ok(InstructionType::UpdatePdaBorsh), + _ => panic!("Invalid instruction discriminator."), + } + } +} + +pub fn process_instruction( + _program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> Result<(), ProgramError> { + let discriminator = InstructionType::try_from(instruction_data[0]).unwrap(); + match discriminator { + InstructionType::CreatePdaBorsh => { + create_pda::create_pda::(accounts, &instruction_data[1..]) + } + InstructionType::UpdatePdaBorsh => { + update_pda::update_pda::(accounts, &instruction_data[1..]) + } + }?; + Ok(()) +} diff --git a/program-tests/sdk-pinocchio-test/src/update_pda.rs b/program-tests/sdk-pinocchio-test/src/update_pda.rs new file mode 100644 index 0000000000..2207984588 --- /dev/null +++ b/program-tests/sdk-pinocchio-test/src/update_pda.rs @@ -0,0 +1,68 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use light_sdk_pinocchio::{ + account::LightAccount, + cpi::{CpiAccounts, CpiAccountsConfig, CpiInputs}, + error::LightSdkError, + instruction::account_meta::CompressedAccountMeta, + ValidityProof, +}; +use pinocchio::{account_info::AccountInfo, log::sol_log_compute_units}; + +use crate::create_pda::MyCompressedAccount; + +/// CU usage: +/// - sdk pre system program 9,183k CU +/// - total with V2 tree: 50,194 CU (proof by index) +/// - total with V2 tree: 67,723 CU (proof by index) +pub fn update_pda( + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> Result<(), LightSdkError> { + sol_log_compute_units(); + let mut instruction_data = instruction_data; + let instruction_data = UpdatePdaInstructionData::deserialize(&mut instruction_data) + .map_err(|_| LightSdkError::Borsh)?; + sol_log_compute_units(); + + let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_mut( + &crate::ID, + &instruction_data.my_compressed_account.meta, + MyCompressedAccount { + data: instruction_data.my_compressed_account.data, + }, + )?; + sol_log_compute_units(); + + my_compressed_account.data = instruction_data.new_data; + + let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); + sol_log_compute_units(); + let cpi_accounts = CpiAccounts::new_with_config( + &accounts[0], + &accounts[instruction_data.system_accounts_offset as usize..], + config, + )?; + sol_log_compute_units(); + let cpi_inputs = CpiInputs::new( + instruction_data.proof, + vec![my_compressed_account.to_account_info()?], + ); + sol_log_compute_units(); + cpi_inputs.invoke_light_system_program(cpi_accounts)?; + + Ok(()) +} + +#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +pub struct UpdatePdaInstructionData { + pub proof: ValidityProof, + pub my_compressed_account: UpdateMyCompressedAccount, + pub new_data: [u8; 31], + pub system_accounts_offset: u8, +} + +#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +pub struct UpdateMyCompressedAccount { + pub meta: CompressedAccountMeta, + pub data: [u8; 31], +} diff --git a/program-tests/sdk-pinocchio-test/tests/test.rs b/program-tests/sdk-pinocchio-test/tests/test.rs new file mode 100644 index 0000000000..cfafdd0854 --- /dev/null +++ b/program-tests/sdk-pinocchio-test/tests/test.rs @@ -0,0 +1,206 @@ +// #![cfg(feature = "test-sbf")] + +use borsh::BorshSerialize; +use light_compressed_account::{ + address::derive_address, compressed_account::CompressedAccountWithMerkleContext, + hashv_to_bn254_field_size_be, +}; +use light_program_test::{ + program_test::LightProgramTest, AddressWithTree, Indexer, ProgramTestConfig, Rpc, RpcError, +}; +use light_sdk::instruction::{PackedAccounts, SystemAccountMetaConfig}; +use light_sdk_pinocchio::instruction::{account_meta::CompressedAccountMeta, PackedStateTreeInfo}; +use sdk_pinocchio_test::{ + create_pda::CreatePdaInstructionData, + update_pda::{UpdateMyCompressedAccount, UpdatePdaInstructionData}, +}; +use solana_sdk::{ + instruction::Instruction, + pubkey::Pubkey, + signature::{Keypair, Signer}, +}; + +#[tokio::test] +async fn test_sdk_test() { + let config = ProgramTestConfig::new_v2( + false, + Some(vec![( + "sdk_pinocchio_test", + Pubkey::new_from_array(sdk_pinocchio_test::ID), + )]), + ); + let mut rpc = LightProgramTest::new(config).await.unwrap(); + let payer = rpc.get_payer().insecure_clone(); + + let address_tree_pubkey = rpc.get_address_merkle_tree_v2(); + let account_data = [1u8; 31]; + + // // V1 trees + // let (address, _) = light_sdk::address::derive_address( + // &[b"compressed", &account_data], + // &address_tree_info, + // &Pubkey::new_from_array(sdk_pinocchio_test::ID), + // ); + // Batched trees + let address_seed = hashv_to_bn254_field_size_be(&[b"compressed", account_data.as_slice()]); + println!("seed {:?}", address_seed); + let address = derive_address( + &address_seed, + &address_tree_pubkey.to_bytes(), + &sdk_pinocchio_test::ID, + ); + println!("address {:?}", address); + println!("address tree pubkey: {:?}", address_tree_pubkey.to_bytes()); + let output_queue = rpc.get_random_state_tree_info().queue; + println!("output_queue tree pubkey: {:?}", output_queue.to_bytes()); + + create_pda( + &payer, + &mut rpc, + &output_queue, + account_data, + address_tree_pubkey, + address, + ) + .await + .unwrap(); + + let compressed_pda = rpc + .indexer() + .unwrap() + .get_compressed_accounts_by_owner( + &Pubkey::new_from_array(sdk_pinocchio_test::ID), + None, + None, + ) + .await + .unwrap() + .value + .items[0] + .clone(); + assert_eq!(compressed_pda.address.unwrap(), address); + + update_pda(&payer, &mut rpc, [2u8; 31], compressed_pda.into()) + .await + .unwrap(); +} + +pub async fn create_pda( + payer: &Keypair, + rpc: &mut LightProgramTest, + merkle_tree_pubkey: &Pubkey, + account_data: [u8; 31], + address_tree_pubkey: Pubkey, + address: [u8; 32], +) -> Result<(), RpcError> { + let system_account_meta_config = + SystemAccountMetaConfig::new(Pubkey::new_from_array(sdk_pinocchio_test::ID)); + let mut accounts = PackedAccounts::default(); + accounts.add_pre_accounts_signer(payer.pubkey()); + accounts.add_system_accounts(system_account_meta_config); + + let rpc_result = rpc + .get_validity_proof( + vec![], + vec![AddressWithTree { + address, + tree: address_tree_pubkey, + }], + None, + ) + .await? + .value; + + let output_merkle_tree_index = accounts.insert_or_get(*merkle_tree_pubkey); + let packed_address_tree_info = rpc_result.pack_tree_infos(&mut accounts).address_trees[0]; + let (accounts, system_accounts_offset, tree_accounts_offset) = accounts.to_account_metas(); + let instruction_data = CreatePdaInstructionData { + proof: rpc_result.proof, + address_tree_info: packed_address_tree_info, + data: account_data, + output_merkle_tree_index, + system_accounts_offset: system_accounts_offset as u8, + tree_accounts_offset: tree_accounts_offset as u8, + }; + let inputs = instruction_data.try_to_vec().unwrap(); + + let instruction = Instruction { + program_id: Pubkey::new_from_array(sdk_pinocchio_test::ID), + accounts, + data: [&[0u8][..], &inputs[..]].concat(), + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) + .await?; + Ok(()) +} + +pub async fn update_pda( + payer: &Keypair, + rpc: &mut LightProgramTest, + new_account_data: [u8; 31], + compressed_account: CompressedAccountWithMerkleContext, +) -> Result<(), RpcError> { + let system_account_meta_config = + SystemAccountMetaConfig::new(Pubkey::new_from_array(sdk_pinocchio_test::ID)); + let mut accounts = PackedAccounts::default(); + accounts.add_pre_accounts_signer(payer.pubkey()); + accounts.add_system_accounts(system_account_meta_config); + + let rpc_result = rpc + .get_validity_proof(vec![compressed_account.hash().unwrap()], vec![], None) + .await? + .value; + + let packed_accounts = rpc_result + .pack_tree_infos(&mut accounts) + .state_trees + .unwrap(); + + let light_sdk_meta = CompressedAccountMeta { + tree_info: packed_accounts.packed_tree_infos[0], + address: compressed_account.compressed_account.address.unwrap(), + output_state_tree_index: packed_accounts.output_tree_index, + }; + + // Convert to pinocchio CompressedAccountMeta + let meta = CompressedAccountMeta { + tree_info: PackedStateTreeInfo { + root_index: light_sdk_meta.tree_info.root_index, + prove_by_index: light_sdk_meta.tree_info.prove_by_index, + merkle_tree_pubkey_index: light_sdk_meta.tree_info.merkle_tree_pubkey_index, + queue_pubkey_index: light_sdk_meta.tree_info.queue_pubkey_index, + leaf_index: light_sdk_meta.tree_info.leaf_index, + }, + address: light_sdk_meta.address, + output_state_tree_index: light_sdk_meta.output_state_tree_index, + }; + + let (accounts, system_accounts_offset, _) = accounts.to_account_metas(); + let instruction_data = UpdatePdaInstructionData { + my_compressed_account: UpdateMyCompressedAccount { + meta, + data: compressed_account + .compressed_account + .data + .unwrap() + .data + .try_into() + .unwrap(), + }, + proof: light_sdk_pinocchio::ValidityProof(None), + new_data: new_account_data, + system_accounts_offset: system_accounts_offset as u8, + }; + let inputs = instruction_data.try_to_vec().unwrap(); + + let instruction = Instruction { + program_id: Pubkey::new_from_array(sdk_pinocchio_test::ID), + accounts, + data: [&[1u8][..], &inputs[..]].concat(), + }; + + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer]) + .await?; + Ok(()) +} diff --git a/program-tests/sdk-test/Cargo.toml b/program-tests/sdk-test/Cargo.toml index f46b53929a..6929b36a55 100644 --- a/program-tests/sdk-test/Cargo.toml +++ b/program-tests/sdk-test/Cargo.toml @@ -19,7 +19,8 @@ test-sbf = [] default = [] [dependencies] -light-sdk = { workspace = true, features = ["solana"] } +light-sdk = { workspace = true } +light-sdk-types = { workspace = true } light-hasher = { workspace = true, features = ["solana"] } solana-program = { workspace = true } light-macros = { workspace = true, features = ["solana"] } diff --git a/program-tests/sdk-test/src/create_pda.rs b/program-tests/sdk-test/src/create_pda.rs index 980c47678e..a728bc3a0c 100644 --- a/program-tests/sdk-test/src/create_pda.rs +++ b/program-tests/sdk-test/src/create_pda.rs @@ -1,21 +1,18 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_sdk::{ account::LightAccount, - cpi::{ - create_light_system_progam_instruction_invoke_cpi, invoke_light_system_program, - CpiAccounts, CpiAccountsConfig, CpiInputs, - }, + cpi::{CpiAccounts, CpiAccountsConfig, CpiInputs}, error::LightSdkError, - hash_to_field_size::hashv_to_bn254_field_size_be_const_array, - instruction::tree_info::PackedAddressTreeInfo, - LightDiscriminator, LightHasher, NewAddressParamsPacked, ValidityProof, + instruction::{PackedAddressTreeInfo, ValidityProof}, + light_hasher::hash_to_field_size::hashv_to_bn254_field_size_be_const_array, + LightDiscriminator, LightHasher, }; use solana_program::account_info::AccountInfo; +/// TODO: write test program with A8JgviaEAByMVLBhcebpDQ7NMuZpqBTBigC1b83imEsd (inconvenient program id) /// CU usage: /// - sdk pre system program cpi 10,942 CU -/// - total with V1 tree: 307,784 CU -/// - total with V2 tree: 138,876 CU +/// - total with V2 tree: 45,758 CU pub fn create_pda( accounts: &[AccountInfo], instruction_data: &[u8], @@ -23,12 +20,7 @@ pub fn create_pda( let mut instruction_data = instruction_data; let instruction_data = CreatePdaInstructionData::deserialize(&mut instruction_data) .map_err(|_| LightSdkError::Borsh)?; - let config = CpiAccountsConfig { - self_program: crate::ID, - cpi_context: false, - sol_pool_pda: false, - sol_compression_recipient: false, - }; + let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); let cpi_accounts = CpiAccounts::new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], @@ -62,12 +54,7 @@ pub fn create_pda( &crate::ID, ) }; - let new_address_params = NewAddressParamsPacked { - seed: address_seed, - address_queue_account_index: address_tree_info.address_queue_pubkey_index, - address_merkle_tree_root_index: address_tree_info.root_index, - address_merkle_tree_account_index: address_tree_info.address_merkle_tree_pubkey_index, - }; + let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_init( &crate::ID, @@ -82,9 +69,7 @@ pub fn create_pda( vec![my_compressed_account.to_account_info()?], vec![new_address_params], ); - let instruction = create_light_system_progam_instruction_invoke_cpi(cpi_inputs, &cpi_accounts)?; - - invoke_light_system_program(&crate::ID, &cpi_accounts.to_account_infos(), instruction)?; + cpi_inputs.invoke_light_system_program(cpi_accounts)?; Ok(()) } diff --git a/program-tests/sdk-test/src/lib.rs b/program-tests/sdk-test/src/lib.rs index dfbb77f309..8fb2b71b2c 100644 --- a/program-tests/sdk-test/src/lib.rs +++ b/program-tests/sdk-test/src/lib.rs @@ -1,5 +1,5 @@ use light_macros::pubkey; -use light_sdk::error::LightSdkError; +use light_sdk::{cpi::CpiSigner, derive_light_cpi_signer, error::LightSdkError}; use solana_program::{ account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey, }; @@ -8,6 +8,8 @@ pub mod create_pda; pub mod update_pda; pub const ID: Pubkey = pubkey!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); +pub const LIGHT_CPI_SIGNER: CpiSigner = + derive_light_cpi_signer!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); entrypoint!(process_instruction); diff --git a/program-tests/sdk-test/src/update_pda.rs b/program-tests/sdk-test/src/update_pda.rs index 6aa4c75cb5..5d96875783 100644 --- a/program-tests/sdk-test/src/update_pda.rs +++ b/program-tests/sdk-test/src/update_pda.rs @@ -1,25 +1,23 @@ use borsh::{BorshDeserialize, BorshSerialize}; use light_sdk::{ account::LightAccount, - cpi::{ - create_light_system_progam_instruction_invoke_cpi, invoke_light_system_program, - CpiAccounts, CpiAccountsConfig, CpiInputs, - }, + cpi::{CpiAccounts, CpiAccountsConfig, CpiInputs}, error::LightSdkError, - instruction::account_meta::CompressedAccountMeta, - ValidityProof, + instruction::{account_meta::CompressedAccountMeta, ValidityProof}, }; -use solana_program::account_info::AccountInfo; +use solana_program::{account_info::AccountInfo, log::sol_log_compute_units}; use crate::create_pda::MyCompressedAccount; /// CU usage: /// - sdk pre system program 9,183k CU /// - total with V2 tree: 50,194 CU (proof by index) +/// - 51,609 pub fn update_pda( accounts: &[AccountInfo], instruction_data: &[u8], ) -> Result<(), LightSdkError> { + sol_log_compute_units(); let mut instruction_data = instruction_data; let instruction_data = UpdatePdaInstructionData::deserialize(&mut instruction_data) .map_err(|_| LightSdkError::Borsh)?; @@ -31,27 +29,24 @@ pub fn update_pda( data: instruction_data.my_compressed_account.data, }, )?; + sol_log_compute_units(); my_compressed_account.data = instruction_data.new_data; - let config = CpiAccountsConfig { - self_program: crate::ID, - cpi_context: false, - sol_pool_pda: false, - sol_compression_recipient: false, - }; + let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); + sol_log_compute_units(); let cpi_accounts = CpiAccounts::new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, )?; + sol_log_compute_units(); let cpi_inputs = CpiInputs::new( instruction_data.proof, vec![my_compressed_account.to_account_info()?], ); - let instruction = create_light_system_progam_instruction_invoke_cpi(cpi_inputs, &cpi_accounts)?; - - invoke_light_system_program(&crate::ID, &cpi_accounts.to_account_infos(), instruction)?; + sol_log_compute_units(); + cpi_inputs.invoke_light_system_program(cpi_accounts)?; Ok(()) } diff --git a/program-tests/sdk-test/tests/test.rs b/program-tests/sdk-test/tests/test.rs index 8d4ec91bb4..a52e497299 100644 --- a/program-tests/sdk-test/tests/test.rs +++ b/program-tests/sdk-test/tests/test.rs @@ -9,8 +9,7 @@ use light_program_test::{ program_test::LightProgramTest, AddressWithTree, Indexer, ProgramTestConfig, Rpc, RpcError, }; use light_sdk::instruction::{ - account_meta::CompressedAccountMeta, accounts::SystemAccountMetaConfig, - pack_accounts::PackedAccounts, + account_meta::CompressedAccountMeta, PackedAccounts, SystemAccountMetaConfig, }; use sdk_test::{ create_pda::CreatePdaInstructionData, diff --git a/program-tests/system-cpi-test/tests/test.rs b/program-tests/system-cpi-test/tests/test.rs index 92c80074af..b8e30c92a9 100644 --- a/program-tests/system-cpi-test/tests/test.rs +++ b/program-tests/system-cpi-test/tests/test.rs @@ -1836,7 +1836,12 @@ async fn perform_create_pda( } else { let input_account_len = input_accounts.as_ref().unwrap().len(); index += input_account_len; - Some(account_root_indices[..index].to_vec()) + Some( + account_root_indices[..index] + .iter() + .map(|x| x.root_index()) + .collect::>(), + ) }; let read_only_accounts = read_only_accounts.as_ref().map(|read_only_accounts| { @@ -1844,7 +1849,8 @@ async fn perform_create_pda( .iter() .map(|x| { index += 1; - x.into_read_only(account_root_indices[index - 1]).unwrap() + x.into_read_only(account_root_indices[index - 1].root_index()) + .unwrap() }) .collect::>() }); @@ -1983,7 +1989,10 @@ pub async fn perform_with_input_accounts Vec { - let cpi_signer = find_cpi_signer_macro!(&config.self_program).0; + let cpi_signer = Pubkey::new_from_array(LIGHT_CPI_SIGNER.cpi_signer); println!("cpi signer {:?}", cpi_signer); let default_pubkeys = SystemAccountPubkeys::default(); let mut vec = if config.small_ix { diff --git a/program-tests/system-test/tests/test.rs b/program-tests/system-test/tests/test.rs index 8bd9aede1c..e6270e776d 100644 --- a/program-tests/system-test/tests/test.rs +++ b/program-tests/system-test/tests/test.rs @@ -234,7 +234,7 @@ pub async fn failing_transaction_inputs( .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(); (root_indices, proof_rpc_res.value.proof.0) } else { @@ -1042,7 +1042,7 @@ async fn invoke_test() { .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), Some(proof), @@ -1095,7 +1095,7 @@ async fn invoke_test() { .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), Some(proof), @@ -1132,7 +1132,7 @@ async fn invoke_test() { .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), Some(proof), @@ -1593,7 +1593,7 @@ async fn test_with_compression() { .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), Some(proof), @@ -1964,7 +1964,9 @@ async fn batch_invoke_test() { // No proof since value is in output queue assert!(proof_rpc_result.value.proof.0.is_none()); // No root index since value is in output queue - assert!(proof_rpc_result.value.accounts[0].root_index.is_none()); + assert!(proof_rpc_result.value.accounts[0] + .root_index + .proof_by_index()); let input_compressed_accounts = vec![compressed_account_with_context.compressed_account]; @@ -2186,7 +2188,7 @@ async fn batch_invoke_test() { .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), Some(proof), @@ -2483,7 +2485,7 @@ pub async fn double_spend_compressed_account( .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), &Vec::new(), proof_rpc_result.value.proof.0, diff --git a/program-tests/utils/src/e2e_test_env.rs b/program-tests/utils/src/e2e_test_env.rs index 9a5f62ca06..e673f54104 100644 --- a/program-tests/utils/src/e2e_test_env.rs +++ b/program-tests/utils/src/e2e_test_env.rs @@ -148,9 +148,9 @@ use light_registry::{ ForesterConfig, }; use light_sdk::{ + address::NewAddressParamsAssignedPacked, + constants::{ADDRESS_MERKLE_TREE_ROOTS, CPI_AUTHORITY_PDA_SEED, STATE_MERKLE_TREE_ROOTS}, token::{AccountState, TokenDataWithMerkleContext}, - NewAddressParamsAssignedPacked, ADDRESS_MERKLE_TREE_ROOTS, CPI_AUTHORITY_PDA_SEED, - STATE_MERKLE_TREE_ROOTS, }; use light_sparse_merkle_tree::{ changelog::ChangelogEntry, indexed_changelog::IndexedChangelogEntry, SparseMerkleTree, @@ -2656,12 +2656,7 @@ where .await .unwrap(); - root_indices = proof_rpc_res - .value - .accounts - .iter() - .map(|x| x.root_index) - .collect::>(); + root_indices = proof_rpc_res.value.get_root_indices(); if let Some(proof_rpc_res) = proof_rpc_res.value.proof.0 { proof = Some(proof_rpc_res); @@ -2692,21 +2687,16 @@ where } if !read_only_accounts.is_empty() { - let account_root_indices: Vec<_> = proof_rpc_res - .value - .accounts - .iter() - .map(|x| x.root_index) - .collect(); + let account_root_indices: Vec<_> = proof_rpc_res.value.get_root_indices(); for (i, input_account) in read_only_accounts.iter_mut().enumerate() { - if let Some(root_index) = account_root_indices - .get(i + input_accounts.len()) - .copied() - .flatten() + if let Some(root_index) = + account_root_indices.get(i + input_accounts.len()).copied() { - input_account.root_index = root_index; - } else { - input_account.merkle_context.prove_by_index = true; + if let Some(root_index) = root_index { + input_account.root_index = root_index; + } else { + input_account.merkle_context.prove_by_index = true; + } } } } diff --git a/program-tests/utils/src/pack.rs b/program-tests/utils/src/pack.rs index a86b4495fe..8bd4f474b1 100644 --- a/program-tests/utils/src/pack.rs +++ b/program-tests/utils/src/pack.rs @@ -6,11 +6,13 @@ use light_compressed_account::{ PackedCompressedAccountWithMerkleContext, PackedMerkleContext, PackedReadOnlyCompressedAccount, ReadOnlyCompressedAccount, }, - instruction_data::data::{NewAddressParams, ReadOnlyAddress}, + instruction_data::data::{ + NewAddressParams, OutputCompressedAccountWithPackedContext, ReadOnlyAddress, + }, }; -use light_sdk::{ - NewAddressParamsAssigned, NewAddressParamsAssignedPacked, NewAddressParamsPacked, - OutputCompressedAccountWithPackedContext, PackedReadOnlyAddress, +use light_sdk::address::{ + NewAddressParamsAssigned, NewAddressParamsAssignedPacked, PackedNewAddressParams, + PackedReadOnlyAddress, }; use solana_sdk::pubkey::Pubkey; @@ -82,16 +84,16 @@ pub fn pack_read_only_accounts( pub fn pack_new_address_params( new_address_params: &[NewAddressParams], remaining_accounts: &mut HashMap, -) -> Vec { +) -> Vec { let mut new_address_params_packed = new_address_params .iter() - .map(|x| NewAddressParamsPacked { + .map(|x| PackedNewAddressParams { seed: x.seed, address_merkle_tree_root_index: x.address_merkle_tree_root_index, address_merkle_tree_account_index: 0, // will be assigned later address_queue_account_index: 0, // will be assigned later }) - .collect::>(); + .collect::>(); let mut next_index: usize = remaining_accounts.len(); for (i, params) in new_address_params.iter().enumerate() { match remaining_accounts.get(¶ms.address_merkle_tree_pubkey.into()) { diff --git a/program-tests/utils/src/spl.rs b/program-tests/utils/src/spl.rs index ceb4ee04d1..163f96757e 100644 --- a/program-tests/utils/src/spl.rs +++ b/program-tests/utils/src/spl.rs @@ -620,12 +620,7 @@ pub async fn compressed_transfer_22_test< &authority_signer.pubkey(), // authority &input_merkle_tree_context, &output_compressed_accounts, - &rpc_result - .value - .accounts - .iter() - .map(|x| x.root_index) - .collect::>(), + &rpc_result.value.get_root_indices(), &rpc_result.value.proof.0, input_compressed_account_token_data .iter() @@ -795,8 +790,8 @@ pub async fn decompress_test>(), // root_indices + .map(|x| x.root_index.root_index()) + .collect::>(), &Some(proof_rpc_result.value.proof.0.unwrap_or_default()), input_compressed_accounts .iter() @@ -1190,7 +1185,7 @@ pub async fn approve_test>(), proof: proof_rpc_result.value.proof.0.unwrap_or_default(), }; @@ -1355,7 +1350,7 @@ pub async fn revoke_test>(), proof: proof_rpc_result.value.proof.0.unwrap_or_default(), }; @@ -1521,7 +1516,7 @@ pub async fn freeze_or_thaw_test< .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(), proof: proof_rpc_result.value.proof.0.unwrap_or_default(), }; @@ -1825,7 +1820,7 @@ pub async fn create_burn_test_instruction>(), proof, mint, diff --git a/program-tests/utils/src/system_program.rs b/program-tests/utils/src/system_program.rs index 42fc4bece7..54a12e7511 100644 --- a/program-tests/utils/src/system_program.rs +++ b/program-tests/utils/src/system_program.rs @@ -363,7 +363,7 @@ pub async fn compressed_transaction_test< .value .accounts .iter() - .map(|x| x.root_index) + .map(|x| x.root_index.root_index()) .collect::>(); if let Some(proof_rpc_res) = proof_rpc_res.value.proof.0 { diff --git a/programs/account-compression/Cargo.toml b/programs/account-compression/Cargo.toml index 36bbe1021c..222110f664 100644 --- a/programs/account-compression/Cargo.toml +++ b/programs/account-compression/Cargo.toml @@ -41,6 +41,7 @@ light-batched-merkle-tree = { workspace = true, features = ["solana"] } light-merkle-tree-metadata = { workspace = true, features = ["anchor"] } light-zero-copy = { workspace = true } zerocopy = { workspace = true, features = ["derive"] } +thiserror = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] solana-sdk = { workspace = true } diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index c39071adf0..c90e91f3cc 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -1,4 +1,8 @@ use anchor_lang::prelude::*; +use light_batched_merkle_tree::errors::BatchedMerkleTreeError; +use light_concurrent_merkle_tree::errors::ConcurrentMerkleTreeError; +use light_indexed_merkle_tree::errors::IndexedMerkleTreeError; +use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; #[error_code] pub enum AccountCompressionErrorCode { @@ -66,4 +70,40 @@ pub enum AccountCompressionErrorCode { UnsupportedHeight, UnsupportedParameters, V1AccountMarkedAsProofByIndex, + #[msg("MerkleTreeMetadataError")] + MerkleTreeMetadataError, + #[msg("BatchedMerkleTreeError")] + BatchedMerkleTreeError, + #[msg("ConcurrentMerkleTreeError")] + ConcurrentMerkleTreeError, + #[msg("IndexedMerkleTreeError")] + IndexedMerkleTreeError, +} + +impl From for AccountCompressionErrorCode { + fn from(err: MerkleTreeMetadataError) -> Self { + msg!("Merkle tree metadata error {}", err); + AccountCompressionErrorCode::MerkleTreeMetadataError + } +} + +impl From for AccountCompressionErrorCode { + fn from(err: BatchedMerkleTreeError) -> Self { + msg!("Batched merkle tree error {}", err); + AccountCompressionErrorCode::BatchedMerkleTreeError + } +} + +impl From for AccountCompressionErrorCode { + fn from(err: ConcurrentMerkleTreeError) -> Self { + msg!("Concurrent merkle tree error {}", err); + AccountCompressionErrorCode::ConcurrentMerkleTreeError + } +} + +impl From for AccountCompressionErrorCode { + fn from(err: IndexedMerkleTreeError) -> Self { + msg!("Indexed merkle tree error {}", err); + AccountCompressionErrorCode::IndexedMerkleTreeError + } } diff --git a/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs b/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs index 42e6842152..115b15c91c 100644 --- a/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs +++ b/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs @@ -3,6 +3,7 @@ use light_account_checks::checks::check_account_balance_is_rent_exempt; use crate::{ address_merkle_tree_from_bytes_zero_copy, + errors::AccountCompressionErrorCode, processor::{ initialize_address_merkle_tree::process_initialize_address_merkle_tree, initialize_address_queue::process_initialize_address_queue, @@ -85,14 +86,14 @@ pub fn process_rollover_address_merkle_tree_and_queue<'a, 'b, 'c: 'info, 'info>( ctx.accounts.old_queue.key().into(), ctx.accounts.new_address_merkle_tree.key().into(), ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; queue_account_loaded .metadata .rollover( ctx.accounts.old_address_merkle_tree.key().into(), ctx.accounts.new_queue.key().into(), ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; let merkle_tree_metadata = merkle_tree_account_loaded.metadata; let queue_metadata = queue_account_loaded.metadata; diff --git a/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs b/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs index 275d5db3dd..1b15020cbe 100644 --- a/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs +++ b/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs @@ -6,6 +6,7 @@ use light_batched_merkle_tree::{ use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use crate::{ + errors::AccountCompressionErrorCode, utils::{ check_signer_is_registered_or_authority::{ check_signer_is_registered_or_authority, GroupAccounts, @@ -91,7 +92,10 @@ pub fn process_rollover_batched_state_merkle_tree<'a, 'b, 'c: 'info, 'info>( rent, )?; if ctx.accounts.old_output_queue.to_account_info().lamports() == 0 { - return Err(ProgramError::from(MerkleTreeMetadataError::NotReadyForRollover).into()); + return Err(AccountCompressionErrorCode::from( + MerkleTreeMetadataError::NotReadyForRollover, + ) + .into()); } Ok(()) } diff --git a/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs b/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs index 7c8c5bb306..6e98354331 100644 --- a/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs +++ b/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs @@ -3,6 +3,7 @@ use light_account_checks::checks::check_account_balance_is_rent_exempt; use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use crate::{ + errors::AccountCompressionErrorCode, processor::{ initialize_concurrent_merkle_tree::process_initialize_state_merkle_tree, initialize_nullifier_queue::process_initialize_nullifier_queue, @@ -91,14 +92,14 @@ pub fn process_rollover_state_merkle_tree_nullifier_queue_pair<'a, 'b, 'c: 'info ctx.accounts.old_nullifier_queue.key().into(), ctx.accounts.new_state_merkle_tree.key().into(), ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; queue_account_loaded .metadata .rollover( ctx.accounts.old_state_merkle_tree.key().into(), ctx.accounts.new_nullifier_queue.key().into(), ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; let merkle_tree_metadata = merkle_tree_account_loaded.metadata; let queue_metadata = queue_account_loaded.metadata; @@ -190,7 +191,10 @@ pub fn process_rollover_state_merkle_tree_nullifier_queue_pair<'a, 'b, 'c: 'info .lamports() == 0 { - return Err(ProgramError::from(MerkleTreeMetadataError::NotReadyForRollover).into()); + return Err(AccountCompressionErrorCode::from( + MerkleTreeMetadataError::NotReadyForRollover, + ) + .into()); } Ok(()) } diff --git a/programs/account-compression/src/processor/initialize_address_queue.rs b/programs/account-compression/src/processor/initialize_address_queue.rs index 316982b3d8..8db3425427 100644 --- a/programs/account-compression/src/processor/initialize_address_queue.rs +++ b/programs/account-compression/src/processor/initialize_address_queue.rs @@ -6,7 +6,10 @@ use light_merkle_tree_metadata::{ QueueType, }; -use crate::state::{queue_from_bytes_zero_copy_init, QueueAccount}; +use crate::{ + errors::AccountCompressionErrorCode, + state::{queue_from_bytes_zero_copy_init, QueueAccount}, +}; pub fn process_initialize_address_queue<'info>( queue_account_info: &AccountInfo<'info>, @@ -33,9 +36,9 @@ pub fn process_initialize_address_queue<'info>( let queue_rent = queue_account_info.lamports(); let rollover_fee = if let Some(rollover_threshold) = rollover_threshold { let rollover_fee = compute_rollover_fee(rollover_threshold, height, merkle_tree_rent) - .map_err(ProgramError::from)? + .map_err(AccountCompressionErrorCode::from)? + compute_rollover_fee(rollover_threshold, height, queue_rent) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; check_rollover_fee_sufficient( rollover_fee, queue_rent, @@ -43,7 +46,7 @@ pub fn process_initialize_address_queue<'info>( rollover_threshold, height, ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; msg!("address queue rollover_fee: {}", rollover_fee); rollover_fee } else { diff --git a/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs b/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs index a120826e4b..4823f67b53 100644 --- a/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs +++ b/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs @@ -5,7 +5,10 @@ use light_merkle_tree_metadata::{ rollover::{check_rollover_fee_sufficient, RolloverMetadata}, }; -use crate::{state::StateMerkleTreeAccount, state_merkle_tree_from_bytes_zero_copy_init}; +use crate::{ + errors::AccountCompressionErrorCode, state::StateMerkleTreeAccount, + state_merkle_tree_from_bytes_zero_copy_init, +}; #[allow(unused_variables)] pub fn process_initialize_state_merkle_tree( @@ -33,9 +36,9 @@ pub fn process_initialize_state_merkle_tree( Some(rollover_threshold) => { let rollover_fee = compute_rollover_fee(rollover_threshold, *height, merkle_tree_rent) - .map_err(ProgramError::from)? + .map_err(AccountCompressionErrorCode::from)? + compute_rollover_fee(rollover_threshold, *height, queue_rent) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; check_rollover_fee_sufficient( rollover_fee, queue_rent, @@ -43,7 +46,7 @@ pub fn process_initialize_state_merkle_tree( rollover_threshold, *height, ) - .map_err(ProgramError::from)?; + .map_err(AccountCompressionErrorCode::from)?; msg!(" state Merkle tree rollover_fee: {}", rollover_fee); rollover_fee } diff --git a/programs/compressed-token/src/freeze.rs b/programs/compressed-token/src/freeze.rs index bb43ea9b89..dc8d160e15 100644 --- a/programs/compressed-token/src/freeze.rs +++ b/programs/compressed-token/src/freeze.rs @@ -175,7 +175,7 @@ fn create_token_output_accounts( BATCHED_DISCRIMINATOR => token_data.hash(), _ => panic!(), } - .map_err(ProgramError::from)?; + .map_err(|_| crate::ErrorCode::HashToFieldError)?; let data: CompressedAccountData = CompressedAccountData { discriminator: TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR, diff --git a/programs/compressed-token/src/process_transfer.rs b/programs/compressed-token/src/process_transfer.rs index e6f097b73c..baeafd9c32 100644 --- a/programs/compressed-token/src/process_transfer.rs +++ b/programs/compressed-token/src/process_transfer.rs @@ -272,7 +272,7 @@ pub fn create_output_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(ProgramError::from)?; + .map_err(|_| crate::ErrorCode::HashToFieldError)?; let data = CompressedAccountData { discriminator: TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR, data: token_data_bytes, @@ -361,7 +361,7 @@ pub fn add_data_hash_to_input_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(ProgramError::from)? + .map_err(|_| crate::ErrorCode::HashToFieldError)? } else { TokenData::hash_frozen_with_hashed_values( hashed_mint, @@ -369,7 +369,7 @@ pub fn add_data_hash_to_input_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(ProgramError::from)? + .map_err(|_| crate::ErrorCode::HashToFieldError)? }; } Ok(()) diff --git a/programs/system/Cargo.toml b/programs/system/Cargo.toml index a214c0fbd1..6e81012759 100644 --- a/programs/system/Cargo.toml +++ b/programs/system/Cargo.toml @@ -40,6 +40,7 @@ light-account-checks = { workspace = true, features = ["pinocchio"] } pinocchio = { workspace = true } pinocchio-system = { version = "0.2.3" } solana-pubkey = { workspace = true, features = ["curve25519", "sha2"] } +solana-msg = { workspace = true } [dev-dependencies] rand = { workspace = true } diff --git a/programs/system/src/errors.rs b/programs/system/src/errors.rs index 2c6e06b27b..149d569e0c 100644 --- a/programs/system/src/errors.rs +++ b/programs/system/src/errors.rs @@ -1,4 +1,5 @@ use pinocchio::program_error::ProgramError; +use solana_msg::msg; use thiserror::Error; #[derive(Debug, Error, PartialEq)] @@ -107,6 +108,10 @@ pub enum SystemProgramError { InvalidTreeHeight, #[error("TooManyOutputAccounts")] TooManyOutputAccounts, + #[error("CompressedAccountError")] + CompressedAccountError, + #[error("HasherError")] + HasherError, } impl From for ProgramError { @@ -114,3 +119,17 @@ impl From for ProgramError { ProgramError::Custom(e as u32 + 6000) } } + +impl From for SystemProgramError { + fn from(err: light_compressed_account::CompressedAccountError) -> Self { + msg!("Compressed account error {}", err); + SystemProgramError::CompressedAccountError + } +} + +impl From for SystemProgramError { + fn from(err: light_hasher::HasherError) -> Self { + msg!("Hasher error {}", err); + SystemProgramError::HasherError + } +} diff --git a/programs/system/src/processor/create_inputs_cpi_data.rs b/programs/system/src/processor/create_inputs_cpi_data.rs index 824e62952c..ae9bf5db2a 100644 --- a/programs/system/src/processor/create_inputs_cpi_data.rs +++ b/programs/system/src/processor/create_inputs_cpi_data.rs @@ -102,7 +102,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &merkle_context.leaf_index.into(), is_batched, ) - .map_err(ProgramError::from)?, + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?, leaf_index: merkle_context.leaf_index, prove_by_index: merkle_context.prove_by_index() as u8, queue_index, @@ -112,7 +112,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.nullifiers[j].account_hash; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.nullifiers[j].account_hash]) - .map_err(ProgramError::from)?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } // TODO: benchmark the chaining. diff --git a/programs/system/src/processor/create_outputs_cpi_data.rs b/programs/system/src/processor/create_outputs_cpi_data.rs index 97a2489f69..9544b88ed5 100644 --- a/programs/system/src/processor/create_outputs_cpi_data.rs +++ b/programs/system/src/processor/create_outputs_cpi_data.rs @@ -169,7 +169,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &cpi_ix_data.output_leaf_indices[j].into(), is_batched, ) - .map_err(ProgramError::from)?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; cpi_ix_data.leaves[j].account_index = index_merkle_tree_account_account - 1; if !cpi_ix_data.nullifiers.is_empty() { @@ -177,7 +177,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.leaves[j].leaf; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.leaves[j].leaf]) - .map_err(ProgramError::from)?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } context.set_rollover_fee(current_index as u8, rollover_fee); diff --git a/programs/system/src/processor/process.rs b/programs/system/src/processor/process.rs index 872ae6d9df..e3fe64278e 100644 --- a/programs/system/src/processor/process.rs +++ b/programs/system/src/processor/process.rs @@ -209,7 +209,7 @@ pub fn process< &output_compressed_account_hashes, current_slot, ) - .map_err(ProgramError::from)?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } // 11. Sum check --------------------------------------------------- diff --git a/programs/system/src/processor/verify_proof.rs b/programs/system/src/processor/verify_proof.rs index 51bbbfabb7..fb1a832aef 100644 --- a/programs/system/src/processor/verify_proof.rs +++ b/programs/system/src/processor/verify_proof.rs @@ -168,18 +168,20 @@ pub fn verify_proof( { let public_input_hash = if !leaves.is_empty() && !addresses.is_empty() { // combined inclusion & non-inclusion proof - let inclusion_hash = - create_two_inputs_hash_chain(roots, leaves).map_err(ProgramError::from)?; + let inclusion_hash = create_two_inputs_hash_chain(roots, leaves) + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; let non_inclusion_hash = create_two_inputs_hash_chain(address_roots, addresses) - .map_err(ProgramError::from)?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; create_hash_chain_from_slice(&[inclusion_hash, non_inclusion_hash]) - .map_err(ProgramError::from)? + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? } else if !leaves.is_empty() { // inclusion proof - create_two_inputs_hash_chain(roots, leaves).map_err(ProgramError::from)? + create_two_inputs_hash_chain(roots, leaves) + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? } else { // non-inclusion proof - create_two_inputs_hash_chain(address_roots, addresses).map_err(ProgramError::from)? + create_two_inputs_hash_chain(address_roots, addresses) + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? }; let vk = select_verifying_key(leaves.len(), addresses.len()) diff --git a/scripts/format.sh b/scripts/format.sh index 92861e9c64..1b009c0dbc 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -23,8 +23,11 @@ cargo clippy \ # Make sure that tests compile cargo test-sbf -p system-test --no-run cargo test-sbf -p system-cpi-test --no-run +cargo test-sbf -p system-cpi-v2-test --no-run cargo test-sbf -p e2e-test --no-run cargo test-sbf -p compressed-token-test --no-run cargo test-sbf -p token-escrow --no-run cargo test-sbf -p sdk-test --no-run cargo test-sbf -p sdk-anchor-test --no-run +cargo test-sbf -p client-test --no-run +cargo test-sbf -p sdk-pinocchio-test --no-run diff --git a/sdk-libs/client/Cargo.toml b/sdk-libs/client/Cargo.toml index 06dea1e963..16862e6836 100644 --- a/sdk-libs/client/Cargo.toml +++ b/sdk-libs/client/Cargo.toml @@ -40,7 +40,7 @@ solana-address-lookup-table-interface = { version = "2.2.1", features = [ light-merkle-tree-metadata = { workspace = true, features = ["solana"] } light-concurrent-merkle-tree = { workspace = true } light-indexed-merkle-tree = { workspace = true } -light-sdk = { workspace = true, features = ["solana"] } +light-sdk = { workspace = true } light-hasher = { workspace = true } light-compressed-account = { workspace = true, features = ["solana"] } diff --git a/sdk-libs/client/src/indexer/mod.rs b/sdk-libs/client/src/indexer/mod.rs index c356b4074e..c66baf2d0b 100644 --- a/sdk-libs/client/src/indexer/mod.rs +++ b/sdk-libs/client/src/indexer/mod.rs @@ -17,8 +17,8 @@ pub use types::{ AccountProofInputs, Address, AddressMerkleTreeAccounts, AddressProofInputs, AddressQueueIndex, AddressWithTree, BatchAddressUpdateIndexerResponse, CompressedAccount, Hash, MerkleProof, MerkleProofWithContext, NewAddressProofWithContext, NextTreeInfo, OwnerBalance, ProofOfLeaf, - SignatureWithMetadata, StateMerkleTreeAccounts, TokenAccount, TokenBalance, TreeInfo, - ValidityProofWithContext, + RootIndex, SignatureWithMetadata, StateMerkleTreeAccounts, TokenAccount, TokenBalance, + TreeInfo, ValidityProofWithContext, }; mod options; pub use options::*; diff --git a/sdk-libs/client/src/indexer/types.rs b/sdk-libs/client/src/indexer/types.rs index ade6cb9bb9..1efb18f908 100644 --- a/sdk-libs/client/src/indexer/types.rs +++ b/sdk-libs/client/src/indexer/types.rs @@ -3,17 +3,13 @@ use light_compressed_account::{ CompressedAccount as ProgramCompressedAccount, CompressedAccountData, CompressedAccountWithMerkleContext, }, + instruction_data::compressed_proof::CompressedProof, TreeType, }; use light_indexed_merkle_tree::array::IndexedElement; use light_sdk::{ - instruction::{ - pack_accounts::PackedAccounts, - tree_info::{PackedAddressTreeInfo, PackedStateTreeInfo}, - }, - light_compressed_account::instruction_data::compressed_proof::CompressedProof, + instruction::{PackedAccounts, PackedAddressTreeInfo, PackedStateTreeInfo, ValidityProof}, token::{AccountState, TokenData}, - ValidityProof, }; use num_bigint::BigUint; use solana_pubkey::Pubkey; @@ -88,7 +84,7 @@ impl ValidityProofWithContext { pub fn get_root_indices(&self) -> Vec> { self.accounts .iter() - .map(|account| account.root_index) + .map(|account| account.root_index.root_index()) .collect() } @@ -104,18 +100,52 @@ impl ValidityProofWithContext { pub struct AccountProofInputs { pub hash: [u8; 32], pub root: [u8; 32], - pub root_index: Option, + pub root_index: RootIndex, pub leaf_index: u64, pub tree_info: TreeInfo, } +#[derive(Clone, Default, Copy, Debug, PartialEq)] +pub struct RootIndex { + proof_by_index: bool, + root_index: u16, +} + +impl RootIndex { + pub fn new_none() -> Self { + Self { + proof_by_index: true, + root_index: 0, + } + } + + pub fn new_some(root_index: u16) -> Self { + Self { + proof_by_index: false, + root_index, + } + } + + pub fn proof_by_index(&self) -> bool { + self.proof_by_index + } + + pub fn root_index(&self) -> Option { + if !self.proof_by_index { + Some(self.root_index) + } else { + None + } + } +} + impl AccountProofInputs { pub fn from_api_model( value: &photon_api::models::AccountProofInputs, ) -> Result { let root_index = { if value.root_index.prove_by_index { - None + RootIndex::new_none() } else { Some(value.root_index.root_index) } @@ -173,11 +203,11 @@ impl ValidityProofWithContext { let merkle_tree_pubkey_index = packed_accounts.insert_or_get(account.tree_info.tree); let queue_pubkey_index = packed_accounts.insert_or_get(account.tree_info.queue); let tree_info_packed = PackedStateTreeInfo { - root_index: account.root_index.unwrap_or_default(), + root_index: account.root_index.root_index, merkle_tree_pubkey_index, queue_pubkey_index, leaf_index: account.leaf_index as u32, - prove_by_index: account.root_index.is_none(), + prove_by_index: account.root_index.proof_by_index(), }; packed_tree_infos.push(tree_info_packed); @@ -262,7 +292,7 @@ impl ValidityProofWithContext { Ok(AccountProofInputs { hash: decode_base58_to_fixed_array(&value.leaves[i])?, root: decode_base58_to_fixed_array(&value.roots[i])?, - root_index: Some(value.root_indices[i] as u16), + root_index: RootIndex::new_some(value.root_indices[i] as u16), leaf_index: value.leaf_indices[i] as u64, tree_info: TreeInfo { tree_type: tree_info.tree_type, diff --git a/sdk-libs/client/src/lib.rs b/sdk-libs/client/src/lib.rs index 98033e9c82..54f86d72c0 100644 --- a/sdk-libs/client/src/lib.rs +++ b/sdk-libs/client/src/lib.rs @@ -1,3 +1,81 @@ +//! # Light Client +//! +//! A client library for interacting with Light Protocol compressed accounts and RPC endpoints. +//! +//! ## Features +//! - Connect to various RPC endpoints (local test validator, devnet/mainnet) +//! - Query compressed accounts and validity proofs from RPC endpoints +//! - Support for both v1 and v2 merkle trees (with v2 feature) +//! - Start local test validator with Light Protocol programs +//! +//! ## Prerequisites +//! +//! For local test validator usage, install the Light CLI: +//! ```bash +//! npm i -g @lightprotocol/zk-compression-cli +//! ``` +//! +//! ## Example +//! +//! ```rust +//! use light_client::{ +//! rpc::{LightClient, RpcConfig, Rpc}, +//! indexer::{Indexer, IndexerRpcConfig, RetryConfig}, +//! local_test_validator::{spawn_validator, LightValidatorConfig}, +//! }; +//! use light_prover_client::prover::ProverConfig; +//! use solana_pubkey::Pubkey; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! // Start local test validator with Light Protocol programs +//! let config = LightValidatorConfig { +//! enable_indexer: true, +//! prover_config: Some(ProverConfig::default()), +//! wait_time: 75, +//! sbf_programs: vec![], +//! limit_ledger_size: None, +//! }; +//! spawn_validator(config).await; +//! +//! // Connect to the validator +//! let mut rpc = LightClient::new(RpcConfig::local()).await?; +//! +//! // Or connect to devnet/mainnet: +//! // let mut rpc = LightClient::new(RpcConfig::new("https://devnet.helius-rpc.com/?api-key=YOUR_KEY")).await?; +//! // let mut rpc = LightClient::new(RpcConfig::new("https://mainnet.helius-rpc.com/?api-key=YOUR_KEY")).await?; +//! +//! let owner = Pubkey::new_unique(); +//! +//! // Create indexer config for queries +//! let slot = rpc.get_slot().await?; +//! let config = IndexerRpcConfig { +//! slot, +//! retry_config: RetryConfig::default(), +//! }; +//! +//! // Query compressed accounts using Indexer trait +//! let accounts = rpc +//! .get_compressed_accounts_by_owner(&owner, None, Some(config)) +//! .await?; +//! +//! println!("Found {} compressed accounts", accounts.value.items.len()); +//! +//! // Get validity proofs for creating transactions +//! let rpc_result = rpc +//! .get_validity_proof( +//! vec![], // add account hashes here +//! vec![], // add addresses with address tree here +//! None +//! ) +//! .await?; +//! +//! println!("Got validity proof and proof inputs {:?}", rpc_result.value); +//! +//! Ok(()) +//! } +//! ``` + pub mod constants; pub mod fee; pub mod indexer; diff --git a/sdk-libs/macros/Cargo.toml b/sdk-libs/macros/Cargo.toml index 90db169c1f..2fa5fba869 100644 --- a/sdk-libs/macros/Cargo.toml +++ b/sdk-libs/macros/Cargo.toml @@ -10,16 +10,18 @@ edition = "2021" proc-macro2 = { workspace = true } quote = { workspace = true } syn = { workspace = true } +solana-pubkey = { workspace = true, features = ["curve25519", "sha2"] } light-hasher = { workspace = true } -# ark-bn254 = { workspace = true } light-poseidon = { workspace = true } [dev-dependencies] light-compressed-account = { workspace = true } +light-sdk-types = { workspace = true } prettyplease = "0.2.29" solana-pubkey = { workspace = true, features = ["borsh"] } borsh = { workspace = true } +light-macros = { workspace = true } [lib] proc-macro = true diff --git a/sdk-libs/macros/src/cpi_signer.rs b/sdk-libs/macros/src/cpi_signer.rs new file mode 100644 index 0000000000..a16084aafa --- /dev/null +++ b/sdk-libs/macros/src/cpi_signer.rs @@ -0,0 +1,95 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, LitStr}; + +pub fn derive_light_cpi_signer_pda(input: TokenStream) -> TokenStream { + // Parse the input - just a program ID string literal + let program_id_lit = parse_macro_input!(input as LitStr); + let program_id_str = program_id_lit.value(); + + // Compute the PDA at compile time using solana-pubkey with "cpi_authority" seed + use std::str::FromStr; + + // Parse program ID at compile time + let program_id = match solana_pubkey::Pubkey::from_str(&program_id_str) { + Ok(id) => id, + Err(_) => { + return syn::Error::new( + program_id_lit.span(), + "Invalid program ID format. Expected a base58 encoded public key", + ) + .to_compile_error() + .into(); + } + }; + + // Use fixed "cpi_authority" seed + let seeds = &[b"cpi_authority".as_slice()]; + + // Compute the PDA at compile time + let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); + + // Generate the output code with precomputed byte array and bump + let pda_bytes = pda.to_bytes(); + let bytes = pda_bytes + .iter() + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); + + let output = quote! { + ([#(#bytes),*], #bump) + }; + + output.into() +} + +pub fn derive_light_cpi_signer(input: TokenStream) -> TokenStream { + // Parse the input - just a program ID string literal + let program_id_lit = parse_macro_input!(input as LitStr); + let program_id_str = program_id_lit.value(); + + // Compute the PDA at compile time using solana-pubkey with "cpi_authority" seed + use std::str::FromStr; + + // Parse program ID at compile time + let program_id = match solana_pubkey::Pubkey::from_str(&program_id_str) { + Ok(id) => id, + Err(_) => { + return syn::Error::new( + program_id_lit.span(), + "Invalid program ID format. Expected a base58 encoded public key", + ) + .to_compile_error() + .into(); + } + }; + + // Use fixed "cpi_authority" seed + let seeds = &[b"cpi_authority".as_slice()]; + + // Compute the PDA at compile time + let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); + + // Generate the output code with precomputed CpiSigner struct + let program_id_bytes = program_id.to_bytes(); + let pda_bytes = pda.to_bytes(); + + let program_id_literals = program_id_bytes + .iter() + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); + let cpi_signer_literals = pda_bytes + .iter() + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); + + let output = quote! { + { + // Use the CpiSigner type from the current scope (should be imported) + CpiSigner { + program_id: [#(#program_id_literals),*], + cpi_signer: [#(#cpi_signer_literals),*], + bump: #bump, + } + } + }; + + output.into() +} diff --git a/sdk-libs/macros/src/discriminator.rs b/sdk-libs/macros/src/discriminator.rs index 176749dcff..1d289db888 100644 --- a/sdk-libs/macros/src/discriminator.rs +++ b/sdk-libs/macros/src/discriminator.rs @@ -44,7 +44,7 @@ mod tests { let output = discriminator(input).unwrap(); let output = output.to_string(); - assert!(output.contains("impl Discriminator for MyAccount")); + assert!(output.contains("impl LightDiscriminator for MyAccount")); assert!(output.contains("[181 , 255 , 112 , 42 , 17 , 188 , 66 , 199]")); } } diff --git a/sdk-libs/macros/src/hasher/light_hasher.rs b/sdk-libs/macros/src/hasher/light_hasher.rs index 720e647227..911cc35f73 100644 --- a/sdk-libs/macros/src/hasher/light_hasher.rs +++ b/sdk-libs/macros/src/hasher/light_hasher.rs @@ -125,17 +125,15 @@ impl ::light_hasher::DataHasher for MyAccount { use ::light_hasher::Hasher; use ::light_hasher::to_byte_array::ToByteArray; #[cfg(debug_assertions)] - { + { if std::env::var("RUST_BACKTRACE").is_ok() { - let debug_prints: Vec<[u8;32]> = vec![ - self.a.to_byte_array()?, - self.b.to_byte_array()?, - self.c.to_byte_array()?, - self.d.to_byte_array()?, + let debug_prints: Vec<[u8; 32]> = vec![ + self.a.to_byte_array() ?, self.b.to_byte_array() ?, self.c + .to_byte_array() ?, self.d.to_byte_array() ?, ]; + println!("DataHasher::hash inputs {:?}", debug_prints); } - println!("DataHasher::hash inputs {:?}", debug_prints); - } + } H::hashv( &[ self.a.to_byte_array()?.as_slice(), diff --git a/sdk-libs/macros/src/lib.rs b/sdk-libs/macros/src/lib.rs index 8f2bc52ec8..8fbbcebd71 100644 --- a/sdk-libs/macros/src/lib.rs +++ b/sdk-libs/macros/src/lib.rs @@ -7,6 +7,7 @@ use traits::process_light_traits; mod account; mod accounts; +mod cpi_signer; mod discriminator; mod hasher; mod program; @@ -270,3 +271,50 @@ pub fn light_program(_: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +/// Derives a Light Protocol CPI signer address at compile time +/// +/// This macro computes the CPI signer PDA using the "cpi_authority" seed +/// for the given program ID at compile time. +/// +/// ## Usage +/// +/// ``` +/// use light_sdk_macros::derive_light_cpi_signer_pda; +/// // Derive CPI signer for your program +/// const CPI_SIGNER_DATA: ([u8; 32], u8) = derive_light_cpi_signer_pda!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); +/// const CPI_SIGNER: [u8; 32] = CPI_SIGNER_DATA.0; +/// const CPI_SIGNER_BUMP: u8 = CPI_SIGNER_DATA.1; +/// ``` +/// +/// This macro computes the PDA during compile time and returns a tuple of ([u8; 32], bump). +#[proc_macro] +pub fn derive_light_cpi_signer_pda(input: TokenStream) -> TokenStream { + cpi_signer::derive_light_cpi_signer_pda(input) +} + +/// Derives a complete Light Protocol CPI configuration at compile time +/// +/// This macro computes the program ID, CPI signer PDA, and bump seed +/// for the given program ID at compile time. +/// +/// ## Usage +/// +/// ``` +/// use light_sdk_macros::derive_light_cpi_signer; +/// use light_sdk_types::CpiSigner; +/// // Derive complete CPI signer for your program +/// const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); +/// +/// // Access individual fields: +/// const PROGRAM_ID: [u8; 32] = LIGHT_CPI_SIGNER.program_id; +/// const CPI_SIGNER: [u8; 32] = LIGHT_CPI_SIGNER.cpi_signer; +/// const BUMP: u8 = LIGHT_CPI_SIGNER.bump; +/// ``` +/// +/// This macro computes all values during compile time and returns a CpiSigner struct +/// containing the program ID, CPI signer address, and bump seed. +#[proc_macro] +pub fn derive_light_cpi_signer(input: TokenStream) -> TokenStream { + cpi_signer::derive_light_cpi_signer(input) +} diff --git a/sdk-libs/macros/tests/pda.rs b/sdk-libs/macros/tests/pda.rs new file mode 100644 index 0000000000..2982cba542 --- /dev/null +++ b/sdk-libs/macros/tests/pda.rs @@ -0,0 +1,77 @@ +use std::str::FromStr; + +use light_sdk_macros::derive_light_cpi_signer; +use light_sdk_types::CpiSigner; +use solana_pubkey::Pubkey; + +#[test] +fn test_compute_pda_basic() { + // Test with a known program ID using fixed "cpi_authority" seed + const RESULT: CpiSigner = + derive_light_cpi_signer!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); + + // Verify the result has valid fields + assert_eq!(RESULT.program_id.len(), 32); + assert_eq!(RESULT.cpi_signer.len(), 32); + + // Verify this matches runtime computation + let runtime_result = Pubkey::find_program_address( + &[b"cpi_authority"], + &Pubkey::from_str("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7").unwrap(), + ); + + assert_eq!(RESULT.cpi_signer, runtime_result.0.to_bytes()); + assert_eq!(RESULT.bump, runtime_result.1); +} + +#[test] +fn test_cpi_signer() { + // Test that the macro can be used in const contexts + const PDA_RESULT: CpiSigner = + derive_light_cpi_signer!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); + + // Extract individual components in const context + const PROGRAM_ID: [u8; 32] = PDA_RESULT.program_id; + const CPI_SIGNER: [u8; 32] = PDA_RESULT.cpi_signer; + const BUMP: u8 = PDA_RESULT.bump; + + // Verify they're valid + assert_eq!( + PROGRAM_ID, + light_macros::pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7") + ); + assert_eq!( + CPI_SIGNER, + [ + 251, 179, 40, 117, 16, 92, 174, 133, 181, 180, 68, 118, 7, 237, 191, 225, 69, 39, 191, + 180, 35, 145, 28, 164, 4, 35, 191, 209, 82, 122, 38, 117 + ] + ); + assert_eq!(BUMP, 255); +} + +#[test] +fn test_cpi_signer_2() { + // Test that the macro can be used in const contexts + const PDA_RESULT: CpiSigner = + derive_light_cpi_signer!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); + + // Extract individual components in const context + const PROGRAM_ID: [u8; 32] = PDA_RESULT.program_id; + const CPI_SIGNER: [u8; 32] = PDA_RESULT.cpi_signer; + const BUMP: u8 = PDA_RESULT.bump; + + // Verify they're valid + assert_eq!( + PROGRAM_ID, + light_macros::pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq") + ); + assert_eq!( + CPI_SIGNER, + [ + 20, 12, 243, 109, 120, 11, 194, 48, 169, 64, 170, 103, 246, 66, 224, 151, 74, 116, 57, + 84, 0, 180, 16, 126, 175, 149, 24, 207, 85, 137, 3, 207 + ] + ); + assert_eq!(BUMP, 255); +} diff --git a/sdk-libs/program-test/src/accounts/initialize.rs b/sdk-libs/program-test/src/accounts/initialize.rs index b080322e7e..6c065a1c41 100644 --- a/sdk-libs/program-test/src/accounts/initialize.rs +++ b/sdk-libs/program-test/src/accounts/initialize.rs @@ -206,7 +206,7 @@ pub async fn initialize_accounts( } let registered_system_program_pda = - get_registered_program_pda(&light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM); + get_registered_program_pda(&Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM)); let registered_registry_program_pda = get_registered_program_pda(&light_registry::ID); let forester_epoch = if *register_forester_and_advance_to_active_phase { let mut registered_epoch = Epoch::register( diff --git a/sdk-libs/program-test/src/accounts/state_tree.rs b/sdk-libs/program-test/src/accounts/state_tree.rs index 4f17d1183a..f39bab2eb4 100644 --- a/sdk-libs/program-test/src/accounts/state_tree.rs +++ b/sdk-libs/program-test/src/accounts/state_tree.rs @@ -157,7 +157,7 @@ pub async fn create_state_merkle_tree_and_queue_account( &payer.pubkey(), ProtocolConfig::default().cpi_context_size as usize, rent_cpi_config, - &light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), Some(cpi_context_keypair), ); diff --git a/sdk-libs/program-test/src/accounts/state_tree_v2.rs b/sdk-libs/program-test/src/accounts/state_tree_v2.rs index 40e2b62f84..f27b2e2d5f 100644 --- a/sdk-libs/program-test/src/accounts/state_tree_v2.rs +++ b/sdk-libs/program-test/src/accounts/state_tree_v2.rs @@ -10,6 +10,7 @@ use light_registry::{ protocol_config::state::ProtocolConfig, }; use solana_instruction::Instruction; +use solana_pubkey::Pubkey; use solana_sdk::signature::{Keypair, Signature, Signer}; pub async fn create_batched_state_merkle_tree( @@ -59,7 +60,7 @@ pub async fn create_batched_state_merkle_tree( &payer.pubkey(), ProtocolConfig::default().cpi_context_size as usize, rent_cpi_config, - &light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), Some(cpi_context_keypair), ); let instruction = if registry { diff --git a/sdk-libs/program-test/src/accounts/test_accounts.rs b/sdk-libs/program-test/src/accounts/test_accounts.rs index e606d0d767..1fc0b2d84c 100644 --- a/sdk-libs/program-test/src/accounts/test_accounts.rs +++ b/sdk-libs/program-test/src/accounts/test_accounts.rs @@ -64,9 +64,9 @@ impl TestAccounts { governance_authority_pda: Pubkey::default(), group_pda: Pubkey::default(), forester: Keypair::from_bytes(&FORESTER_TEST_KEYPAIR).unwrap(), - registered_program_pda: get_registered_program_pda( - &light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, - ), + registered_program_pda: get_registered_program_pda(&Pubkey::from( + light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + )), registered_registry_program_pda: get_registered_program_pda(&light_registry::ID), registered_forester_pda: Pubkey::default(), forester_epoch: None, // Set to None or to an appropriate Epoch value if needed @@ -103,7 +103,7 @@ impl TestAccounts { payer.pubkey(), protocol_config_pda, group_pda, - light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), ); let cpi_context_keypair = Keypair::from_bytes(&SIGNATURE_CPI_TEST_KEYPAIR).unwrap(); diff --git a/sdk-libs/program-test/src/indexer/address_tree.rs b/sdk-libs/program-test/src/indexer/address_tree.rs index 9806ecd00a..cb998d0ebe 100644 --- a/sdk-libs/program-test/src/indexer/address_tree.rs +++ b/sdk-libs/program-test/src/indexer/address_tree.rs @@ -11,7 +11,7 @@ use light_indexed_merkle_tree::{ reference::IndexedMerkleTree, }; use light_prover_client::proof_types::non_inclusion::v2::NonInclusionMerkleProofInputs; -use light_sdk::STATE_MERKLE_TREE_ROOTS; +use light_sdk::constants::STATE_MERKLE_TREE_ROOTS; use num_bigint::{BigInt, BigUint}; use num_traits::ops::bytes::FromBytes; diff --git a/sdk-libs/program-test/src/indexer/test_indexer.rs b/sdk-libs/program-test/src/indexer/test_indexer.rs index 92b025ca18..efd15eba7c 100644 --- a/sdk-libs/program-test/src/indexer/test_indexer.rs +++ b/sdk-libs/program-test/src/indexer/test_indexer.rs @@ -20,8 +20,8 @@ use light_client::{ GetCompressedAccountsByOwnerConfig, GetCompressedTokenAccountsByOwnerOrDelegateOptions, Indexer, IndexerError, IndexerRpcConfig, Items, ItemsWithCursor, MerkleProof, MerkleProofWithContext, NewAddressProofWithContext, OwnerBalance, PaginatedOptions, - Response, RetryConfig, SignatureWithMetadata, StateMerkleTreeAccounts, TokenAccount, - TokenBalance, ValidityProofWithContext, + Response, RetryConfig, RootIndex, SignatureWithMetadata, StateMerkleTreeAccounts, + TokenAccount, TokenBalance, ValidityProofWithContext, }, rpc::{Rpc, RpcError}, }; @@ -60,8 +60,8 @@ use light_prover_client::{ }, }; use light_sdk::{ + light_hasher::Hash, token::{TokenData, TokenDataWithMerkleContext}, - Hash, }; use log::info; use num_bigint::{BigInt, BigUint}; @@ -477,11 +477,13 @@ impl Indexer for TestIndexer { if accounts.output_queue_batch_size.is_some() && accounts.leaf_index_in_queue_range(*index as usize)? { + use light_client::indexer::RootIndex; + indices_to_remove.push(i); proof_inputs.push(AccountProofInputs { hash: *compressed_account, root: [0u8; 32], - root_index: None, + root_index: RootIndex::new_none(), leaf_index: accounts .output_queue_elements .iter() @@ -563,7 +565,6 @@ impl Indexer for TestIndexer { } } root_indices - // root_indices.into_iter().map(|x| x.unwrap_or(0)).collect() }; Ok(Response { @@ -1932,7 +1933,7 @@ impl TestIndexer { }); account_proof_inputs.push(AccountProofInputs { - root_index: Some(root_index), + root_index: RootIndex::new_some(root_index), root: merkle_root, leaf_index: leaf_index as u64, hash: accounts[i], diff --git a/sdk-libs/program-test/src/lib.rs b/sdk-libs/program-test/src/lib.rs index 016763f208..e1825673de 100644 --- a/sdk-libs/program-test/src/lib.rs +++ b/sdk-libs/program-test/src/lib.rs @@ -1,3 +1,117 @@ +//! # Light Program Test +//! +//! A fast local test environment for Solana programs using compressed accounts and tokens. +//! +//! ## Features +//! - Fast in-memory indexer and SVM via [LiteSVM](https://github.com/LiteSVM/LiteSVM) +//! - Supports custom programs +//! - Built-in prover +//! +//! **Use `light-program-test` when:** +//! - You need fast test execution +//! - You write unit/integration tests for your program or client code +//! +//! **Use `solana-test-validator` when:** +//! - You need RPC methods or external tools that are incompatible with LiteSVM +//! - Testing against real validator behavior +//! +//! ## Configuration Options +//! +//! ### `with_prover: bool` +//! - `true`: Starts a prover server in the background for generating validity proofs +//! - `false`: Runs without prover (faster for tests that don't need proofs, or repeated test runs to reduce startup time) +//! +//! ### `additional_programs: Option>` +//! - Specify custom programs to deploy alongside the default Light Protocol programs +//! - Format: `vec![("program_name", program_id)]` +//! - Programs are loaded from built artifacts +//! +//! ## Prerequisites +//! +//! 1. **ZK Compression CLI**: Required to start the prover server and download Light Protocol programs +//! ```bash +//! npm i -g @lightprotocol/zk-compression-cli +//! ``` +//! - If programs are missing after CLI installation, run `light test-validator` once to download them +//! +//! 2. **Build programs**: Run `cargo test-sbf` to build program binaries and set the required +//! environment variables for locating program artifacts +//! +//! ## Prover Server +//! +//! The prover server runs on port 3001 when enabled. To manually stop it: +//! ```bash +//! # Find the process ID +//! lsof -i:3001 +//! # Kill the process +//! kill +//! ``` +//! +//! ## Examples +//! +//! ### V1 Trees +//! ```rust +//! use light_program_test::{LightProgramTest, ProgramTestConfig}; +//! use solana_sdk::signer::Signer; +//! +//! #[tokio::test] +//! async fn test_v1_compressed_account() { +//! // Initialize with v1 trees +//! let config = ProgramTestConfig::default(); +//! let mut rpc = LightProgramTest::new(config).await.unwrap(); +//! +//! let payer = Keypair::new(); +//! +//! // Get v1 tree info +//! let address_tree_info = rpc.get_address_tree_v1(); +//! let state_tree_info = rpc.get_random_state_tree_info(); +//! +//! // Airdrop for testing +//! rpc.airdrop_lamports(&payer.pubkey(), 1_000_000_000).await.unwrap(); +//! +//! // Query compressed accounts using Indexer trait +//! let accounts = rpc.indexer().unwrap() +//! .get_compressed_accounts_by_owner(&payer.pubkey()) +//! .await +//! .unwrap(); +//! +//! println!("Found {} compressed accounts", accounts.len()); +//! } +//! ``` +//! +//! ### V2 Trees +//! ```rust +//! use light_program_test::{LightProgramTest, ProgramTestConfig}; +//! use solana_sdk::signer::Signer; +//! +//! #[tokio::test] +//! async fn test_v2_compressed_account() { +//! // Initialize with v2 batched trees and custom program +//! let config = ProgramTestConfig::new_v2( +//! true, // with_prover +//! Some(vec![("my_program", my_program::ID)]) +//! ); +//! let mut rpc = LightProgramTest::new(config).await.unwrap(); +//! +//! let payer = Keypair::new(); +//! +//! // Get v2 tree pubkeys +//! let address_tree_info = rpc.get_address_tree_v2(); +//! let state_tree_info = rpc.get_random_state_tree_info(); +//! +//! +//! rpc.airdrop_lamports(&payer.pubkey(), 1_000_000_000).await.unwrap(); +//! +//! // Query using Indexer trait methods +//! let accounts = rpc.indexer().unwrap() +//! .get_compressed_accounts_by_owner(&payer.pubkey()) +//! .await +//! .unwrap(); +//! +//! println!("Found {} compressed accounts with v2 trees", accounts.len()); +//! } +//! ``` + pub mod accounts; pub mod indexer; pub mod program_test; diff --git a/sdk-libs/program-test/src/program_test/light_program_test.rs b/sdk-libs/program-test/src/program_test/light_program_test.rs index 24ff7cfeb8..afc6d7b4b1 100644 --- a/sdk-libs/program-test/src/program_test/light_program_test.rs +++ b/sdk-libs/program-test/src/program_test/light_program_test.rs @@ -140,13 +140,6 @@ impl LightProgramTest { self.test_accounts.v1_state_trees[0] } - #[cfg(feature = "v2")] - pub fn get_state_merkle_tree_v2( - &self, - ) -> crate::accounts::test_accounts::StateMerkleTreeAccountsV2 { - self.test_accounts.v2_state_trees[0] - } - pub fn get_address_merkle_tree(&self) -> AddressMerkleTreeAccounts { self.test_accounts.v1_address_trees[0] } diff --git a/sdk-libs/program-test/src/program_test/rpc.rs b/sdk-libs/program-test/src/program_test/rpc.rs index a298a7ef8a..4ba05963c7 100644 --- a/sdk-libs/program-test/src/program_test/rpc.rs +++ b/sdk-libs/program-test/src/program_test/rpc.rs @@ -23,8 +23,7 @@ use solana_sdk::{ instruction::Instruction, pubkey::Pubkey, rent::Rent, - signature::{Keypair, Signature, Signer}, - system_instruction, + signature::{Keypair, Signature}, transaction::Transaction, }; use solana_transaction_status_client_types::TransactionStatus; @@ -84,41 +83,8 @@ impl Rpc for LightProgramTest { to: &Pubkey, lamports: u64, ) -> Result { - // Create a transfer instruction - let transfer_instruction = - system_instruction::transfer(&self.get_payer().pubkey(), to, lamports); - let latest_blockhash = self.get_latest_blockhash().await?.0; - - // Use the Rpc implementation of get_payer to avoid ambiguity - let payer = ::get_payer(self); - - // Create and sign a transaction - let transaction = Transaction::new_signed_with_payer( - &[transfer_instruction], - Some(&payer.pubkey()), - &vec![payer], - latest_blockhash, - ); - let sig = *transaction.signatures.first().unwrap(); - - // Send the transaction - let _res = self.context.send_transaction(transaction).map_err(|x| { - #[cfg(not(debug_assertions))] - { - if self.config.log_failed_tx { - println!("{}", x.meta.pretty_logs()); - } - } - RpcError::TransactionError(x.err) - })?; - #[cfg(debug_assertions)] - { - if std::env::var("RUST_BACKTRACE").is_ok() { - println!("{}", _res.pretty_logs()); - } - } - - Ok(sig) + let res = self.context.airdrop(to, lamports).map_err(|e| e.err)?; + Ok(res.signature) } async fn get_balance(&self, pubkey: &Pubkey) -> Result { @@ -322,18 +288,20 @@ impl Rpc for LightProgramTest { tree_type: TreeType::AddressV1, } } - // fn get_address_tree_v2(&self) -> MerkleContext { - // MerkleContext { - // tree: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), - // queue: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), - // cpi_context: None, - // next_tree_info: None, - // tree_type: TreeType::AddressV2, - // } - // } } impl LightProgramTest { + #[cfg(feature = "v2")] + pub fn get_address_tree_v2(&self) -> TreeInfo { + TreeInfo { + tree: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), + queue: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), + cpi_context: None, + next_tree_info: None, + tree_type: TreeType::AddressV2, + } + } + async fn _send_transaction_with_batched_event( &mut self, transaction: Transaction, diff --git a/sdk-libs/program-test/src/utils/setup_light_programs.rs b/sdk-libs/program-test/src/utils/setup_light_programs.rs index 221fcb826a..1b9213ab09 100644 --- a/sdk-libs/program-test/src/utils/setup_light_programs.rs +++ b/sdk-libs/program-test/src/utils/setup_light_programs.rs @@ -1,5 +1,6 @@ use light_client::rpc::RpcError; -use light_sdk::utils::get_registered_program_pda; +use light_compressed_account::constants::REGISTERED_PROGRAM_PDA; +use light_registry::account_compression_cpi::sdk::get_registered_program_pda; use litesvm::LiteSVM; use solana_compute_budget::compute_budget::ComputeBudget; use solana_pubkey::Pubkey; @@ -72,7 +73,10 @@ pub fn setup_light_programs( { let path = format!("{}/light_system_program_pinocchio.so", light_bin_path); program_test - .add_program_from_file(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, path.clone()) + .add_program_from_file( + light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM.into(), + path.clone(), + ) .inspect_err(|_| { println!( "Program light_system_program_pinocchio bin not found in {}", @@ -84,13 +88,18 @@ pub fn setup_light_programs( #[cfg(not(feature = "devenv"))] { let path = format!("{}/light_system_program.so", light_bin_path); - program_test.add_program_from_file(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, path)?; + program_test.add_program_from_file( + Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + path, + )?; } let registered_program = registered_program_test_account_system_program(); program_test .set_account( - get_registered_program_pda(&light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + Pubkey::new_from_array(REGISTERED_PROGRAM_PDA), // get_registered_program_pda(&Pubkey::from( + // light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + // )) registered_program, ) .map_err(|e| { diff --git a/sdk-libs/sdk-pinocchio/Cargo.toml b/sdk-libs/sdk-pinocchio/Cargo.toml new file mode 100644 index 0000000000..da0366e693 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "light-sdk-pinocchio" +version = "0.12.0" +description = "Rust SDK for ZK Compression on Solana with Pinocchio features" +repository = "https://github.com/Lightprotocol/light-protocol" +license = "Apache-2.0" +edition = "2021" + +[features] +default = [] +v2 = [] + +[dependencies] +pinocchio = { workspace = true } +light-hasher = { workspace = true } +light-account-checks = { workspace = true } +light-macros = { workspace = true } +light-sdk-macros = { workspace = true } +light-sdk-types = { workspace = true } +light-zero-copy = { workspace = true } +borsh = { workspace = true } +thiserror = { workspace = true } +light-compressed-account = { workspace = true } +solana-pubkey = { workspace = true } diff --git a/sdk-libs/sdk-pinocchio/src/account.rs b/sdk-libs/sdk-pinocchio/src/account.rs new file mode 100644 index 0000000000..5d40bff7cb --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/account.rs @@ -0,0 +1,196 @@ +use std::ops::{Deref, DerefMut}; + +use light_compressed_account::{ + compressed_account::PackedMerkleContext, + instruction_data::with_account_info::{CompressedAccountInfo, InAccountInfo, OutAccountInfo}, +}; +use light_hasher::{DataHasher, Poseidon}; +use light_sdk_types::instruction::account_meta::CompressedAccountMetaTrait; +use pinocchio::pubkey::Pubkey; + +use crate::{error::LightSdkError, BorshDeserialize, BorshSerialize, LightDiscriminator}; + +#[derive(Debug, PartialEq)] +pub struct LightAccount< + 'a, + A: BorshSerialize + BorshDeserialize + LightDiscriminator + DataHasher + Default, +> { + owner: &'a Pubkey, + pub account: A, + account_info: CompressedAccountInfo, +} + +impl<'a, A: BorshSerialize + BorshDeserialize + LightDiscriminator + DataHasher + Default> + LightAccount<'a, A> +{ + pub fn new_init( + owner: &'a Pubkey, + address: Option<[u8; 32]>, + output_state_tree_index: u8, + ) -> Self { + let output_account_info = OutAccountInfo { + output_merkle_tree_index: output_state_tree_index, + discriminator: A::LIGHT_DISCRIMINATOR, + ..Default::default() + }; + Self { + owner, + account: A::default(), + account_info: CompressedAccountInfo { + address, + input: None, + output: Some(output_account_info), + }, + } + } + + pub fn new_mut( + owner: &'a Pubkey, + input_account_meta: &impl CompressedAccountMetaTrait, + input_account: A, + ) -> Result { + let input_account_info = { + let input_data_hash = input_account.hash::()?; + let tree_info = input_account_meta.get_tree_info(); + InAccountInfo { + data_hash: input_data_hash, + lamports: input_account_meta.get_lamports().unwrap_or_default(), + merkle_context: PackedMerkleContext { + merkle_tree_pubkey_index: tree_info.merkle_tree_pubkey_index, + queue_pubkey_index: tree_info.queue_pubkey_index, + leaf_index: tree_info.leaf_index, + prove_by_index: tree_info.prove_by_index, + }, + root_index: input_account_meta.get_root_index().unwrap_or_default(), + discriminator: A::LIGHT_DISCRIMINATOR, + } + }; + let output_account_info = { + let output_merkle_tree_index = input_account_meta + .get_output_state_tree_index() + .ok_or(LightSdkError::OutputStateTreeIndexIsNone)?; + OutAccountInfo { + lamports: input_account_meta.get_lamports().unwrap_or_default(), + output_merkle_tree_index, + discriminator: A::LIGHT_DISCRIMINATOR, + ..Default::default() + } + }; + + Ok(Self { + owner, + account: input_account, + account_info: CompressedAccountInfo { + address: input_account_meta.get_address(), + input: Some(input_account_info), + output: Some(output_account_info), + }, + }) + } + + pub fn new_close( + owner: &'a Pubkey, + input_account_meta: &impl CompressedAccountMetaTrait, + input_account: A, + ) -> Result { + let input_account_info = { + let input_data_hash = input_account.hash::()?; + let tree_info = input_account_meta.get_tree_info(); + InAccountInfo { + data_hash: input_data_hash, + lamports: input_account_meta.get_lamports().unwrap_or_default(), + merkle_context: PackedMerkleContext { + merkle_tree_pubkey_index: tree_info.merkle_tree_pubkey_index, + queue_pubkey_index: tree_info.queue_pubkey_index, + leaf_index: tree_info.leaf_index, + prove_by_index: tree_info.prove_by_index, + }, + root_index: input_account_meta.get_root_index().unwrap_or_default(), + discriminator: A::LIGHT_DISCRIMINATOR, + } + }; + Ok(Self { + owner, + account: input_account, + account_info: CompressedAccountInfo { + address: input_account_meta.get_address(), + input: Some(input_account_info), + output: None, + }, + }) + } + + pub fn discriminator(&self) -> &[u8; 8] { + &A::LIGHT_DISCRIMINATOR + } + + pub fn lamports(&self) -> u64 { + if let Some(output) = self.account_info.output.as_ref() { + output.lamports + } else if let Some(input) = self.account_info.input.as_ref() { + input.lamports + } else { + 0 + } + } + + pub fn lamports_mut(&mut self) -> &mut u64 { + if let Some(output) = self.account_info.output.as_mut() { + &mut output.lamports + } else if let Some(input) = self.account_info.input.as_mut() { + &mut input.lamports + } else { + panic!("No lamports field available in account_info") + } + } + + pub fn address(&self) -> &Option<[u8; 32]> { + &self.account_info.address + } + + pub fn owner(&self) -> &Pubkey { + self.owner + } + + pub fn in_account_info(&self) -> &Option { + &self.account_info.input + } + + pub fn out_account_info(&mut self) -> &Option { + &self.account_info.output + } + + /// 1. Serializes the account data and sets the output data hash. + /// 2. Returns CompressedAccountInfo. + /// + /// Note this is an expensive operation + /// that should only be called once per instruction. + pub fn to_account_info(mut self) -> Result { + if let Some(output) = self.account_info.output.as_mut() { + output.data_hash = self.account.hash::()?; + output.data = self + .account + .try_to_vec() + .map_err(|_| LightSdkError::Borsh)?; + } + Ok(self.account_info) + } +} + +impl Deref + for LightAccount<'_, A> +{ + type Target = A; + + fn deref(&self) -> &Self::Target { + &self.account + } +} + +impl DerefMut + for LightAccount<'_, A> +{ + fn deref_mut(&mut self) -> &mut ::Target { + &mut self.account + } +} diff --git a/sdk-libs/sdk-pinocchio/src/address.rs b/sdk-libs/sdk-pinocchio/src/address.rs new file mode 100644 index 0000000000..ebfbf955d6 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/address.rs @@ -0,0 +1,47 @@ +use pinocchio::{account_info::AccountInfo, pubkey::Pubkey}; + +// Define data structures needed +#[derive(Clone, Debug, Default)] +pub struct NewAddressParams { + pub seed: [u8; 32], + pub address_queue_pubkey: [u8; 32], + pub address_merkle_tree_pubkey: [u8; 32], + pub address_merkle_tree_root_index: u16, +} + +pub fn unpack_new_address_params( + address_params: &crate::NewAddressParamsPacked, + remaining_accounts: &[AccountInfo], +) -> NewAddressParams { + let address_merkle_tree_pubkey = + remaining_accounts[address_params.address_merkle_tree_account_index as usize].key(); + let address_queue_pubkey = + remaining_accounts[address_params.address_queue_account_index as usize].key(); + + NewAddressParams { + seed: address_params.seed, + address_queue_pubkey: *address_queue_pubkey, + address_merkle_tree_pubkey: *address_merkle_tree_pubkey, + address_merkle_tree_root_index: address_params.address_merkle_tree_root_index, + } +} + +pub mod v1 { + use super::*; + + /// Derives a single address seed for a compressed account, based on the + /// provided multiple `seeds`, `program_id` and `merkle_tree_pubkey`. + pub fn derive_address_seed(seeds: &[&[u8]], program_id: &Pubkey) -> [u8; 32] { + light_sdk_types::address::v1::derive_address_seed(seeds, program_id) + } + + /// Derives an address from provided seeds. Returns that address and a singular + /// seed. + pub fn derive_address( + seeds: &[&[u8]], + merkle_tree_pubkey: &Pubkey, + program_id: &Pubkey, + ) -> ([u8; 32], [u8; 32]) { + light_sdk_types::address::v1::derive_address(seeds, merkle_tree_pubkey, program_id) + } +} diff --git a/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs new file mode 100644 index 0000000000..fe1b24e55a --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs @@ -0,0 +1,249 @@ +use light_sdk_types::CpiSigner; +use pinocchio::{account_info::AccountInfo, instruction::AccountMeta, msg}; + +use crate::error::{LightSdkError, Result}; + +#[derive(Debug, Copy, Clone)] +pub struct CpiAccountsConfig { + pub cpi_context: bool, + pub sol_compression_recipient: bool, + pub sol_pool_pda: bool, + pub cpi_signer: CpiSigner, +} + +impl CpiAccountsConfig { + pub const fn new(cpi_signer: CpiSigner) -> Self { + Self { + cpi_context: false, + sol_compression_recipient: false, + sol_pool_pda: false, + cpi_signer, + } + } + + pub const fn new_with_cpi_context(cpi_signer: CpiSigner) -> Self { + Self { + cpi_context: true, + sol_compression_recipient: false, + sol_pool_pda: false, + cpi_signer, + } + } + + pub fn cpi_signer(&self) -> [u8; 32] { + self.cpi_signer.cpi_signer + } + + pub fn bump(&self) -> u8 { + self.cpi_signer.bump + } +} + +#[repr(usize)] +pub enum CompressionCpiAccountIndex { + LightSystemProgram, + Authority, + RegisteredProgramPda, + NoopProgram, + AccountCompressionAuthority, + AccountCompressionProgram, + InvokingProgram, + SolPoolPda, + DecompressionRecipent, + SystemProgram, + CpiContext, +} + +pub const SYSTEM_ACCOUNTS_LEN: usize = 11; + +pub struct CpiAccounts<'a> { + fee_payer: &'a AccountInfo, + accounts: &'a [AccountInfo], + config: CpiAccountsConfig, +} + +impl<'a> CpiAccounts<'a> { + pub fn new( + fee_payer: &'a AccountInfo, + accounts: &'a [AccountInfo], + config: CpiSigner, + ) -> Result { + let new = Self { + fee_payer, + accounts, + config: CpiAccountsConfig::new(config), + }; + if accounts.len() < new.system_accounts_len() { + return Err(LightSdkError::FewerAccountsThanSystemAccounts); + } + Ok(new) + } + + pub fn new_with_config( + fee_payer: &'a AccountInfo, + accounts: &'a [AccountInfo], + config: CpiAccountsConfig, + ) -> Result { + let new = Self { + fee_payer, + accounts, + config, + }; + if accounts.len() < new.system_accounts_len() { + return Err(LightSdkError::FewerAccountsThanSystemAccounts); + } + Ok(new) + } + + pub fn fee_payer(&self) -> &'a AccountInfo { + self.fee_payer + } + + pub fn light_system_program(&self) -> &'a AccountInfo { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::LightSystemProgram as usize) + .unwrap() + } + + pub fn authority(&self) -> &'a AccountInfo { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::Authority as usize) + .unwrap() + } + + pub fn invoking_program(&self) -> &'a AccountInfo { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::InvokingProgram as usize) + .unwrap() + } + + pub fn self_program_id(&self) -> [u8; 32] { + self.config.cpi_signer.program_id + } + + pub fn bump(&self) -> u8 { + self.config.cpi_signer.bump + } + + pub fn to_account_infos(&self) -> Vec<&'a AccountInfo> { + let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_infos.push(self.fee_payer); + // Skip the first account (light_system_program) and add the rest + self.accounts[1..] + .iter() + .for_each(|acc| account_infos.push(acc)); + let mut current_index = 7; + if !self.config.sol_pool_pda { + account_infos.insert(current_index, self.light_system_program()); + } + current_index += 1; + + if !self.config.sol_compression_recipient { + account_infos.insert(current_index, self.light_system_program()); + } + current_index += 1; + // system program + current_index += 1; + + if !self.config.cpi_context { + account_infos.insert(current_index, self.light_system_program()); + } + account_infos + } + + pub fn to_account_metas(&self) -> Vec { + let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_metas.push(AccountMeta::writable_signer(self.fee_payer.key())); + account_metas.push(AccountMeta::readonly_signer(self.authority().key())); + + account_metas.push(AccountMeta::readonly( + self.accounts[CompressionCpiAccountIndex::RegisteredProgramPda as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + self.accounts[CompressionCpiAccountIndex::NoopProgram as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + self.accounts[CompressionCpiAccountIndex::AccountCompressionAuthority as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + self.accounts[CompressionCpiAccountIndex::AccountCompressionProgram as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + self.accounts[CompressionCpiAccountIndex::InvokingProgram as usize].key(), + )); + let mut current_index = 7; + if !self.config.sol_pool_pda { + account_metas.push(AccountMeta::readonly(self.light_system_program().key())); + } else { + account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); + current_index += 1; + } + + if !self.config.sol_compression_recipient { + account_metas.push(AccountMeta::readonly(self.light_system_program().key())); + } else { + account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); + current_index += 1; + } + + // System program - use default (all zeros) + account_metas.push(AccountMeta::readonly(&[0u8; 32])); + current_index += 1; + + if !self.config.cpi_context { + account_metas.push(AccountMeta::readonly(self.light_system_program().key())); + } else { + account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); + current_index += 1; + } + + // Add remaining tree accounts + self.accounts[current_index..].iter().for_each(|acc| { + let account_meta = if acc.is_writable() { + AccountMeta::writable(acc.key()) + } else { + AccountMeta::readonly(acc.key()) + }; + account_metas.push(account_meta); + }); + + account_metas + } + + pub fn system_accounts_len(&self) -> usize { + let mut len = 7; // Base system accounts + + if self.config.sol_pool_pda { + len += 1; + } + + if self.config.sol_compression_recipient { + len += 1; + } + + if self.config.cpi_context { + len += 1; + } + + len + 1 // Add system program + } + + pub fn account_infos(&self) -> &'a [AccountInfo] { + self.accounts + } + + pub fn tree_accounts(&self) -> &'a [AccountInfo] { + msg!(format!("tree_accounts: {}", self.accounts.len()).as_str()); + msg!(format!("offset {}", self.system_accounts_len()).as_str()); + + // Debug print all accounts + for (i, acc) in self.accounts.iter().enumerate() { + msg!(format!(" accounts[{}] = {:?}", i, acc.key()).as_str()); + } + + &self.accounts[self.system_accounts_len()..] + } +} diff --git a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs new file mode 100644 index 0000000000..f6685d0e50 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs @@ -0,0 +1,206 @@ +use light_compressed_account::{ + compressed_account::{ + CompressedAccount, CompressedAccountData, PackedCompressedAccountWithMerkleContext, + }, + discriminators::DISCRIMINATOR_INVOKE_CPI, + instruction_data::{ + cpi_context::CompressedCpiContext, + data::{NewAddressParamsPacked, OutputCompressedAccountWithPackedContext}, + invoke_cpi::InstructionDataInvokeCpi, + with_account_info::CompressedAccountInfo, + }, +}; +use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM}; +use pinocchio::{cpi::slice_invoke_signed, log::sol_log_compute_units, msg, pubkey::Pubkey}; + +use crate::{ + cpi::CpiAccounts, + error::{LightSdkError, Result}, + BorshSerialize, ValidityProof, +}; + +// Trait to provide the missing methods for CompressedAccountInfo +pub trait CompressedAccountInfoExt { + fn input_compressed_account( + &self, + owner: Pubkey, + ) -> Result>; + fn output_compressed_account( + &self, + owner: Pubkey, + ) -> Result>; +} + +impl CompressedAccountInfoExt for CompressedAccountInfo { + fn input_compressed_account( + &self, + owner: Pubkey, + ) -> Result> { + match self.input.as_ref() { + Some(input) => { + let data = Some(CompressedAccountData { + discriminator: input.discriminator, + data: Vec::new(), + data_hash: input.data_hash, + }); + Ok(Some(PackedCompressedAccountWithMerkleContext { + compressed_account: CompressedAccount { + owner: owner.into(), + lamports: input.lamports, + address: self.address, + data, + }, + merkle_context: input.merkle_context, + root_index: input.root_index, + read_only: false, + })) + } + None => Ok(None), + } + } + + fn output_compressed_account( + &self, + owner: Pubkey, + ) -> Result> { + match self.output.as_ref() { + Some(output) => { + let data = Some(CompressedAccountData { + discriminator: output.discriminator, + data: output.data.clone(), + data_hash: output.data_hash, + }); + Ok(Some(OutputCompressedAccountWithPackedContext { + compressed_account: CompressedAccount { + owner: owner.into(), + lamports: output.lamports, + address: self.address, + data, + }, + merkle_tree_index: output.output_merkle_tree_index, + })) + } + None => Ok(None), + } + } +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub struct CpiInputs { + pub proof: ValidityProof, + pub account_infos: Option>, + pub new_addresses: Option>, + pub compress_or_decompress_lamports: Option, + pub is_compress: bool, + pub cpi_context: Option, +} + +impl CpiInputs { + pub fn new(proof: ValidityProof, account_infos: Vec) -> Self { + Self { + proof, + account_infos: Some(account_infos), + ..Default::default() + } + } + + pub fn new_with_address( + proof: ValidityProof, + account_infos: Vec, + new_addresses: Vec, + ) -> Self { + Self { + proof, + account_infos: Some(account_infos), + new_addresses: Some(new_addresses), + ..Default::default() + } + } + + pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts) -> Result<()> { + light_system_progam_instruction_invoke_cpi(self, &cpi_accounts) + } +} + +pub fn light_system_progam_instruction_invoke_cpi( + cpi_inputs: CpiInputs, + cpi_accounts: &CpiAccounts, +) -> Result<()> { + let owner = *cpi_accounts.invoking_program().key(); + let (input_compressed_accounts_with_merkle_context, output_compressed_accounts) = + if let Some(account_infos) = cpi_inputs.account_infos.as_ref() { + let mut input_compressed_accounts_with_merkle_context = + Vec::with_capacity(account_infos.len()); + let mut output_compressed_accounts = Vec::with_capacity(account_infos.len()); + for account_info in account_infos.iter() { + if let Some(input_account) = + CompressedAccountInfoExt::input_compressed_account(account_info, owner)? + { + input_compressed_accounts_with_merkle_context.push(input_account); + } + if let Some(output_account) = + CompressedAccountInfoExt::output_compressed_account(account_info, owner)? + { + output_compressed_accounts.push(output_account); + } + } + ( + input_compressed_accounts_with_merkle_context, + output_compressed_accounts, + ) + } else { + (vec![], vec![]) + }; + + let inputs = InstructionDataInvokeCpi { + proof: cpi_inputs.proof.0, + new_address_params: cpi_inputs.new_addresses.unwrap_or_default(), + relay_fee: None, + input_compressed_accounts_with_merkle_context, + output_compressed_accounts, + compress_or_decompress_lamports: cpi_inputs.compress_or_decompress_lamports, + is_compress: cpi_inputs.is_compress, + cpi_context: cpi_inputs.cpi_context, + }; + let inputs = inputs.try_to_vec().map_err(|_| LightSdkError::Borsh)?; + + let mut data = Vec::with_capacity(8 + 4 + inputs.len()); + data.extend_from_slice(&DISCRIMINATOR_INVOKE_CPI); + data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); + data.extend(inputs); + + let account_metas: Vec = cpi_accounts.to_account_metas(); + + // Create instruction with owned data and immediately invoke it + use pinocchio::instruction::{Instruction, Seed, Signer}; + sol_log_compute_units(); + + // Use the precomputed CPI signer and bump from the config + let bump = cpi_accounts.bump(); + sol_log_compute_units(); + let bump_seed = [bump]; + let seed_array = [ + Seed::from(CPI_AUTHORITY_PDA_SEED), + Seed::from(bump_seed.as_slice()), + ]; + let signer = Signer::from(&seed_array); + sol_log_compute_units(); + + let instruction = Instruction { + program_id: &PROGRAM_ID_LIGHT_SYSTEM, + accounts: &account_metas, + data: &data, + }; + sol_log_compute_units(); + let account_infos = cpi_accounts.to_account_infos(); + sol_log_compute_units(); + + match slice_invoke_signed(&instruction, &account_infos, &[signer]) { + Ok(()) => {} + Err(e) => { + msg!(format!("slice_invoke_signed failed: {:?}", e).as_str()); + return Err(LightSdkError::ProgramError(e)); + } + } + Ok(()) +} diff --git a/sdk-libs/sdk-pinocchio/src/cpi/mod.rs b/sdk-libs/sdk-pinocchio/src/cpi/mod.rs new file mode 100644 index 0000000000..f077b4ab32 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/cpi/mod.rs @@ -0,0 +1,5 @@ +pub mod accounts; +pub mod invoke; + +pub use accounts::*; +pub use invoke::*; diff --git a/sdk-libs/sdk-pinocchio/src/error.rs b/sdk-libs/sdk-pinocchio/src/error.rs new file mode 100644 index 0000000000..3042a6d853 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/error.rs @@ -0,0 +1,147 @@ +use light_hasher::HasherError; +pub use light_sdk_types::error::LightSdkTypesError; +use light_zero_copy::errors::ZeroCopyError; +use pinocchio::program_error::ProgramError; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Debug, Error, PartialEq)] +pub enum LightSdkError { + #[error("Constraint violation")] + ConstraintViolation, + #[error("Invalid light-system-program ID")] + InvalidLightSystemProgram, + #[error("Expected accounts in the instruction")] + ExpectedAccounts, + #[error("Expected address Merkle context to be provided")] + ExpectedAddressTreeInfo, + #[error("Expected address root index to be provided")] + ExpectedAddressRootIndex, + #[error("Accounts with a specified input are expected to have data")] + ExpectedData, + #[error("Accounts with specified data are expected to have a discriminator")] + ExpectedDiscriminator, + #[error("Accounts with specified data are expected to have a hash")] + ExpectedHash, + #[error("Expected the `{0}` light account to be provided")] + ExpectedLightSystemAccount(String), + #[error("`mut` and `close` accounts are expected to have a Merkle context")] + ExpectedMerkleContext, + #[error("Expected root index to be provided")] + ExpectedRootIndex, + #[error("Cannot transfer lamports from an account without input")] + TransferFromNoInput, + #[error("Cannot transfer from an account without lamports")] + TransferFromNoLamports, + #[error("Account, from which a transfer was attempted, has insufficient amount of lamports")] + TransferFromInsufficientLamports, + #[error("Integer overflow resulting from too large resulting amount")] + TransferIntegerOverflow, + #[error("Borsh error.")] + Borsh, + #[error("Fewer accounts than number of system accounts.")] + FewerAccountsThanSystemAccounts, + #[error("InvalidCpiSignerAccount")] + InvalidCpiSignerAccount, + #[error("Missing meta field: {0}")] + MissingField(String), + #[error("Output state tree index is none. Use an CompressedAccountMeta type with output tree index to initialize or update accounts.")] + OutputStateTreeIndexIsNone, + #[error("Address is none during initialization")] + InitAddressIsNone, + #[error("Address is none during initialization with address")] + InitWithAddressIsNone, + #[error("Output is none during initialization with address")] + InitWithAddressOutputIsNone, + #[error("Address is none during meta mutation")] + MetaMutAddressIsNone, + #[error("Input is none during meta mutation")] + MetaMutInputIsNone, + #[error("Output lamports is none during meta mutation")] + MetaMutOutputLamportsIsNone, + #[error("Output is none during meta mutation")] + MetaMutOutputIsNone, + #[error("Address is none during meta close")] + MetaCloseAddressIsNone, + #[error("Input is none during meta close")] + MetaCloseInputIsNone, + #[error(transparent)] + Hasher(#[from] HasherError), + #[error(transparent)] + ZeroCopy(#[from] ZeroCopyError), + #[error("Program error: {0:?}")] + ProgramError(ProgramError), +} + +impl From for LightSdkError { + fn from(error: ProgramError) -> Self { + LightSdkError::ProgramError(error) + } +} + +impl From for ProgramError { + fn from(e: LightSdkError) -> Self { + ProgramError::Custom(e.into()) + } +} + +impl From for LightSdkError { + fn from(e: LightSdkTypesError) -> Self { + match e { + LightSdkTypesError::InitAddressIsNone => LightSdkError::InitAddressIsNone, + LightSdkTypesError::InitWithAddressIsNone => LightSdkError::InitWithAddressIsNone, + LightSdkTypesError::InitWithAddressOutputIsNone => { + LightSdkError::InitWithAddressOutputIsNone + } + LightSdkTypesError::MetaMutAddressIsNone => LightSdkError::MetaMutAddressIsNone, + LightSdkTypesError::MetaMutInputIsNone => LightSdkError::MetaMutInputIsNone, + LightSdkTypesError::MetaMutOutputLamportsIsNone => { + LightSdkError::MetaMutOutputLamportsIsNone + } + LightSdkTypesError::MetaMutOutputIsNone => LightSdkError::MetaMutOutputIsNone, + LightSdkTypesError::MetaCloseAddressIsNone => LightSdkError::MetaCloseAddressIsNone, + LightSdkTypesError::MetaCloseInputIsNone => LightSdkError::MetaCloseInputIsNone, + LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), + } + } +} + +impl From for u32 { + fn from(e: LightSdkError) -> Self { + match e { + LightSdkError::ConstraintViolation => 14001, + LightSdkError::InvalidLightSystemProgram => 14002, + LightSdkError::ExpectedAccounts => 14003, + LightSdkError::ExpectedAddressTreeInfo => 14004, + LightSdkError::ExpectedAddressRootIndex => 14005, + LightSdkError::ExpectedData => 14006, + LightSdkError::ExpectedDiscriminator => 14007, + LightSdkError::ExpectedHash => 14008, + LightSdkError::ExpectedLightSystemAccount(_) => 14009, + LightSdkError::ExpectedMerkleContext => 14010, + LightSdkError::ExpectedRootIndex => 14011, + LightSdkError::TransferFromNoInput => 14012, + LightSdkError::TransferFromNoLamports => 14013, + LightSdkError::TransferFromInsufficientLamports => 14014, + LightSdkError::TransferIntegerOverflow => 14015, + LightSdkError::Borsh => 14016, + LightSdkError::FewerAccountsThanSystemAccounts => 14017, + LightSdkError::InvalidCpiSignerAccount => 14018, + LightSdkError::MissingField(_) => 14019, + LightSdkError::OutputStateTreeIndexIsNone => 14020, + LightSdkError::InitAddressIsNone => 14021, + LightSdkError::InitWithAddressIsNone => 14022, + LightSdkError::InitWithAddressOutputIsNone => 14023, + LightSdkError::MetaMutAddressIsNone => 14024, + LightSdkError::MetaMutInputIsNone => 14025, + LightSdkError::MetaMutOutputLamportsIsNone => 14026, + LightSdkError::MetaMutOutputIsNone => 14027, + LightSdkError::MetaCloseAddressIsNone => 14028, + LightSdkError::MetaCloseInputIsNone => 14029, + LightSdkError::Hasher(e) => e.into(), + LightSdkError::ZeroCopy(e) => e.into(), + LightSdkError::ProgramError(e) => u64::from(e) as u32, + } + } +} diff --git a/sdk-libs/sdk-pinocchio/src/instruction/mod.rs b/sdk-libs/sdk-pinocchio/src/instruction/mod.rs new file mode 100644 index 0000000000..b28b45fcba --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/instruction/mod.rs @@ -0,0 +1 @@ +pub use light_sdk_types::instruction::*; diff --git a/sdk-libs/sdk-pinocchio/src/lib.rs b/sdk-libs/sdk-pinocchio/src/lib.rs new file mode 100644 index 0000000000..24626ead6b --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/lib.rs @@ -0,0 +1,17 @@ +pub mod account; +pub mod address; +pub mod cpi; +pub mod error; +pub mod instruction; + +pub use account::LightAccount; +pub use borsh::{BorshDeserialize, BorshSerialize}; +pub use cpi::{CpiAccounts, CpiAccountsConfig, CpiInputs}; +pub use light_account_checks::discriminator::Discriminator as LightDiscriminator; +pub use light_compressed_account::{ + self, + instruction_data::{compressed_proof::ValidityProof, data::*}, +}; +pub use light_hasher; +pub use light_sdk_macros::{derive_light_cpi_signer, LightDiscriminator, LightHasher}; +pub use light_sdk_types::{self, constants, CpiSigner}; diff --git a/sdk-libs/sdk-types/Cargo.toml b/sdk-libs/sdk-types/Cargo.toml new file mode 100644 index 0000000000..64613a2940 --- /dev/null +++ b/sdk-libs/sdk-types/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "light-sdk-types" +version = "0.9.1" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/lightprotocol/light-protocol" +description = "Core types for Light Protocol SDK" + +[features] +anchor = ["anchor-lang", "light-compressed-account/anchor"] + +[dependencies] +anchor-lang = { workspace = true, optional = true } +# Light Protocol dependencies +light-hasher = { workspace = true } +light-compressed-account = { workspace = true } +light-macros = { workspace = true } +light-zero-copy = { workspace = true } + +# External dependencies +borsh = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +solana-pubkey = { workspace = true } diff --git a/sdk-libs/sdk-types/src/address.rs b/sdk-libs/sdk-types/src/address.rs new file mode 100644 index 0000000000..a1394fbb81 --- /dev/null +++ b/sdk-libs/sdk-types/src/address.rs @@ -0,0 +1,160 @@ +pub mod v1 { + use light_hasher::{hash_to_field_size::hashv_to_bn254_field_size_be, Hasher, Keccak}; + + /// Derives a single address seed for a compressed account, based on the + /// provided multiple `seeds`, `program_id` and `merkle_tree_pubkey`. + /// + /// # Examples + /// + /// ```ignore + /// use light_sdk::{address::derive_address, pubkey}; + /// + /// let address = derive_address( + /// &[b"my_compressed_account"], + /// &crate::ID, + /// ); + /// ``` + pub fn derive_address_seed(seeds: &[&[u8]], program_id: &[u8; 32]) -> [u8; 32] { + let mut inputs = Vec::with_capacity(seeds.len() + 1); + + inputs.push(program_id.as_slice()); + + inputs.extend(seeds); + + let seed = hashv_to_bn254_field_size_be_legacy(inputs.as_slice()); + seed + } + + fn hashv_to_bn254_field_size_be_legacy(bytes: &[&[u8]]) -> [u8; 32] { + let mut hashed_value: [u8; 32] = Keccak::hashv(bytes).unwrap(); + // Truncates to 31 bytes so that value is less than bn254 Fr modulo + // field size. + hashed_value[0] = 0; + hashed_value + } + + /// Derives an address for a compressed account, based on the provided singular + /// `seed` and `merkle_tree_pubkey`: + pub(crate) fn derive_address_from_seed( + address_seed: &[u8; 32], + merkle_tree_pubkey: &[u8; 32], + ) -> [u8; 32] { + let input = [merkle_tree_pubkey.as_slice(), address_seed.as_slice()]; + hashv_to_bn254_field_size_be(input.as_slice()) + } + + /// Derives an address from provided seeds. Returns that address and a singular + /// seed. + /// + /// # Examples + /// + /// ```ignore + /// use light_sdk::{address::derive_address, pubkey}; + /// + /// let address_tree_info = { + /// address_merkle_tree_pubkey: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"), + /// address_queue_pubkey: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"), + /// }; + /// let (address, address_seed) = derive_address( + /// &[b"my_compressed_account"], + /// &address_tree_info, + /// &crate::ID, + /// ); + /// ``` + pub fn derive_address( + seeds: &[&[u8]], + merkle_tree_pubkey: &[u8; 32], + program_id: &[u8; 32], + ) -> ([u8; 32], [u8; 32]) { + let address_seed = derive_address_seed(seeds, program_id); + let address = derive_address_from_seed(&address_seed, merkle_tree_pubkey); + + (address, address_seed) + } +} + +#[cfg(test)] +mod test { + use super::v1::*; + + #[allow(dead_code)] + #[derive(Debug)] + struct AddressTreeInfo { + pub address_merkle_tree_pubkey: [u8; 32], + pub address_queue_pubkey: [u8; 32], + } + + #[test] + fn test_derive_address_seed() { + use light_macros::pubkey_array; + let program_id = pubkey_array!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz"); + + let address_seed = derive_address_seed(&[b"foo", b"bar"], &program_id); + assert_eq!( + address_seed, + [ + 0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155, + 251, 72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110 + ] + ); + + let address_seed = derive_address_seed(&[b"ayy", b"lmao"], &program_id); + assert_eq!( + address_seed, + [ + 0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120, + 214, 152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233 + ] + ); + } + + #[test] + fn test_derive_address() { + use light_macros::pubkey_array; + let address_tree_info = AddressTreeInfo { + address_merkle_tree_pubkey: [0; 32], + address_queue_pubkey: [0; 32], + }; + let program_id = pubkey_array!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz"); + + let seeds: &[&[u8]] = &[b"foo", b"bar"]; + let expected_address_seed = [ + 0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155, 251, + 72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110, + ]; + let expected_address = [ + 0, 141, 60, 24, 250, 156, 15, 250, 237, 196, 171, 243, 182, 10, 8, 66, 147, 57, 27, + 209, 222, 86, 109, 234, 161, 219, 142, 43, 121, 104, 16, 63, + ]; + + let address_seed = derive_address_seed(seeds, &program_id); + assert_eq!(address_seed, expected_address_seed); + let (address, address_seed) = derive_address( + seeds, + &address_tree_info.address_merkle_tree_pubkey, + &program_id, + ); + assert_eq!(address_seed, expected_address_seed); + assert_eq!(address, expected_address); + + let seeds: &[&[u8]] = &[b"ayy", b"lmao"]; + let expected_address_seed = [ + 0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120, 214, + 152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233, + ]; + let expected_address = [ + 0, 104, 207, 102, 176, 61, 126, 178, 11, 174, 213, 195, 17, 36, 71, 95, 0, 231, 179, + 87, 218, 195, 114, 84, 47, 97, 176, 93, 106, 175, 72, 115, + ]; + + let address_seed = derive_address_seed(seeds, &program_id); + assert_eq!(address_seed, expected_address_seed); + let (address, address_seed) = derive_address( + seeds, + &address_tree_info.address_merkle_tree_pubkey, + &program_id, + ); + assert_eq!(address_seed, expected_address_seed); + assert_eq!(address, expected_address); + } +} diff --git a/sdk-libs/sdk-types/src/constants.rs b/sdk-libs/sdk-types/src/constants.rs new file mode 100644 index 0000000000..8cb8ad3252 --- /dev/null +++ b/sdk-libs/sdk-types/src/constants.rs @@ -0,0 +1,38 @@ +use light_macros::pubkey_array; + +pub const ACCOUNT_COMPRESSION_PROGRAM_ID: [u8; 32] = + pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); +pub const SYSTEM_PROGRAM_ID: [u8; 32] = + pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); +pub const REGISTERED_PROGRAM_PDA: [u8; 32] = + pubkey_array!("35hkDgaAKwMCaxRz2ocSZ6NaUrtKkyNqU6c4RV3tYJRh"); + +/// Seed of the CPI authority. +pub const CPI_AUTHORITY_PDA_SEED: &[u8] = b"cpi_authority"; + +/// ID of the account-compression program. +pub const PROGRAM_ID_ACCOUNT_COMPRESSION: [u8; 32] = + pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); +pub const PROGRAM_ID_NOOP: [u8; 32] = pubkey_array!("noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"); +/// ID of the light-system program. +pub const PROGRAM_ID_LIGHT_SYSTEM: [u8; 32] = + pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); +/// ID of the light-compressed-token program. +pub const PROGRAM_ID_LIGHT_TOKEN: [u8; 32] = + pubkey_array!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); + +pub const STATE_MERKLE_TREE_HEIGHT: usize = 26; +pub const STATE_MERKLE_TREE_CHANGELOG: usize = 1400; +pub const STATE_MERKLE_TREE_ROOTS: usize = 2400; +pub const STATE_MERKLE_TREE_CANOPY_DEPTH: usize = 10; + +pub const ADDRESS_MERKLE_TREE_HEIGHT: usize = 26; +pub const ADDRESS_MERKLE_TREE_CHANGELOG: usize = 1400; +pub const ADDRESS_MERKLE_TREE_ROOTS: usize = 2400; +pub const ADDRESS_MERKLE_TREE_CANOPY_DEPTH: usize = 10; +pub const ADDRESS_MERKLE_TREE_INDEXED_CHANGELOG: usize = 1400; + +pub const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [2, 0, 0, 0, 0, 0, 0, 0]; + +pub const ADDRESS_TREE_V1: [u8; 32] = pubkey_array!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"); +pub const ADDRESS_QUEUE_V1: [u8; 32] = pubkey_array!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"); diff --git a/sdk-libs/sdk-types/src/error.rs b/sdk-libs/sdk-types/src/error.rs new file mode 100644 index 0000000000..9653c3a706 --- /dev/null +++ b/sdk-libs/sdk-types/src/error.rs @@ -0,0 +1,45 @@ +use light_hasher::HasherError; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Debug, Error, PartialEq)] +pub enum LightSdkTypesError { + #[error("Address is none during initialization")] + InitAddressIsNone, + #[error("Address is none during initialization with address")] + InitWithAddressIsNone, + #[error("Output is none during initialization with address")] + InitWithAddressOutputIsNone, + #[error("Address is none during meta mutation")] + MetaMutAddressIsNone, + #[error("Input is none during meta mutation")] + MetaMutInputIsNone, + #[error("Output lamports is none during meta mutation")] + MetaMutOutputLamportsIsNone, + #[error("Output is none during meta mutation")] + MetaMutOutputIsNone, + #[error("Address is none during meta close")] + MetaCloseAddressIsNone, + #[error("Input is none during meta close")] + MetaCloseInputIsNone, + #[error(transparent)] + Hasher(#[from] HasherError), +} + +impl From for u32 { + fn from(e: LightSdkTypesError) -> Self { + match e { + LightSdkTypesError::InitAddressIsNone => 14021, + LightSdkTypesError::InitWithAddressIsNone => 14022, + LightSdkTypesError::InitWithAddressOutputIsNone => 14023, + LightSdkTypesError::MetaMutAddressIsNone => 14024, + LightSdkTypesError::MetaMutInputIsNone => 14025, + LightSdkTypesError::MetaMutOutputLamportsIsNone => 14026, + LightSdkTypesError::MetaMutOutputIsNone => 14027, + LightSdkTypesError::MetaCloseAddressIsNone => 14028, + LightSdkTypesError::MetaCloseInputIsNone => 14029, + LightSdkTypesError::Hasher(e) => e.into(), + } + } +} diff --git a/sdk-libs/sdk/src/account_info.rs b/sdk-libs/sdk-types/src/instruction/account_info.rs similarity index 76% rename from sdk-libs/sdk/src/account_info.rs rename to sdk-libs/sdk-types/src/instruction/account_info.rs index 6687cdffc8..1b5ab89814 100644 --- a/sdk-libs/sdk/src/account_info.rs +++ b/sdk-libs/sdk-types/src/instruction/account_info.rs @@ -7,10 +7,11 @@ use light_compressed_account::{ data::OutputCompressedAccountWithPackedContext, with_account_info::{CompressedAccountInfo, InAccountInfo}, }, - CompressedAccountError, + Pubkey, }; -use crate::{error::LightSdkError, instruction::account_meta::CompressedAccountMetaTrait, msg}; +use super::account_meta::CompressedAccountMetaTrait; +use crate::error::LightSdkTypesError; pub trait InAccountInfoTrait { fn input_meta( @@ -52,7 +53,7 @@ pub trait AccountInfoTrait { discriminator: [u8; 8], address: Option<[u8; 32]>, output_state_tree_index: u8, - ) -> Result<(), CompressedAccountError>; + ) -> Result<(), LightSdkTypesError>; fn meta_mut( &mut self, @@ -60,22 +61,22 @@ pub trait AccountInfoTrait { input_data_hash: [u8; 32], discriminator: [u8; 8], output_state_tree_index: u8, - ) -> Result<(), CompressedAccountError>; + ) -> Result<(), LightSdkTypesError>; fn meta_close( &mut self, input_account_meta: &M, input_data_hash: [u8; 32], discriminator: [u8; 8], - ) -> Result<(), CompressedAccountError>; + ) -> Result<(), LightSdkTypesError>; fn input_compressed_account( &self, - owner: crate::Pubkey, - ) -> Result, LightSdkError>; + owner: Pubkey, + ) -> Result, LightSdkTypesError>; fn output_compressed_account( &self, - owner: crate::Pubkey, - ) -> Result, LightSdkError>; + owner: Pubkey, + ) -> Result, LightSdkTypesError>; } impl AccountInfoTrait for CompressedAccountInfo { @@ -87,24 +88,21 @@ impl AccountInfoTrait for CompressedAccountInfo { discriminator: [u8; 8], address: Option<[u8; 32]>, output_state_tree_index: u8, - ) -> Result<(), CompressedAccountError> { + ) -> Result<(), LightSdkTypesError> { if let Some(self_address) = self.address.as_mut() { if let Some(address) = address { self_address.copy_from_slice(&address); } else { - msg!("init: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::InitAddressIsNone); } } else { - msg!("init_with_address: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::InitWithAddressIsNone); } if let Some(output) = self.output.as_mut() { output.output_merkle_tree_index = output_state_tree_index; output.discriminator = discriminator; } else { - msg!("init_with_address: output is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::InitWithAddressOutputIsNone); } Ok(()) } @@ -115,24 +113,21 @@ impl AccountInfoTrait for CompressedAccountInfo { input_data_hash: [u8; 32], discriminator: [u8; 8], output_state_tree_index: u8, - ) -> Result<(), CompressedAccountError> { + ) -> Result<(), LightSdkTypesError> { if let Some(self_address) = self.address.as_mut() { if let Some(address) = input_account_meta.get_address().as_ref() { *self_address = *address; } else { - msg!("from_z_meta_mut: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutAddressIsNone); } } else { - msg!("from_z_meta_mut: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaCloseAddressIsNone); } if let Some(input) = self.input.as_mut() { input.input_meta(input_account_meta, input_data_hash, discriminator); } else { - msg!("from_z_meta_mut: input is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutInputIsNone); } if let Some(output) = self.output.as_mut() { @@ -142,12 +137,10 @@ impl AccountInfoTrait for CompressedAccountInfo { if let Some(input_lamports) = input_account_meta.get_lamports() { output.lamports = input_lamports; } else { - msg!("from_z_meta_mut: output lamports is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutOutputLamportsIsNone); } } else { - msg!("from_z_meta_mut: output is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutOutputIsNone); } Ok(()) } @@ -157,24 +150,21 @@ impl AccountInfoTrait for CompressedAccountInfo { input_account_meta: &M, input_data_hash: [u8; 32], discriminator: [u8; 8], - ) -> Result<(), CompressedAccountError> { + ) -> Result<(), LightSdkTypesError> { if let Some(self_address) = self.address.as_mut() { if let Some(address) = input_account_meta.get_address() { self_address.copy_from_slice(&address); } else { - msg!("from_z_meta_mut: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutAddressIsNone); } } else { - msg!("from_z_meta_mut: address is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaCloseAddressIsNone); } if let Some(input) = self.input.as_mut() { input.input_meta(input_account_meta, input_data_hash, discriminator); } else { - msg!("from_z_meta_mut: input is none"); - return Err(CompressedAccountError::InvalidAccountSize); + return Err(LightSdkTypesError::MetaMutInputIsNone); } Ok(()) @@ -182,8 +172,8 @@ impl AccountInfoTrait for CompressedAccountInfo { fn input_compressed_account( &self, - owner: crate::Pubkey, - ) -> Result, LightSdkError> { + owner: Pubkey, + ) -> Result, LightSdkTypesError> { match self.input.as_ref() { Some(input) => { let data = Some(CompressedAccountData { @@ -209,8 +199,8 @@ impl AccountInfoTrait for CompressedAccountInfo { fn output_compressed_account( &self, - owner: crate::Pubkey, - ) -> Result, LightSdkError> { + owner: Pubkey, + ) -> Result, LightSdkTypesError> { match self.output.as_ref() { Some(output) => { let data = Some(CompressedAccountData { diff --git a/sdk-libs/sdk/src/instruction/account_meta.rs b/sdk-libs/sdk-types/src/instruction/account_meta.rs similarity index 98% rename from sdk-libs/sdk/src/instruction/account_meta.rs rename to sdk-libs/sdk-types/src/instruction/account_meta.rs index a0929f5fcb..49c4ff2336 100644 --- a/sdk-libs/sdk/src/instruction/account_meta.rs +++ b/sdk-libs/sdk-types/src/instruction/account_meta.rs @@ -1,4 +1,5 @@ -use crate::{instruction::tree_info::PackedStateTreeInfo, AnchorDeserialize, AnchorSerialize}; +use super::tree_info::PackedStateTreeInfo; +use crate::{AnchorDeserialize, AnchorSerialize}; /// CompressedAccountMeta (context, address, root_index, output_state_tree_index) /// CompressedAccountMetaNoLamportsNoAddress (context, root_index, output_state_tree_index) diff --git a/sdk-libs/sdk-types/src/instruction/mod.rs b/sdk-libs/sdk-types/src/instruction/mod.rs new file mode 100644 index 0000000000..b2d2019e94 --- /dev/null +++ b/sdk-libs/sdk-types/src/instruction/mod.rs @@ -0,0 +1,4 @@ +pub mod account_info; +pub mod account_meta; +mod tree_info; +pub use tree_info::*; diff --git a/sdk-libs/sdk-types/src/instruction/tree_info.rs b/sdk-libs/sdk-types/src/instruction/tree_info.rs new file mode 100644 index 0000000000..c65003fabc --- /dev/null +++ b/sdk-libs/sdk-types/src/instruction/tree_info.rs @@ -0,0 +1,30 @@ +use light_compressed_account::instruction_data::data::NewAddressParamsPacked; + +use crate::{AnchorDeserialize, AnchorSerialize}; + +#[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] +pub struct PackedStateTreeInfo { + pub root_index: u16, + pub prove_by_index: bool, + pub merkle_tree_pubkey_index: u8, + pub queue_pubkey_index: u8, + pub leaf_index: u32, +} + +#[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] +pub struct PackedAddressTreeInfo { + pub address_merkle_tree_pubkey_index: u8, + pub address_queue_pubkey_index: u8, + pub root_index: u16, +} + +impl PackedAddressTreeInfo { + pub fn into_new_address_params_packed(self, seed: [u8; 32]) -> NewAddressParamsPacked { + NewAddressParamsPacked { + address_merkle_tree_account_index: self.address_merkle_tree_pubkey_index, + address_queue_account_index: self.address_queue_pubkey_index, + address_merkle_tree_root_index: self.root_index, + seed, + } + } +} diff --git a/sdk-libs/sdk-types/src/lib.rs b/sdk-libs/sdk-types/src/lib.rs new file mode 100644 index 0000000000..7a49b9e1d8 --- /dev/null +++ b/sdk-libs/sdk-types/src/lib.rs @@ -0,0 +1,19 @@ +pub mod address; +pub mod constants; +pub mod error; +pub mod instruction; + +// Re-exports +#[cfg(feature = "anchor")] +use anchor_lang::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; +pub use constants::*; + +/// Configuration struct containing program ID, CPI signer, and bump for Light Protocol +#[derive(Debug, Clone, Copy, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)] +pub struct CpiSigner { + pub program_id: [u8; 32], + pub cpi_signer: [u8; 32], + pub bump: u8, +} diff --git a/sdk-libs/sdk/Cargo.toml b/sdk-libs/sdk/Cargo.toml index d40492c813..074e412500 100644 --- a/sdk-libs/sdk/Cargo.toml +++ b/sdk-libs/sdk/Cargo.toml @@ -11,31 +11,22 @@ crate-type = ["cdylib", "lib"] name = "light_sdk" [features] -solana = [ - "solana-cpi", - "solana-instruction", - "solana-pubkey", - "solana-pubkey/borsh", - "solana-pubkey/curve25519", - "solana-pubkey/sha2", - "solana-account-info", - "solana-msg", - "solana-program-error", - "borsh", - "light-compressed-account/solana", -] -default = ["solana"] +default = ["borsh"] idl-build = ["anchor-lang/idl-build"] -anchor = ["anchor-lang", "light-compressed-account/anchor"] +anchor = [ + "anchor-lang", + "light-compressed-account/anchor", + "light-sdk-types/anchor", +] v2 = [] [dependencies] -solana-pubkey = { workspace = true, optional = true } -solana-account-info = { workspace = true, optional = true } -solana-msg = { workspace = true, optional = true } -solana-cpi = { workspace = true, optional = true } -solana-program-error = { workspace = true, optional = true } -solana-instruction = { workspace = true, optional = true } +solana-pubkey = { workspace = true, features = ["borsh", "sha2", "curve25519"] } +solana-account-info = { workspace = true } +solana-msg = { workspace = true } +solana-cpi = { workspace = true } +solana-program-error = { workspace = true } +solana-instruction = { workspace = true } anchor-lang = { workspace = true, optional = true } num-bigint = { workspace = true } @@ -45,11 +36,13 @@ borsh = { workspace = true, optional = true } thiserror = { workspace = true } light-sdk-macros = { workspace = true } +light-sdk-types = { workspace = true } light-macros = { workspace = true } light-compressed-account = { workspace = true } light-hasher = { workspace = true } light-account-checks = { workspace = true } +light-zero-copy = { workspace = true } [dev-dependencies] num-bigint = { workspace = true } -light-compressed-account = { workspace = true , features = ["new-unique"]} +light-compressed-account = { workspace = true, features = ["new-unique"] } diff --git a/sdk-libs/sdk/src/account.rs b/sdk-libs/sdk/src/account.rs index 29da44c72c..8206696040 100644 --- a/sdk-libs/sdk/src/account.rs +++ b/sdk-libs/sdk/src/account.rs @@ -1,15 +1,83 @@ +//! # Light Account +//! +//! LightAccount is a wrapper around a compressed account similar to anchor Account. +//! LightAccount abstracts hashing of compressed account data, +//! and wraps the compressed account data so that it is easy to use. +//! +//! Data structs used with LightAccount must implement the traits: +//! - DataHasher +//! - LightDiscriminator +//! - BorshSerialize, BorshDeserialize +//! - Debug, Default, Clone +//! +//! ### Account Data Hashing +//! The LightHasher derives a hashing scheme from the compressed account layout. +//! Alternatively, DataHasher can be implemented manually. +//! +//! Constraints: +//! - Poseidon hashes can only take up to 12 inputs +//! -> use nested structs for structs with more than 12 fields. +//! - Poseidon hashes inputs must be less than bn254 field size (254 bits). +//! hash_to_field_size methods in light hasher can be used to hash data longer than 253 bits. +//! -> use the `#[hash]` attribute for fields with data types greater than 31 bytes eg Pubkeys. +//! +//! ### Compressed account with LightHasher and LightDiscriminator +//! ``` +//! use light_sdk::{LightHasher, LightDiscriminator}; +//! use solana_pubkey::Pubkey; +//! #[derive(Clone, Debug, Default, LightHasher, LightDiscriminator)] +//! pub struct CounterAccount { +//! #[hash] +//! pub owner: Pubkey, +//! pub counter: u64, +//! } +//! ``` +//! +//! +//! ### Create compressed account +//! ```ignore +//! let mut my_compressed_account = LightAccount::<'_, CounterAccount>::new_init( +//! &crate::ID, +//! // Address +//! Some(address), +//! output_tree_index, +//! ); +//! // Set data: +//! my_compressed_account.owner = ctx.accounts.signer.key(); +//! ``` +//! ### Update compressed account +//! ```ignore +//! let mut my_compressed_account = LightAccount::<'_, CounterAccount>::new_mut( +//! &crate::ID, +//! &account_meta, +//! my_compressed_account, +//! ); +//! // Increment counter. +//! my_compressed_account.counter += 1; +//! ``` +//! ### Close compressed account +//! ```ignore +//! let mut my_compressed_account = LightAccount::<'_, CounterAccount>::new_close( +//! &crate::ID, +//! &account_meta_close, +//! my_compressed_account, +//! ); +//! ``` +// TODO: add example for manual hashing + use std::ops::{Deref, DerefMut}; use light_compressed_account::{ compressed_account::PackedMerkleContext, instruction_data::with_account_info::{CompressedAccountInfo, InAccountInfo, OutAccountInfo}, }; -use light_hasher::{DataHasher, Poseidon}; +use light_sdk_types::instruction::account_meta::CompressedAccountMetaTrait; use solana_pubkey::Pubkey; use crate::{ - error::LightSdkError, instruction::account_meta::CompressedAccountMetaTrait, AnchorDeserialize, - AnchorSerialize, LightDiscriminator, + error::LightSdkError, + light_hasher::{DataHasher, Poseidon}, + AnchorDeserialize, AnchorSerialize, LightDiscriminator, }; #[derive(Debug, PartialEq)] diff --git a/sdk-libs/sdk/src/address.rs b/sdk-libs/sdk/src/address.rs index 3adb5043f7..90eb9fd819 100644 --- a/sdk-libs/sdk/src/address.rs +++ b/sdk-libs/sdk/src/address.rs @@ -1,63 +1,50 @@ -use light_compressed_account::instruction_data::data::{ - NewAddressParams, NewAddressParamsPacked as PackedNewAddressParams, +//! ## Addresses +//! Address seed is 32 bytes. Multiple seeds are hashed +//! into a single 32 bytes seed that is passed into the light system program for address creation. +//! Addresses are created independently from compressed accounts. +//! This means that an address can be used in a compressed account but does not have to be used. +//! +//! ### Address uniqueness +//! Every address can only be created once per address tree. +//! Addresses over all address trees are unique but +//! address seeds can be reused in different address trees. +//! If your program security requires global address uniqueness over all address trees, +//! the used address Merkle tree must be checked. +//! If your program just requires addresses to identify accounts but not uniqueness over all address trees +//! the used address Merkle tree does not need to be checked. +//! +//! +//! ### Create address example +//! ```ignore +//! let packed_address_tree_info = instruction_data.address_tree_info; +//! let tree_acounts = cpi_accounts.tree_accounts(); +//! +//! let address_tree_pubkey = tree_acounts[address_tree_info +//! .address_merkle_tree_pubkey_index as usize] +//! .key(); +//! +//! let (address, address_seed) = derive_address( +//! &[b"counter"], +//! &address_tree_pubkey, +//! &crate::ID, +//! ); +//! +//! // Used in cpi to light-system program +//! // to insert the new address into the address merkle tree. +//! let new_address_params = packed_address_tree_info +//! .into_new_address_params_packed(address_seed); +//! ``` + +pub use light_compressed_account::instruction_data::data::NewAddressParams; +/// Struct passed into the light system program cpi to create a new address. +pub use light_compressed_account::instruction_data::data::NewAddressParamsPacked as PackedNewAddressParams; +#[cfg(feature = "v2")] +pub use light_compressed_account::instruction_data::data::{ + NewAddressParamsAssigned, NewAddressParamsAssignedPacked, PackedReadOnlyAddress, + ReadOnlyAddress, }; -use crate::{ - instruction::{pack_accounts::PackedAccounts, tree_info::AddressTreeInfo}, - AccountInfo, -}; - -pub struct AddressWithMerkleContext { - pub address: [u8; 32], - pub address_tree_info: AddressTreeInfo, -} - -pub fn pack_new_addresses_params( - addresses_params: &[NewAddressParams], - remaining_accounts: &mut PackedAccounts, -) -> Vec { - addresses_params - .iter() - .map(|x| { - let address_queue_account_index = - remaining_accounts.insert_or_get(x.address_queue_pubkey.to_bytes().into()); - let address_merkle_tree_account_index = - remaining_accounts.insert_or_get(x.address_merkle_tree_pubkey.to_bytes().into()); - PackedNewAddressParams { - seed: x.seed, - address_queue_account_index, - address_merkle_tree_account_index, - address_merkle_tree_root_index: x.address_merkle_tree_root_index, - } - }) - .collect::>() -} - -pub fn pack_new_address_params( - address_params: NewAddressParams, - remaining_accounts: &mut PackedAccounts, -) -> PackedNewAddressParams { - pack_new_addresses_params(&[address_params], remaining_accounts)[0] -} - -pub fn unpack_new_address_params( - address_params: &PackedNewAddressParams, - remaining_accounts: &[AccountInfo], -) -> NewAddressParams { - let address_merkle_tree_pubkey = - remaining_accounts[address_params.address_merkle_tree_account_index as usize].key; - let address_queue_pubkey = - remaining_accounts[address_params.address_queue_account_index as usize].key; - NewAddressParams { - seed: address_params.seed, - address_queue_pubkey: address_queue_pubkey.to_bytes().into(), - address_merkle_tree_pubkey: address_merkle_tree_pubkey.to_bytes().into(), - address_merkle_tree_root_index: address_params.address_merkle_tree_root_index, - } -} - pub mod v1 { - use light_hasher::{hash_to_field_size::hashv_to_bn254_field_size_be, Hasher, Keccak}; use crate::Pubkey; @@ -75,33 +62,7 @@ pub mod v1 { /// ); /// ``` pub fn derive_address_seed(seeds: &[&[u8]], program_id: &Pubkey) -> [u8; 32] { - let mut inputs = Vec::with_capacity(seeds.len() + 1); - - let program_id = program_id.to_bytes(); - inputs.push(program_id.as_slice()); - - inputs.extend(seeds); - - let seed = hashv_to_bn254_field_size_be_legacy(inputs.as_slice()); - seed - } - - fn hashv_to_bn254_field_size_be_legacy(bytes: &[&[u8]]) -> [u8; 32] { - let mut hashed_value: [u8; 32] = Keccak::hashv(bytes).unwrap(); - // Truncates to 31 bytes so that value is less than bn254 Fr modulo - // field size. - hashed_value[0] = 0; - hashed_value - } - - /// Derives an address for a compressed account, based on the provided singular - /// `seed` and `merkle_tree_pubkey`: - pub(crate) fn derive_address_from_seed( - address_seed: &[u8; 32], - merkle_tree_pubkey: &Pubkey, - ) -> [u8; 32] { - let input = [merkle_tree_pubkey.to_bytes(), *address_seed].concat(); - hashv_to_bn254_field_size_be(&[input.as_slice()]) + light_sdk_types::address::v1::derive_address_seed(seeds, &program_id.to_bytes()) } /// Derives an address from provided seeds. Returns that address and a singular @@ -127,10 +88,11 @@ pub mod v1 { merkle_tree_pubkey: &Pubkey, program_id: &Pubkey, ) -> ([u8; 32], [u8; 32]) { - let address_seed = derive_address_seed(seeds, program_id); - let address = derive_address_from_seed(&address_seed, merkle_tree_pubkey); - - (address, address_seed) + light_sdk_types::address::v1::derive_address( + seeds, + &merkle_tree_pubkey.to_bytes(), + &program_id.to_bytes(), + ) } } @@ -138,7 +100,8 @@ pub mod v1 { mod test { use solana_pubkey::pubkey; - use super::{v1::*, *}; + use super::v1::*; + use crate::instruction::AddressTreeInfo; #[test] fn test_derive_address_seed() { @@ -180,9 +143,6 @@ mod test { let address_seed = derive_address_seed(seeds, &program_id); assert_eq!(address_seed, expected_address_seed); - let address = - derive_address_from_seed(&address_seed, &address_tree_info.address_merkle_tree_pubkey); - assert_eq!(address, expected_address.to_bytes()); let (address, address_seed) = derive_address( seeds, &address_tree_info.address_merkle_tree_pubkey, @@ -200,9 +160,6 @@ mod test { let address_seed = derive_address_seed(seeds, &program_id); assert_eq!(address_seed, expected_address_seed); - let address = - derive_address_from_seed(&address_seed, &address_tree_info.address_merkle_tree_pubkey); - assert_eq!(address, expected_address.to_bytes()); let (address, address_seed) = derive_address( seeds, &address_tree_info.address_merkle_tree_pubkey, diff --git a/sdk-libs/sdk/src/constants.rs b/sdk-libs/sdk/src/constants.rs deleted file mode 100644 index 329ddee860..0000000000 --- a/sdk-libs/sdk/src/constants.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::{pubkey, Pubkey}; - -/// Seed of the CPI authority. -pub const CPI_AUTHORITY_PDA_SEED: &[u8] = b"cpi_authority"; - -/// ID of the account-compression program. -pub const PROGRAM_ID_ACCOUNT_COMPRESSION: Pubkey = - pubkey!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); -pub const PROGRAM_ID_NOOP: Pubkey = pubkey!("noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"); -/// ID of the light-system program. -pub const PROGRAM_ID_LIGHT_SYSTEM: Pubkey = pubkey!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); -/// ID of the light-compressed-token program. -pub const PROGRAM_ID_LIGHT_TOKEN: Pubkey = pubkey!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); - -pub const STATE_MERKLE_TREE_HEIGHT: usize = 26; -pub const STATE_MERKLE_TREE_CHANGELOG: usize = 1400; -pub const STATE_MERKLE_TREE_ROOTS: usize = 2400; -pub const STATE_MERKLE_TREE_CANOPY_DEPTH: usize = 10; - -pub const ADDRESS_MERKLE_TREE_HEIGHT: usize = 26; -pub const ADDRESS_MERKLE_TREE_CHANGELOG: usize = 1400; -pub const ADDRESS_MERKLE_TREE_ROOTS: usize = 2400; -pub const ADDRESS_MERKLE_TREE_CANOPY_DEPTH: usize = 10; -pub const ADDRESS_MERKLE_TREE_INDEXED_CHANGELOG: usize = 1400; - -pub const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [2, 0, 0, 0, 0, 0, 0, 0]; - -pub const ADDRESS_TREE_V1: Pubkey = pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"); -pub const ADDRESS_QUEUE_V1: Pubkey = pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"); diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index d6046c7621..5e0cf037a9 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -1,34 +1,48 @@ +#[cfg(feature = "anchor")] +use anchor_lang::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; +use light_sdk_types::CpiSigner; + use crate::{ error::{LightSdkError, Result}, - AccountInfo, AccountMeta, AnchorDeserialize, AnchorSerialize, Pubkey, + AccountInfo, AccountMeta, Pubkey, }; -#[derive(Debug, Default, Copy, Clone, AnchorSerialize, AnchorDeserialize)] +#[derive(Debug, Copy, Clone, AnchorSerialize, AnchorDeserialize)] pub struct CpiAccountsConfig { - pub self_program: Pubkey, pub cpi_context: bool, pub sol_compression_recipient: bool, pub sol_pool_pda: bool, + pub cpi_signer: CpiSigner, } impl CpiAccountsConfig { - pub fn new(self_program: Pubkey) -> Self { + pub fn new(cpi_signer: CpiSigner) -> Self { Self { - self_program, cpi_context: false, sol_compression_recipient: false, sol_pool_pda: false, + cpi_signer, } } - pub fn new_with_cpi_context(self_program: Pubkey) -> Self { + pub fn new_with_cpi_context(cpi_signer: CpiSigner) -> Self { Self { - self_program, cpi_context: true, sol_compression_recipient: false, sol_pool_pda: false, + cpi_signer, } } + + pub fn cpi_signer(&self) -> [u8; 32] { + self.cpi_signer.cpi_signer + } + + pub fn bump(&self) -> u8 { + self.cpi_signer.bump + } } #[repr(usize)] @@ -59,15 +73,12 @@ impl<'c, 'info> CpiAccounts<'c, 'info> { pub fn new( fee_payer: &'c AccountInfo<'info>, accounts: &'c [AccountInfo<'info>], - program_id: Pubkey, + cpi_signer: CpiSigner, ) -> Result { let new = Self { fee_payer, accounts, - config: CpiAccountsConfig { - self_program: program_id, - ..Default::default() - }, + config: CpiAccountsConfig::new(cpi_signer), }; if accounts.len() < new.system_accounts_len() { crate::msg!("accounts len {}", accounts.len()); @@ -118,8 +129,12 @@ impl<'c, 'info> CpiAccounts<'c, 'info> { .unwrap() } - pub fn self_program_id(&self) -> &Pubkey { - &self.config.self_program + pub fn self_program_id(&self) -> Pubkey { + Pubkey::new_from_array(self.config.cpi_signer.program_id) + } + + pub fn bump(&self) -> u8 { + self.config.cpi_signer.bump } pub fn to_account_infos(&self) -> Vec> { diff --git a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs index edc5d916c5..763de0156c 100644 --- a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs +++ b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs @@ -1,5 +1,9 @@ #![cfg(feature = "v2")] -use crate::{cpi::CpiAccountsConfig, error::Result, msg, AccountInfo, AccountMeta, Pubkey}; +use crate::{ + cpi::{CpiAccountsConfig, CpiSigner}, + error::Result, + msg, AccountInfo, AccountMeta, Pubkey, +}; #[repr(usize)] pub enum CompressionCpiAccountIndexSmall { @@ -30,7 +34,7 @@ impl<'c, 'info> CpiAccountsSmall<'c, 'info> { pub fn new( fee_payer: &'c AccountInfo<'info>, accounts: &'c [AccountInfo<'info>], - program_id: Pubkey, + cpi_signer: CpiSigner, ) -> Result { // if accounts.len() < SYSTEM_ACCOUNTS_LEN { // msg!("accounts len {}", accounts.len()); @@ -39,10 +43,7 @@ impl<'c, 'info> CpiAccountsSmall<'c, 'info> { Ok(Self { fee_payer, accounts, - config: CpiAccountsConfig { - self_program: program_id, - ..Default::default() - }, + config: CpiAccountsConfig::new(cpi_signer), }) } @@ -73,8 +74,8 @@ impl<'c, 'info> CpiAccountsSmall<'c, 'info> { .unwrap() } - pub fn self_program_id(&self) -> &Pubkey { - &self.config.self_program + pub fn self_program_id(&self) -> Pubkey { + Pubkey::new_from_array(self.config.cpi_signer.program_id) } /// Account infos for cpi to light system program. diff --git a/sdk-libs/sdk/src/cpi/invoke.rs b/sdk-libs/sdk/src/cpi/invoke.rs index 8387226caf..30e64fb48e 100644 --- a/sdk-libs/sdk/src/cpi/invoke.rs +++ b/sdk-libs/sdk/src/cpi/invoke.rs @@ -7,13 +7,13 @@ use light_compressed_account::{ with_account_info::CompressedAccountInfo, }, }; +use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM}; use crate::{ - account_info::AccountInfoTrait, cpi::CpiAccounts, error::{LightSdkError, Result}, - find_cpi_signer_macro, invoke_signed, AccountInfo, AccountMeta, AnchorSerialize, Instruction, - Pubkey, ValidityProof, CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM, + instruction::{account_info::AccountInfoTrait, ValidityProof}, + invoke_signed, AccountInfo, AccountMeta, AnchorSerialize, Instruction, }; #[derive(Debug, Default, PartialEq, Clone)] @@ -52,12 +52,10 @@ impl CpiInputs { pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts) -> Result<()> { let instruction = create_light_system_progam_instruction_invoke_cpi(self, &cpi_accounts)?; - - invoke_light_system_program( - cpi_accounts.self_program_id(), - cpi_accounts.to_account_infos().as_slice(), - instruction, - ) + // sol_log_compute_units(); + let account_infos: Vec = cpi_accounts.to_account_infos(); + // sol_log_compute_units(); + invoke_light_system_program(account_infos.as_slice(), instruction, cpi_accounts.bump()) } } @@ -72,10 +70,14 @@ pub fn create_light_system_progam_instruction_invoke_cpi( Vec::with_capacity(account_infos.len()); let mut output_compressed_accounts = Vec::with_capacity(account_infos.len()); for account_info in account_infos.iter() { - if let Some(input_account) = account_info.input_compressed_account(owner)? { + if let Some(input_account) = + account_info.input_compressed_account(owner.to_bytes().into())? + { input_compressed_accounts_with_merkle_context.push(input_account); } - if let Some(output_account) = account_info.output_compressed_account(owner)? { + if let Some(output_account) = + account_info.output_compressed_account(owner.to_bytes().into())? + { output_compressed_accounts.push(output_account); } } @@ -114,7 +116,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( let account_metas: Vec = cpi_accounts.to_account_metas(); Ok(Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM, + program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), accounts: account_metas, data, }) @@ -137,24 +139,23 @@ where let account_metas: Vec = light_system_accounts.to_account_metas(); let instruction = Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM, + program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), accounts: account_metas, data, }; invoke_light_system_program( - light_system_accounts.self_program_id(), account_infos.as_slice(), instruction, + light_system_accounts.bump(), ) } #[inline(always)] pub fn invoke_light_system_program( - invoking_program_id: &Pubkey, account_infos: &[AccountInfo], instruction: Instruction, + bump: u8, ) -> Result<()> { - let (_authority, bump) = find_cpi_signer_macro!(invoking_program_id); let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]]; // TODO: restore but not a priority it is a convenience check diff --git a/sdk-libs/sdk/src/cpi/mod.rs b/sdk-libs/sdk/src/cpi/mod.rs index fb5d23bea7..6c53697e81 100644 --- a/sdk-libs/sdk/src/cpi/mod.rs +++ b/sdk-libs/sdk/src/cpi/mod.rs @@ -1,3 +1,54 @@ +//! +//! +//! To create, update, or close compressed accounts, +//! programs need to invoke the light system program via cross program invocation (cpi). +//! +//! ```ignore +//! declare_id!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); +//! pub const LIGHT_CPI_SIGNER: CpiSigner = +//! derive_light_cpi_signer!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); +//! +//! let light_cpi_accounts = CpiAccounts::new( +//! ctx.accounts.fee_payer.as_ref(), +//! ctx.remaining_accounts, +//! crate::LIGHT_CPI_SIGNER, +//! ) +//! .map_err(ProgramError::from)?; +//! +//! let (address, address_seed) = derive_address( +//! &[b"compressed", name.as_bytes()], +//! &light_cpi_accounts.tree_accounts() +//! [address_tree_info.address_merkle_tree_pubkey_index as usize] +//! .key(), +//! &crate::ID, +//! ); +//! let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); +//! +//! let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_init( +//! &crate::ID, +//! Some(address), +//! output_tree_index, +//! ); +//! +//! my_compressed_account.name = name; +//! my_compressed_account.nested = NestedData::default(); +//! +//! let cpi_inputs = CpiInputs::new_with_address( +//! proof, +//! // add compressed accounts to create, update or close here +//! vec![my_compressed_account +//! .to_account_info() +//! .map_err(ProgramError::from)?], +//! // add new addresses here +//! // (existing addresses are part of the account info and must not be added here) +//! vec![new_address_params], +//! ); +//! +//! cpi_inputs +//! .invoke_light_system_program(light_cpi_accounts) +//! .map_err(ProgramError::from)?; +//! ``` + mod accounts; mod accounts_small_ix; mod invoke; @@ -6,3 +57,7 @@ pub use accounts::*; #[cfg(feature = "v2")] pub use accounts_small_ix::*; pub use invoke::*; +/// Derives cpi signer and bump to invoke the light system program at compile time. +pub use light_sdk_macros::derive_light_cpi_signer; +/// Contains program id, derived cpi signer, and bump, +pub use light_sdk_types::CpiSigner; diff --git a/sdk-libs/sdk/src/error.rs b/sdk-libs/sdk/src/error.rs index 68806ca875..ff53ca93fe 100644 --- a/sdk-libs/sdk/src/error.rs +++ b/sdk-libs/sdk/src/error.rs @@ -1,4 +1,6 @@ use light_hasher::HasherError; +use light_sdk_types::error::LightSdkTypesError; +use light_zero_copy::errors::ZeroCopyError; use thiserror::Error; use crate::ProgramError; @@ -47,12 +49,59 @@ pub enum LightSdkError { MissingField(String), #[error("Output state tree index is none. Use an CompressedAccountMeta type with output tree index to initialize or update accounts.")] OutputStateTreeIndexIsNone, + #[error("Address is none during initialization")] + InitAddressIsNone, + #[error("Address is none during initialization with address")] + InitWithAddressIsNone, + #[error("Output is none during initialization with address")] + InitWithAddressOutputIsNone, + #[error("Address is none during meta mutation")] + MetaMutAddressIsNone, + #[error("Input is none during meta mutation")] + MetaMutInputIsNone, + #[error("Output lamports is none during meta mutation")] + MetaMutOutputLamportsIsNone, + #[error("Output is none during meta mutation")] + MetaMutOutputIsNone, + #[error("Address is none during meta close")] + MetaCloseAddressIsNone, + #[error("Input is none during meta close")] + MetaCloseInputIsNone, #[error(transparent)] Hasher(#[from] HasherError), + #[error(transparent)] + ZeroCopy(#[from] ZeroCopyError), #[error("Program error: {0}")] ProgramError(#[from] ProgramError), } +impl From for ProgramError { + fn from(e: LightSdkError) -> Self { + ProgramError::Custom(e.into()) + } +} + +impl From for LightSdkError { + fn from(e: LightSdkTypesError) -> Self { + match e { + LightSdkTypesError::InitAddressIsNone => LightSdkError::InitAddressIsNone, + LightSdkTypesError::InitWithAddressIsNone => LightSdkError::InitWithAddressIsNone, + LightSdkTypesError::InitWithAddressOutputIsNone => { + LightSdkError::InitWithAddressOutputIsNone + } + LightSdkTypesError::MetaMutAddressIsNone => LightSdkError::MetaMutAddressIsNone, + LightSdkTypesError::MetaMutInputIsNone => LightSdkError::MetaMutInputIsNone, + LightSdkTypesError::MetaMutOutputLamportsIsNone => { + LightSdkError::MetaMutOutputLamportsIsNone + } + LightSdkTypesError::MetaMutOutputIsNone => LightSdkError::MetaMutOutputIsNone, + LightSdkTypesError::MetaCloseAddressIsNone => LightSdkError::MetaCloseAddressIsNone, + LightSdkTypesError::MetaCloseInputIsNone => LightSdkError::MetaCloseInputIsNone, + LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), + } + } +} + impl From for u32 { fn from(e: LightSdkError) -> Self { match e { @@ -76,14 +125,18 @@ impl From for u32 { LightSdkError::InvalidCpiSignerAccount => 14018, LightSdkError::MissingField(_) => 14019, LightSdkError::OutputStateTreeIndexIsNone => 14020, + LightSdkError::InitAddressIsNone => 14021, + LightSdkError::InitWithAddressIsNone => 14022, + LightSdkError::InitWithAddressOutputIsNone => 14023, + LightSdkError::MetaMutAddressIsNone => 14024, + LightSdkError::MetaMutInputIsNone => 14025, + LightSdkError::MetaMutOutputLamportsIsNone => 14026, + LightSdkError::MetaMutOutputIsNone => 14027, + LightSdkError::MetaCloseAddressIsNone => 14028, + LightSdkError::MetaCloseInputIsNone => 14029, LightSdkError::Hasher(e) => e.into(), - LightSdkError::ProgramError(e) => u32::try_from(u64::from(e)).unwrap(), + LightSdkError::ZeroCopy(e) => e.into(), + LightSdkError::ProgramError(e) => u64::from(e) as u32, } } } - -impl From for ProgramError { - fn from(e: LightSdkError) -> Self { - ProgramError::Custom(e.into()) - } -} diff --git a/sdk-libs/sdk/src/instruction/mod.rs b/sdk-libs/sdk/src/instruction/mod.rs index b2faa2566f..49cd82bd60 100644 --- a/sdk-libs/sdk/src/instruction/mod.rs +++ b/sdk-libs/sdk/src/instruction/mod.rs @@ -1,4 +1,184 @@ -pub mod account_meta; -pub mod accounts; -pub mod pack_accounts; -pub mod tree_info; +//! # Instruction data with AccountMeta (in Solana program) +//! ### Example instruction data to create new compressed account with address: +//! ```ignore +//! #[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +//! pub struct CreatePdaInstructionData { +//! /// Prove validity of the new address. +//! pub proof: ValidityProof, +//! pub address_tree_info: PackedAddressTreeInfo, +//! /// Index of Merkle tree in the account array (remaining accounts in anchor). +//! pub output_merkle_tree_index: u8, +//! /// Arbitrary data of the new account. +//! pub data: [u8; 31], +//! /// Account offsets for convenience (can be hardcoded). +//! pub system_accounts_offset: u8, +//! pub tree_accounts_offset: u8, +//! } +//! ``` +//! +//! +//! ### Example instruction data to update a compressed account: +//! ```ignore +//! #[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +//!pub struct UpdatePdaInstructionData { +//! /// Prove validity of the existing compressed account state. +//! pub proof: ValidityProof, +//! /// Data and metadata of the compressed account. +//! pub my_compressed_account: UpdateMyCompressedAccount, +//! /// Arbitrary new data the compressed account will be updated with. +//! pub new_data: [u8; 31], +//! /// Account offsets for convenience (can be hardcoded). +//! pub system_accounts_offset: u8, +//! } +//! +//! #[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)] +//! pub struct UpdateMyCompressedAccount { +//! /// Metadata of the compressed account. +//! pub meta: CompressedAccountMeta, +//! /// Data of the compressed account. +//! pub data: [u8; 31], +//! } +//! ``` +//! ### Example anchor instruction data to create new compressed account with address: +//! ```ignore +//! pub fn create_compressed_account<'info>( +//! ctx: Context<'_, '_, '_, 'info, WithNestedData<'info>>, +//! /// Prove validity of the new address. +//! proof: ValidityProof, +//! address_tree_info: PackedAddressTreeInfo, +//! /// Index of Merkle tree in remaining accounts. +//! output_tree_index: u8, +//! /// Arbitrary data of the new account. +//! name: String, +//! ) -> Result<()>; +//! ``` +//! ### Example anchor instruction data to update a compressed account: +//! ```ignore +//! pub fn update_compressed_account<'info>( +//! ctx: Context<'_, '_, '_, 'info, UpdateNestedData<'info>>, +//! /// Prove validity of the existing compressed account state. +//! proof: ValidityProof, +//! /// Data of the compressed account. +//! my_compressed_account: MyCompressedAccount, +//! /// Metadata of the compressed account. +//! account_meta: CompressedAccountMeta, +//! /// Arbitrary new data the compressed account will be updated with. +//! nested_data: NestedData, +//! ) -> Result<()>; +//! ``` +//! ### Example instruction data to update a compressed account: +//! # Create instruction with packed accounts (in client) +//! +//! ### Create instruction to create 1 compressed account and address +//! ```ignore +//! let config = +//! ProgramTestConfig::new_v2(true, Some(vec![("sdk_anchor_test", sdk_anchor_test::ID)])); +//! let mut rpc = LightProgramTest::new(config).await.unwrap(); +//! let name = "test"; +//! let address_tree_info = rpc.get_address_tree_v1(); +//! let (address, _) = derive_address( +//! &[b"compressed", name.as_bytes()], +//! &address_tree_info.tree, +//! &sdk_anchor_test::ID, +//! ); +//! let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); +//! let mut remaining_accounts = PackedAccounts::default(); +//! remaining_accounts.add_system_accounts(config); +//! +//! let address_merkle_tree_info = rpc.get_address_tree_v1(); +//! +//! let rpc_result = rpc +//! .get_validity_proof( +//! vec![], +//! vec![AddressWithTree { +//! address: *address, +//! tree: address_merkle_tree_info.tree, +//! }], +//! None, +//! ) +//! .await? +//! .value; +//! let packed_accounts = rpc_result.pack_tree_infos(&mut remaining_accounts); +//! +//! let output_tree_index = rpc +//! .get_random_state_tree_info() +//! .pack_output_tree_index(&mut remaining_accounts) +//! .unwrap(); +//! +//! let (remaining_accounts, _, _) = remaining_accounts.to_account_metas(); +//! +//! let instruction_data = sdk_anchor_test::instruction::WithNestedData { +//! proof: rpc_result.proof, +//! address_tree_info: packed_accounts.address_trees[0], +//! name, +//! output_tree_index, +//! }; +//! +//! let accounts = sdk_anchor_test::accounts::WithNestedData { +//! signer: payer.pubkey(), +//! }; +//! +//! let instruction = Instruction { +//! program_id: sdk_anchor_test::ID, +//! accounts: [accounts.to_account_metas(Some(true)), remaining_accounts].concat(), +//! data: instruction_data.data(), +//! }; +//! ``` +//! +//! ### Create instruction to create 1 compressed account and address (anchor) +//! ```ignore +//! let mut remaining_accounts = PackedAccounts::default(); +//! +//! let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); +//! remaining_accounts.add_system_accounts(config); +//! let hash = compressed_account.hash; +//! +//! let rpc_result = rpc +//! .get_validity_proof(vec![hash], vec![], None) +//! .await? +//! .value; +//! +//! let packed_tree_accounts = rpc_result +//! .pack_tree_infos(&mut remaining_accounts) +//! .state_trees +//! .unwrap(); +//! +//! let (remaining_accounts, _, _) = remaining_accounts.to_account_metas(); +//! +//! let my_compressed_account = MyCompressedAccount::deserialize( +//! &mut compressed_account.data.as_mut().unwrap().data.as_slice(), +//! ) +//! .unwrap(); +//! let instruction_data = sdk_anchor_test::instruction::UpdateNestedData { +//! proof: rpc_result.proof, +//! my_compressed_account, +//! account_meta: CompressedAccountMeta { +//! tree_info: packed_tree_accounts.packed_tree_infos[0], +//! address: compressed_account.address.unwrap(), +//! output_state_tree_index: packed_tree_accounts.output_tree_index, +//! }, +//! nested_data, +//! }; +//! +//! let accounts = sdk_anchor_test::accounts::UpdateNestedData { +//! signer: payer.pubkey(), +//! }; +//! +//! let instruction = Instruction { +//! program_id: sdk_anchor_test::ID, +//! accounts: [accounts.to_account_metas(Some(true)), remaining_accounts].concat(), +//! data: instruction_data.data(), +//! }; +//! ``` +// TODO: link to examples + +mod pack_accounts; +mod system_accounts; +mod tree_info; + +/// Zero-knowledge proof to prove the validity of existing compressed accounts and new addresses. +pub use light_compressed_account::instruction_data::compressed_proof::ValidityProof; +pub use light_sdk_types::instruction::*; +pub use pack_accounts::*; +pub use system_accounts::*; +pub use tree_info::*; diff --git a/sdk-libs/sdk/src/instruction/pack_accounts.rs b/sdk-libs/sdk/src/instruction/pack_accounts.rs index ea5a951ddf..830ebe98a1 100644 --- a/sdk-libs/sdk/src/instruction/pack_accounts.rs +++ b/sdk-libs/sdk/src/instruction/pack_accounts.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - instruction::accounts::{get_light_system_account_metas, SystemAccountMetaConfig}, + instruction::system_accounts::{get_light_system_account_metas, SystemAccountMetaConfig}, AccountMeta, Pubkey, }; @@ -102,7 +102,7 @@ impl PackedAccounts { } /// Converts the collection of accounts to a vector of - /// [`AccountMeta`](solana_sdk::instruction::AccountMeta), which can be used + /// [`AccountMeta`](solana_instruction::AccountMeta), which can be used /// as remaining accounts in instructions or CPI calls. pub fn to_account_metas(&self) -> (Vec, usize, usize) { let packed_accounts = self.hash_set_accounts_to_metas(); diff --git a/sdk-libs/sdk/src/instruction/accounts.rs b/sdk-libs/sdk/src/instruction/system_accounts.rs similarity index 88% rename from sdk-libs/sdk/src/instruction/accounts.rs rename to sdk-libs/sdk/src/instruction/system_accounts.rs index 3ff9f58418..2227da20b7 100644 --- a/sdk-libs/sdk/src/instruction/accounts.rs +++ b/sdk-libs/sdk/src/instruction/system_accounts.rs @@ -1,8 +1,10 @@ -use crate::{ - find_cpi_signer_macro, AccountMeta, Pubkey, CPI_AUTHORITY_PDA_SEED, - PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, PROGRAM_ID_NOOP, +use light_sdk_types::constants::{ + CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, + PROGRAM_ID_NOOP, }; +use crate::{find_cpi_signer_macro, AccountMeta, Pubkey}; + #[derive(Debug, Default, Copy, Clone)] pub struct SystemAccountMetaConfig { pub self_program: Pubkey, @@ -45,20 +47,20 @@ pub struct SystemAccountPubkeys { impl Default for SystemAccountPubkeys { fn default() -> Self { Self { - light_sytem_program: PROGRAM_ID_LIGHT_SYSTEM, + light_sytem_program: Pubkey::from(PROGRAM_ID_LIGHT_SYSTEM), system_program: Pubkey::default(), - account_compression_program: PROGRAM_ID_ACCOUNT_COMPRESSION, + account_compression_program: Pubkey::from(PROGRAM_ID_ACCOUNT_COMPRESSION), account_compression_authority: Pubkey::find_program_address( &[CPI_AUTHORITY_PDA_SEED], - &PROGRAM_ID_LIGHT_SYSTEM, + &Pubkey::from(PROGRAM_ID_LIGHT_SYSTEM), ) .0, registered_program_pda: Pubkey::find_program_address( - &[PROGRAM_ID_LIGHT_SYSTEM.to_bytes().as_slice()], - &PROGRAM_ID_ACCOUNT_COMPRESSION, + &[PROGRAM_ID_LIGHT_SYSTEM.as_slice()], + &Pubkey::from(PROGRAM_ID_ACCOUNT_COMPRESSION), ) .0, - noop_program: PROGRAM_ID_NOOP, + noop_program: Pubkey::from(PROGRAM_ID_NOOP), // TODO: add correct pubkey sol_pool_pda: Pubkey::default(), } diff --git a/sdk-libs/sdk/src/instruction/tree_info.rs b/sdk-libs/sdk/src/instruction/tree_info.rs index 59fbbb378d..eb0a547983 100644 --- a/sdk-libs/sdk/src/instruction/tree_info.rs +++ b/sdk-libs/sdk/src/instruction/tree_info.rs @@ -1,42 +1,15 @@ pub use light_compressed_account::compressed_account::{MerkleContext, PackedMerkleContext}; -use light_compressed_account::instruction_data::data::NewAddressParamsPacked; +use light_sdk_types::instruction::PackedAddressTreeInfo; -use super::pack_accounts::PackedAccounts; +use super::PackedAccounts; use crate::{AccountInfo, AnchorDeserialize, AnchorSerialize, Pubkey}; -#[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] -pub struct PackedStateTreeInfo { - pub root_index: u16, - pub prove_by_index: bool, - pub merkle_tree_pubkey_index: u8, - pub queue_pubkey_index: u8, - pub leaf_index: u32, -} - #[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] pub struct AddressTreeInfo { pub address_merkle_tree_pubkey: Pubkey, pub address_queue_pubkey: Pubkey, } -#[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] -pub struct PackedAddressTreeInfo { - pub address_merkle_tree_pubkey_index: u8, - pub address_queue_pubkey_index: u8, - pub root_index: u16, -} - -impl PackedAddressTreeInfo { - pub fn into_new_address_params_packed(self, seed: [u8; 32]) -> NewAddressParamsPacked { - NewAddressParamsPacked { - address_merkle_tree_account_index: self.address_merkle_tree_pubkey_index, - address_queue_account_index: self.address_queue_pubkey_index, - address_merkle_tree_root_index: self.root_index, - seed, - } - } -} - #[deprecated(since = "0.13.0", note = "please use PackedStateTreeInfo")] pub fn pack_merkle_contexts<'a, I>( merkle_contexts: I, diff --git a/sdk-libs/sdk/src/lib.rs b/sdk-libs/sdk/src/lib.rs index bdf746d760..d21cacc16d 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -1,13 +1,120 @@ +//! +//! # Core Functionality +//! 1. Instruction +//! - `AccountMeta` - Compressed account metadata structs for instruction data. +//! - `PackedAccounts` - Abstraction to prepare accounts offchain for instructions with compressed accounts. +//! 2. Program logic +//! - `LightAccount` - Compressed account abstraction similar to anchor Account. +//! - `derive_address` - Create a compressed account address. +//! - `LightHasher` - DeriveMacro to derive a hashing scheme from a struct layout. +//! - `LightDiscriminator` - DeriveMacro to derive a compressed account discriminator. +//! 3. Cpi +//! - `CpiAccounts` - Prepare accounts to cpi the light system program. +//! - `CpiInputs` - Prepare instruction data to cpi the light system program. +//! - `invoke_light_system_program` - Invoke the light system program via cpi. +//! +//! +//! # Features +//! 1. `anchor` - Derives AnchorSerialize, AnchorDeserialize instead of BorshSerialize, BorshDeserialize. +//! +//! 2. `v2` - light protocol program v2 are currently in audit and only available on local host and with light-program-test. +//! Deploy on devnet and mainnet only without v2 features enabled. +//! +//! ### Example Solana program code to create a compressed account +//! ```ignore +//! use anchor_lang::{prelude::*, Discriminator}; +//! use light_sdk::{ +//! account::LightAccount, +//! address::v1::derive_address, +//! cpi::{CpiAccounts, CpiInputs}, +//! derive_light_cpi_signer, +//! instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo}, +//! CpiSigner, LightDiscriminator, LightHasher, ValidityProof, +//! }; +//! +//! declare_id!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); +//! +//! pub const LIGHT_CPI_SIGNER: CpiSigner = +//! derive_light_cpi_signer!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt"); +//! +//! #[program] +//! pub mod counter { +//! +//! use super::*; +//! +//! pub fn create_compressed_account<'info>( +//! ctx: Context<'_, '_, '_, 'info, CreateCompressedAccount<'info>>, +//! proof: ValidityProof, +//! address_tree_info: PackedAddressTreeInfo, +//! output_tree_index: u8, +//! ) -> Result<()> { +//! let light_cpi_accounts = CpiAccounts::new( +//! ctx.accounts.fee_payer.as_ref(), +//! ctx.remaining_accounts, +//! crate::LIGHT_CPI_SIGNER, +//! ) +//! .map_err(ProgramError::from)?; +//! +//! let (address, address_seed) = derive_address( +//! &[b"counter", ctx.accounts.fee_payer.key().as_ref()], +//! &light_cpi_accounts.tree_accounts() +//! [address_tree_info.address_merkle_tree_pubkey_index as usize] +//! .key(), +//! &crate::ID, +//! ); +//! let new_address_params = address_tree_info +//! .into_new_address_params_packed(address_seed); +//! +//! let mut my_compressed_account = LightAccount::<'_, CounterAccount>::new_init( +//! &crate::ID, +//! Some(address), +//! output_tree_index, +//! ); +//! +//! my_compressed_account.owner = ctx.accounts.fee_payer.key(); +//! +//! let cpi_inputs = CpiInputs::new_with_address( +//! proof, +//! vec![my_compressed_account +//! .to_account_info() +//! .map_err(ProgramError::from)?], +//! vec![new_address_params], +//! ); +//! +//! cpi_inputs +//! .invoke_light_system_program(light_cpi_accounts) +//! .map_err(ProgramError::from)?; +//! +//! Ok(()) +//! } +//! } +//! +//! #[derive(Accounts)] +//! pub struct CreateCompressedAccount<'info> { +//! #[account(mut)] +//! pub fee_payer: Signer<'info>, +//! } +//! +//! #[derive(Clone, Debug, Default, LightHasher, LightDiscriminator)] +//!pub struct CounterAccount { +//! #[hash] +//! pub owner: Pubkey, +//! pub counter: u64 +//!} +//! ``` + +/// Compressed account abstraction similar to anchor Account. pub mod account; -pub mod account_info; +/// Functions to derive compressed account addresses. pub mod address; -pub mod constants; -pub use constants::*; +/// Utilities to invoke the light-system-program via cpi. pub mod cpi; pub mod error; +/// Utilities to build instructions for programs with compressed accounts. pub mod instruction; pub mod legacy; pub mod token; +/// Transfer compressed sol between compressed accounts. pub mod transfer; pub mod utils; @@ -15,53 +122,15 @@ pub mod utils; use anchor_lang::{AnchorDeserialize, AnchorSerialize}; #[cfg(not(feature = "anchor"))] use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; -pub use light_account_checks::{discriminator::Discriminator as LightDiscriminator, *}; -use light_compressed_account::instruction_data::compressed_proof::CompressedProof; -pub use light_compressed_account::{self, instruction_data::data::*}; -pub use light_hasher::*; -pub use light_sdk_macros::*; +pub use light_account_checks::{self, discriminator::Discriminator as LightDiscriminator}; +pub use light_hasher; +pub use light_sdk_macros::{ + derive_light_cpi_signer, light_system_accounts, LightDiscriminator, LightHasher, LightTraits, +}; +pub use light_sdk_types::constants; use solana_account_info::AccountInfo; use solana_cpi::invoke_signed; use solana_instruction::{AccountMeta, Instruction}; use solana_msg::msg; use solana_program_error::ProgramError; -use solana_pubkey::{pubkey, Pubkey}; - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)] -pub struct ValidityProof(pub Option); - -impl ValidityProof { - pub fn new(proof: Option) -> Self { - Self(proof) - } -} - -impl From for ValidityProof { - fn from(proof: CompressedProof) -> Self { - Self(Some(proof)) - } -} - -impl From> for ValidityProof { - fn from(proof: Option) -> Self { - Self(proof) - } -} -impl From<&CompressedProof> for ValidityProof { - fn from(proof: &CompressedProof) -> Self { - Self(Some(*proof)) - } -} - -impl From<&Option> for ValidityProof { - fn from(proof: &Option) -> Self { - Self(*proof) - } -} - -#[allow(clippy::from_over_into)] -impl Into> for ValidityProof { - fn into(self) -> Option { - self.0 - } -} +use solana_pubkey::Pubkey; diff --git a/sdk-libs/sdk/src/utils.rs b/sdk-libs/sdk/src/utils.rs index 62936ab5f1..70dea91527 100644 --- a/sdk-libs/sdk/src/utils.rs +++ b/sdk-libs/sdk/src/utils.rs @@ -1,21 +1,5 @@ -use crate::{Pubkey, CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_ACCOUNT_COMPRESSION}; - -pub fn get_registered_program_pda(program_id: &Pubkey) -> Pubkey { - Pubkey::find_program_address( - &[program_id.to_bytes().as_slice()], - &PROGRAM_ID_ACCOUNT_COMPRESSION, - ) - .0 -} - -pub fn find_cpi_signer(program_id: &Pubkey) -> Pubkey { - Pubkey::find_program_address([CPI_AUTHORITY_PDA_SEED].as_slice(), program_id).0 -} - -pub fn get_cpi_authority_pda(program_id: &Pubkey) -> Pubkey { - Pubkey::find_program_address(&[CPI_AUTHORITY_PDA_SEED], program_id).0 -} - +#[allow(unused_imports)] +use crate::constants::CPI_AUTHORITY_PDA_SEED; #[macro_export] macro_rules! find_cpi_signer_macro { ($program_id:expr) => { From badb2aa38f197e3edc570ed553db9c268344bde4 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 07:26:25 +0100 Subject: [PATCH 02/21] refactor: unify CpiAccounts in sdk-types --- Cargo.lock | 2 + examples/anchor/counter/src/lib.rs | 15 +- .../src/escrow_with_compressed_pda/escrow.rs | 5 +- .../escrow_with_compressed_pda/withdrawal.rs | 5 +- program-libs/account-checks/Cargo.toml | 2 + .../src/account_info/account_info_trait.rs | 6 + .../src/account_info/pinocchio.rs | 9 + .../account-checks/src/account_info/solana.rs | 21 +- .../create-address-test-program/src/lib.rs | 18 +- .../programs/sdk-anchor-test/src/lib.rs | 6 +- .../sdk-pinocchio-test/src/create_pda.rs | 2 +- .../sdk-pinocchio-test/src/update_pda.rs | 2 +- program-tests/sdk-test/src/create_pda.rs | 2 +- program-tests/sdk-test/src/update_pda.rs | 2 +- sdk-libs/sdk-pinocchio/Cargo.toml | 2 +- sdk-libs/sdk-pinocchio/src/cpi/accounts.rs | 308 +++++----------- sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 5 +- sdk-libs/sdk-pinocchio/src/error.rs | 3 + sdk-libs/sdk-types/Cargo.toml | 1 + sdk-libs/sdk-types/src/cpi_accounts.rs | 153 ++++++++ sdk-libs/sdk-types/src/error.rs | 3 + sdk-libs/sdk-types/src/lib.rs | 2 + sdk-libs/sdk/Cargo.toml | 2 +- sdk-libs/sdk/src/cpi/accounts.rs | 335 +++++------------- sdk-libs/sdk/src/cpi/accounts_small_ix.rs | 4 +- sdk-libs/sdk/src/cpi/invoke.rs | 30 +- sdk-libs/sdk/src/error.rs | 3 + sdk-libs/sdk/src/lib.rs | 1 - 28 files changed, 415 insertions(+), 534 deletions(-) create mode 100644 sdk-libs/sdk-types/src/cpi_accounts.rs diff --git a/Cargo.lock b/Cargo.lock index 2f12161641..bf50bd7e28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3240,6 +3240,7 @@ dependencies = [ "pinocchio", "rand 0.8.5", "solana-account-info", + "solana-instruction", "solana-program-error", "solana-pubkey", "solana-sysvar", @@ -3685,6 +3686,7 @@ version = "0.9.1" dependencies = [ "anchor-lang", "borsh 0.10.4", + "light-account-checks", "light-compressed-account", "light-hasher", "light-macros", diff --git a/examples/anchor/counter/src/lib.rs b/examples/anchor/counter/src/lib.rs index b38bc2ad1e..dd81f5b14a 100644 --- a/examples/anchor/counter/src/lib.rs +++ b/examples/anchor/counter/src/lib.rs @@ -38,8 +38,7 @@ pub mod counter { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let (address, address_seed) = derive_address( &[b"counter", ctx.accounts.signer.key().as_ref()], @@ -99,8 +98,7 @@ pub mod counter { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let cpi_inputs = CpiInputs::new( proof, @@ -138,8 +136,7 @@ pub mod counter { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let cpi_inputs = CpiInputs::new( proof, @@ -175,8 +172,7 @@ pub mod counter { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let cpi_inputs = CpiInputs::new( proof, vec![counter.to_account_info().map_err(ProgramError::from)?], @@ -212,8 +208,7 @@ pub mod counter { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let cpi_inputs = CpiInputs::new( proof, diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs index 66f2ab65e7..dfcf6a3787 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs @@ -137,10 +137,9 @@ fn cpi_compressed_pda_transfer<'info>( ctx.accounts.signer.as_ref(), &system_accounts, CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), - ) - .unwrap(); + ); - verify_borsh(&light_accounts, &inputs_struct).map_err(ProgramError::from)?; + verify_borsh(light_accounts, &inputs_struct).map_err(ProgramError::from)?; Ok(()) } diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs index 65d37fe0b8..0ffdb10b1a 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs @@ -161,9 +161,8 @@ fn cpi_compressed_pda_withdrawal<'info>( ctx.accounts.signer.as_ref(), &system_accounts, CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), - ) - .unwrap(); - verify_borsh(&light_accounts, &inputs_struct).unwrap(); + ); + verify_borsh(light_accounts, &inputs_struct).unwrap(); Ok(()) } diff --git a/program-libs/account-checks/Cargo.toml b/program-libs/account-checks/Cargo.toml index fa95073710..6d0933e266 100644 --- a/program-libs/account-checks/Cargo.toml +++ b/program-libs/account-checks/Cargo.toml @@ -13,6 +13,7 @@ solana = [ "solana-sysvar", "solana-account-info", "solana-pubkey", + "solana-instruction", ] pinocchio = ["dep:pinocchio"] test-only = ["dep:rand"] @@ -25,6 +26,7 @@ solana-pubkey = { workspace = true, optional = true, features = [ "curve25519", "sha2", ] } +solana-instruction = { workspace = true, optional = true } pinocchio = { workspace = true, optional = true } thiserror = { workspace = true } rand = { workspace = true, optional = true } diff --git a/program-libs/account-checks/src/account_info/account_info_trait.rs b/program-libs/account-checks/src/account_info/account_info_trait.rs index c39bd32ed9..03b5cf4729 100644 --- a/program-libs/account-checks/src/account_info/account_info_trait.rs +++ b/program-libs/account-checks/src/account_info/account_info_trait.rs @@ -4,6 +4,7 @@ use crate::error::AccountError; /// Trait to abstract over different AccountInfo implementations (pinocchio vs solana) pub trait AccountInfoTrait { + type Pubkey: Copy + Clone; type DataRef<'a>: Deref where Self: 'a; @@ -13,6 +14,8 @@ pub trait AccountInfoTrait { /// Return raw byte array for maximum compatibility fn key(&self) -> [u8; 32]; + /// Return the pubkey in the native format + fn pubkey(&self) -> Self::Pubkey; fn is_writable(&self) -> bool; fn is_signer(&self) -> bool; fn executable(&self) -> bool; @@ -26,6 +29,9 @@ pub trait AccountInfoTrait { /// Check ownership safely - each implementation handles this without exposing owner fn is_owned_by(&self, program: &[u8; 32]) -> bool; + /// Convert byte array to native Pubkey type + fn pubkey_from_bytes(bytes: [u8; 32]) -> Self::Pubkey; + /// PDA functions - each implementation uses its own backend fn find_program_address(seeds: &[&[u8]], program_id: &[u8; 32]) -> ([u8; 32], u8); fn create_program_address( diff --git a/program-libs/account-checks/src/account_info/pinocchio.rs b/program-libs/account-checks/src/account_info/pinocchio.rs index 190e03c77d..2b4f6ff2a9 100644 --- a/program-libs/account-checks/src/account_info/pinocchio.rs +++ b/program-libs/account-checks/src/account_info/pinocchio.rs @@ -3,6 +3,7 @@ use crate::error::AccountError; /// Implement trait for pinocchio AccountInfo impl AccountInfoTrait for pinocchio::account_info::AccountInfo { + type Pubkey = [u8; 32]; type DataRef<'a> = pinocchio::account_info::Ref<'a, [u8]>; type DataRefMut<'a> = pinocchio::account_info::RefMut<'a, [u8]>; @@ -10,6 +11,14 @@ impl AccountInfoTrait for pinocchio::account_info::AccountInfo { *self.key() } + fn pubkey(&self) -> Self::Pubkey { + *self.key() + } + + fn pubkey_from_bytes(bytes: [u8; 32]) -> Self::Pubkey { + bytes + } + fn is_writable(&self) -> bool { self.is_writable() } diff --git a/program-libs/account-checks/src/account_info/solana.rs b/program-libs/account-checks/src/account_info/solana.rs index 7c68b80dfe..14a5507d51 100644 --- a/program-libs/account-checks/src/account_info/solana.rs +++ b/program-libs/account-checks/src/account_info/solana.rs @@ -3,19 +3,28 @@ use crate::error::AccountError; /// Implement trait for solana AccountInfo impl AccountInfoTrait for solana_account_info::AccountInfo<'_> { - type DataRef<'a> - = std::cell::Ref<'a, [u8]> + type Pubkey = solana_pubkey::Pubkey; + type DataRef<'b> + = std::cell::Ref<'b, [u8]> where - Self: 'a; - type DataRefMut<'a> - = std::cell::RefMut<'a, [u8]> + Self: 'b; + type DataRefMut<'b> + = std::cell::RefMut<'b, [u8]> where - Self: 'a; + Self: 'b; fn key(&self) -> [u8; 32] { self.key.to_bytes() } + fn pubkey(&self) -> Self::Pubkey { + *self.key + } + + fn pubkey_from_bytes(bytes: [u8; 32]) -> Self::Pubkey { + solana_pubkey::Pubkey::from(bytes) + } + fn is_writable(&self) -> bool { self.is_writable } diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index 04f322cedb..d014a4eb00 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -23,7 +23,10 @@ pub const LIGHT_CPI_SIGNER: CpiSigner = #[program] pub mod system_cpi_test { - use light_sdk::{constants::PROGRAM_ID_LIGHT_SYSTEM, cpi::invoke_light_system_program}; + use light_sdk::{ + constants::PROGRAM_ID_LIGHT_SYSTEM, + cpi::{invoke_light_system_program, to_account_metas}, + }; use super::*; @@ -69,11 +72,14 @@ pub mod system_cpi_test { } else { use light_sdk::cpi::CpiAccounts; let cpi_accounts = - CpiAccounts::new_with_config(&fee_payer, ctx.remaining_accounts, config) - .map_err(ProgramError::from)?; - let account_infos = cpi_accounts.to_account_infos(); - - let account_metas = cpi_accounts.to_account_metas(); + CpiAccounts::new_with_config(&fee_payer, ctx.remaining_accounts, config); + let account_infos = cpi_accounts + .to_account_infos() + .into_iter() + .cloned() + .collect::>(); + + let account_metas = to_account_metas(cpi_accounts); (account_infos, account_metas) }; let instruction = Instruction { diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs index 26d67ee252..aec943b09a 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs @@ -31,8 +31,7 @@ pub mod sdk_anchor_test { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let (address, address_seed) = derive_address( &[b"compressed", name.as_bytes()], @@ -87,8 +86,7 @@ pub mod sdk_anchor_test { ctx.accounts.signer.as_ref(), ctx.remaining_accounts, crate::LIGHT_CPI_SIGNER, - ) - .map_err(ProgramError::from)?; + ); let cpi_inputs = CpiInputs::new( proof, diff --git a/program-tests/sdk-pinocchio-test/src/create_pda.rs b/program-tests/sdk-pinocchio-test/src/create_pda.rs index d15fdaf3eb..0948e69f32 100644 --- a/program-tests/sdk-pinocchio-test/src/create_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/create_pda.rs @@ -25,7 +25,7 @@ pub fn create_pda( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - )?; + ); let address_tree_info = instruction_data.address_tree_info; let (address, address_seed) = if BATCHED { diff --git a/program-tests/sdk-pinocchio-test/src/update_pda.rs b/program-tests/sdk-pinocchio-test/src/update_pda.rs index 2207984588..5c18ac74f1 100644 --- a/program-tests/sdk-pinocchio-test/src/update_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/update_pda.rs @@ -41,7 +41,7 @@ pub fn update_pda( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - )?; + ); sol_log_compute_units(); let cpi_inputs = CpiInputs::new( instruction_data.proof, diff --git a/program-tests/sdk-test/src/create_pda.rs b/program-tests/sdk-test/src/create_pda.rs index a728bc3a0c..749cb88572 100644 --- a/program-tests/sdk-test/src/create_pda.rs +++ b/program-tests/sdk-test/src/create_pda.rs @@ -25,7 +25,7 @@ pub fn create_pda( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - )?; + ); let address_tree_info = instruction_data.address_tree_info; let (address, address_seed) = if BATCHED { diff --git a/program-tests/sdk-test/src/update_pda.rs b/program-tests/sdk-test/src/update_pda.rs index 5d96875783..b946e3baaa 100644 --- a/program-tests/sdk-test/src/update_pda.rs +++ b/program-tests/sdk-test/src/update_pda.rs @@ -39,7 +39,7 @@ pub fn update_pda( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - )?; + ); sol_log_compute_units(); let cpi_inputs = CpiInputs::new( instruction_data.proof, diff --git a/sdk-libs/sdk-pinocchio/Cargo.toml b/sdk-libs/sdk-pinocchio/Cargo.toml index da0366e693..e24acb671c 100644 --- a/sdk-libs/sdk-pinocchio/Cargo.toml +++ b/sdk-libs/sdk-pinocchio/Cargo.toml @@ -13,7 +13,7 @@ v2 = [] [dependencies] pinocchio = { workspace = true } light-hasher = { workspace = true } -light-account-checks = { workspace = true } +light-account-checks = { workspace = true, features = ["pinocchio"] } light-macros = { workspace = true } light-sdk-macros = { workspace = true } light-sdk-types = { workspace = true } diff --git a/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs index fe1b24e55a..a4c9a6f198 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs @@ -1,207 +1,78 @@ -use light_sdk_types::CpiSigner; -use pinocchio::{account_info::AccountInfo, instruction::AccountMeta, msg}; - -use crate::error::{LightSdkError, Result}; - -#[derive(Debug, Copy, Clone)] -pub struct CpiAccountsConfig { - pub cpi_context: bool, - pub sol_compression_recipient: bool, - pub sol_pool_pda: bool, - pub cpi_signer: CpiSigner, -} - -impl CpiAccountsConfig { - pub const fn new(cpi_signer: CpiSigner) -> Self { - Self { - cpi_context: false, - sol_compression_recipient: false, - sol_pool_pda: false, - cpi_signer, - } - } - - pub const fn new_with_cpi_context(cpi_signer: CpiSigner) -> Self { - Self { - cpi_context: true, - sol_compression_recipient: false, - sol_pool_pda: false, - cpi_signer, - } - } - - pub fn cpi_signer(&self) -> [u8; 32] { - self.cpi_signer.cpi_signer - } - - pub fn bump(&self) -> u8 { - self.cpi_signer.bump - } -} - -#[repr(usize)] -pub enum CompressionCpiAccountIndex { - LightSystemProgram, - Authority, - RegisteredProgramPda, - NoopProgram, - AccountCompressionAuthority, - AccountCompressionProgram, - InvokingProgram, - SolPoolPda, - DecompressionRecipent, - SystemProgram, - CpiContext, -} - -pub const SYSTEM_ACCOUNTS_LEN: usize = 11; - -pub struct CpiAccounts<'a> { - fee_payer: &'a AccountInfo, - accounts: &'a [AccountInfo], - config: CpiAccountsConfig, -} - -impl<'a> CpiAccounts<'a> { - pub fn new( - fee_payer: &'a AccountInfo, - accounts: &'a [AccountInfo], - config: CpiSigner, - ) -> Result { - let new = Self { - fee_payer, - accounts, - config: CpiAccountsConfig::new(config), - }; - if accounts.len() < new.system_accounts_len() { - return Err(LightSdkError::FewerAccountsThanSystemAccounts); - } - Ok(new) - } - - pub fn new_with_config( - fee_payer: &'a AccountInfo, - accounts: &'a [AccountInfo], - config: CpiAccountsConfig, - ) -> Result { - let new = Self { - fee_payer, - accounts, - config, - }; - if accounts.len() < new.system_accounts_len() { - return Err(LightSdkError::FewerAccountsThanSystemAccounts); - } - Ok(new) - } - - pub fn fee_payer(&self) -> &'a AccountInfo { - self.fee_payer - } - - pub fn light_system_program(&self) -> &'a AccountInfo { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::LightSystemProgram as usize) - .unwrap() - } - - pub fn authority(&self) -> &'a AccountInfo { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::Authority as usize) - .unwrap() - } - - pub fn invoking_program(&self) -> &'a AccountInfo { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::InvokingProgram as usize) - .unwrap() - } - - pub fn self_program_id(&self) -> [u8; 32] { - self.config.cpi_signer.program_id - } - - pub fn bump(&self) -> u8 { - self.config.cpi_signer.bump - } - - pub fn to_account_infos(&self) -> Vec<&'a AccountInfo> { - let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); - account_infos.push(self.fee_payer); - // Skip the first account (light_system_program) and add the rest - self.accounts[1..] - .iter() - .for_each(|acc| account_infos.push(acc)); - let mut current_index = 7; - if !self.config.sol_pool_pda { - account_infos.insert(current_index, self.light_system_program()); - } - current_index += 1; - - if !self.config.sol_compression_recipient { - account_infos.insert(current_index, self.light_system_program()); - } - current_index += 1; - // system program +use light_sdk_types::{ + CompressionCpiAccountIndex, CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN, +}; +pub use light_sdk_types::{CpiAccountsConfig, CpiSigner}; +use pinocchio::{account_info::AccountInfo, instruction::AccountMeta}; + +pub type CpiAccounts<'a> = GenericCpiAccounts<'a, AccountInfo>; + +pub fn to_account_metas<'a>(cpi_accounts: &CpiAccounts<'a>) -> Vec> { + let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_metas.push(AccountMeta::writable_signer(cpi_accounts.fee_payer().key())); + account_metas.push(AccountMeta::readonly_signer(cpi_accounts.authority().key())); + + account_metas.push(AccountMeta::readonly( + cpi_accounts.account_infos()[CompressionCpiAccountIndex::RegisteredProgramPda as usize] + .key(), + )); + account_metas.push(AccountMeta::readonly( + cpi_accounts.account_infos()[CompressionCpiAccountIndex::NoopProgram as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + cpi_accounts.account_infos() + [CompressionCpiAccountIndex::AccountCompressionAuthority as usize] + .key(), + )); + account_metas.push(AccountMeta::readonly( + cpi_accounts.account_infos() + [CompressionCpiAccountIndex::AccountCompressionProgram as usize] + .key(), + )); + account_metas.push(AccountMeta::readonly( + cpi_accounts.account_infos()[CompressionCpiAccountIndex::InvokingProgram as usize].key(), + )); + let mut current_index = 7; + if !cpi_accounts.config().sol_pool_pda { + account_metas.push(AccountMeta::readonly( + cpi_accounts.light_system_program().key(), + )); + } else { + account_metas.push(AccountMeta::writable( + cpi_accounts.account_infos()[current_index].key(), + )); current_index += 1; - - if !self.config.cpi_context { - account_infos.insert(current_index, self.light_system_program()); - } - account_infos } - pub fn to_account_metas(&self) -> Vec { - let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); - account_metas.push(AccountMeta::writable_signer(self.fee_payer.key())); - account_metas.push(AccountMeta::readonly_signer(self.authority().key())); - - account_metas.push(AccountMeta::readonly( - self.accounts[CompressionCpiAccountIndex::RegisteredProgramPda as usize].key(), - )); + if !cpi_accounts.config().sol_compression_recipient { account_metas.push(AccountMeta::readonly( - self.accounts[CompressionCpiAccountIndex::NoopProgram as usize].key(), + cpi_accounts.light_system_program().key(), )); - account_metas.push(AccountMeta::readonly( - self.accounts[CompressionCpiAccountIndex::AccountCompressionAuthority as usize].key(), + } else { + account_metas.push(AccountMeta::writable( + cpi_accounts.account_infos()[current_index].key(), )); + current_index += 1; + } + + // System program - use default (all zeros) + account_metas.push(AccountMeta::readonly(&[0u8; 32])); + current_index += 1; + + if !cpi_accounts.config().cpi_context { account_metas.push(AccountMeta::readonly( - self.accounts[CompressionCpiAccountIndex::AccountCompressionProgram as usize].key(), + cpi_accounts.light_system_program().key(), )); - account_metas.push(AccountMeta::readonly( - self.accounts[CompressionCpiAccountIndex::InvokingProgram as usize].key(), + } else { + account_metas.push(AccountMeta::writable( + cpi_accounts.account_infos()[current_index].key(), )); - let mut current_index = 7; - if !self.config.sol_pool_pda { - account_metas.push(AccountMeta::readonly(self.light_system_program().key())); - } else { - account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); - current_index += 1; - } - - if !self.config.sol_compression_recipient { - account_metas.push(AccountMeta::readonly(self.light_system_program().key())); - } else { - account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); - current_index += 1; - } - - // System program - use default (all zeros) - account_metas.push(AccountMeta::readonly(&[0u8; 32])); current_index += 1; + } - if !self.config.cpi_context { - account_metas.push(AccountMeta::readonly(self.light_system_program().key())); - } else { - account_metas.push(AccountMeta::writable(self.accounts[current_index].key())); - current_index += 1; - } - - // Add remaining tree accounts - self.accounts[current_index..].iter().for_each(|acc| { + // Add remaining tree accounts + cpi_accounts.account_infos()[current_index..] + .iter() + .for_each(|acc| { let account_meta = if acc.is_writable() { AccountMeta::writable(acc.key()) } else { @@ -210,40 +81,31 @@ impl<'a> CpiAccounts<'a> { account_metas.push(account_meta); }); - account_metas - } - - pub fn system_accounts_len(&self) -> usize { - let mut len = 7; // Base system accounts - - if self.config.sol_pool_pda { - len += 1; - } - - if self.config.sol_compression_recipient { - len += 1; - } - - if self.config.cpi_context { - len += 1; - } + account_metas +} - len + 1 // Add system program +pub fn to_account_infos_for_invoke<'a>(cpi_accounts: &CpiAccounts<'a>) -> Vec<&'a AccountInfo> { + let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_infos.push(cpi_accounts.fee_payer()); + // Skip the first account (light_system_program) and add the rest + cpi_accounts.account_infos()[1..] + .iter() + .for_each(|acc| account_infos.push(acc)); + let mut current_index = 7; + if !cpi_accounts.config().sol_pool_pda { + account_infos.insert(current_index, cpi_accounts.light_system_program()); } + current_index += 1; - pub fn account_infos(&self) -> &'a [AccountInfo] { - self.accounts + if !cpi_accounts.config().sol_compression_recipient { + account_infos.insert(current_index, cpi_accounts.light_system_program()); } + current_index += 1; + // system program + current_index += 1; - pub fn tree_accounts(&self) -> &'a [AccountInfo] { - msg!(format!("tree_accounts: {}", self.accounts.len()).as_str()); - msg!(format!("offset {}", self.system_accounts_len()).as_str()); - - // Debug print all accounts - for (i, acc) in self.accounts.iter().enumerate() { - msg!(format!(" accounts[{}] = {:?}", i, acc.key()).as_str()); - } - - &self.accounts[self.system_accounts_len()..] + if !cpi_accounts.config().cpi_context { + account_infos.insert(current_index, cpi_accounts.light_system_program()); } + account_infos } diff --git a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs index f6685d0e50..da60fcea03 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs @@ -169,7 +169,8 @@ pub fn light_system_progam_instruction_invoke_cpi( data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_metas: Vec = cpi_accounts.to_account_metas(); + let account_metas: Vec = + crate::cpi::accounts::to_account_metas(cpi_accounts); // Create instruction with owned data and immediately invoke it use pinocchio::instruction::{Instruction, Seed, Signer}; @@ -192,7 +193,7 @@ pub fn light_system_progam_instruction_invoke_cpi( data: &data, }; sol_log_compute_units(); - let account_infos = cpi_accounts.to_account_infos(); + let account_infos = crate::cpi::accounts::to_account_infos_for_invoke(cpi_accounts); sol_log_compute_units(); match slice_invoke_signed(&instruction, &account_infos, &[signer]) { diff --git a/sdk-libs/sdk-pinocchio/src/error.rs b/sdk-libs/sdk-pinocchio/src/error.rs index 3042a6d853..02f131561a 100644 --- a/sdk-libs/sdk-pinocchio/src/error.rs +++ b/sdk-libs/sdk-pinocchio/src/error.rs @@ -103,6 +103,9 @@ impl From for LightSdkError { LightSdkTypesError::MetaCloseAddressIsNone => LightSdkError::MetaCloseAddressIsNone, LightSdkTypesError::MetaCloseInputIsNone => LightSdkError::MetaCloseInputIsNone, LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), + LightSdkTypesError::FewerAccountsThanSystemAccounts => { + LightSdkError::FewerAccountsThanSystemAccounts + } } } } diff --git a/sdk-libs/sdk-types/Cargo.toml b/sdk-libs/sdk-types/Cargo.toml index 64613a2940..a0821a9f6e 100644 --- a/sdk-libs/sdk-types/Cargo.toml +++ b/sdk-libs/sdk-types/Cargo.toml @@ -12,6 +12,7 @@ anchor = ["anchor-lang", "light-compressed-account/anchor"] [dependencies] anchor-lang = { workspace = true, optional = true } # Light Protocol dependencies +light-account-checks = { workspace = true } light-hasher = { workspace = true } light-compressed-account = { workspace = true } light-macros = { workspace = true } diff --git a/sdk-libs/sdk-types/src/cpi_accounts.rs b/sdk-libs/sdk-types/src/cpi_accounts.rs new file mode 100644 index 0000000000..58ebb62f0d --- /dev/null +++ b/sdk-libs/sdk-types/src/cpi_accounts.rs @@ -0,0 +1,153 @@ +#[cfg(feature = "anchor")] +use anchor_lang::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; +use light_account_checks::AccountInfoTrait; + +use crate::CpiSigner; + +#[derive(Debug, Copy, Clone, AnchorSerialize, AnchorDeserialize)] +pub struct CpiAccountsConfig { + pub cpi_context: bool, + pub sol_compression_recipient: bool, + pub sol_pool_pda: bool, + pub cpi_signer: CpiSigner, +} + +impl CpiAccountsConfig { + pub const fn new(cpi_signer: CpiSigner) -> Self { + Self { + cpi_context: false, + sol_compression_recipient: false, + sol_pool_pda: false, + cpi_signer, + } + } + + pub const fn new_with_cpi_context(cpi_signer: CpiSigner) -> Self { + Self { + cpi_context: true, + sol_compression_recipient: false, + sol_pool_pda: false, + cpi_signer, + } + } + + pub fn cpi_signer(&self) -> [u8; 32] { + self.cpi_signer.cpi_signer + } + + pub fn bump(&self) -> u8 { + self.cpi_signer.bump + } +} + +#[repr(usize)] +pub enum CompressionCpiAccountIndex { + LightSystemProgram, + Authority, + RegisteredProgramPda, + NoopProgram, + AccountCompressionAuthority, + AccountCompressionProgram, + InvokingProgram, + SolPoolPda, + DecompressionRecipent, + SystemProgram, + CpiContext, +} + +pub const SYSTEM_ACCOUNTS_LEN: usize = 11; + +pub struct CpiAccounts<'a, T: AccountInfoTrait> { + fee_payer: &'a T, + accounts: &'a [T], + config: CpiAccountsConfig, +} + +impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { + pub fn new(fee_payer: &'a T, accounts: &'a [T], cpi_signer: CpiSigner) -> Self { + Self { + fee_payer, + accounts, + config: CpiAccountsConfig::new(cpi_signer), + } + } + + pub fn new_with_config(fee_payer: &'a T, accounts: &'a [T], config: CpiAccountsConfig) -> Self { + Self { + fee_payer, + accounts, + config, + } + } + + pub fn fee_payer(&self) -> &'a T { + self.fee_payer + } + + pub fn light_system_program(&self) -> &'a T { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::LightSystemProgram as usize) + .unwrap() + } + + pub fn authority(&self) -> &'a T { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::Authority as usize) + .unwrap() + } + + pub fn invoking_program(&self) -> &'a T { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndex::InvokingProgram as usize) + .unwrap() + } + + pub fn self_program_id(&self) -> T::Pubkey { + T::pubkey_from_bytes(self.config.cpi_signer.program_id) + } + + pub fn bump(&self) -> u8 { + self.config.cpi_signer.bump + } + + pub fn config(&self) -> &CpiAccountsConfig { + &self.config + } + + pub fn system_accounts_len(&self) -> usize { + let mut len = SYSTEM_ACCOUNTS_LEN; + if !self.config.sol_pool_pda { + len -= 1; + } + if !self.config.sol_compression_recipient { + len -= 1; + } + if !self.config.cpi_context { + len -= 1; + } + len + } + + pub fn account_infos(&self) -> &'a [T] { + self.accounts + } + + pub fn tree_accounts(&self) -> &'a [T] { + &self.accounts[self.system_accounts_len()..] + } + + /// Create a vector of account info references + pub fn to_account_infos(&self) -> Vec<&'a T> { + let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_infos.push(self.fee_payer()); + self.account_infos()[1..] + .iter() + .for_each(|acc| account_infos.push(acc)); + account_infos + } +} diff --git a/sdk-libs/sdk-types/src/error.rs b/sdk-libs/sdk-types/src/error.rs index 9653c3a706..b64ada0bcf 100644 --- a/sdk-libs/sdk-types/src/error.rs +++ b/sdk-libs/sdk-types/src/error.rs @@ -23,6 +23,8 @@ pub enum LightSdkTypesError { MetaCloseAddressIsNone, #[error("Input is none during meta close")] MetaCloseInputIsNone, + #[error("Fewer accounts than system accounts")] + FewerAccountsThanSystemAccounts, #[error(transparent)] Hasher(#[from] HasherError), } @@ -39,6 +41,7 @@ impl From for u32 { LightSdkTypesError::MetaMutOutputIsNone => 14027, LightSdkTypesError::MetaCloseAddressIsNone => 14028, LightSdkTypesError::MetaCloseInputIsNone => 14029, + LightSdkTypesError::FewerAccountsThanSystemAccounts => 14030, LightSdkTypesError::Hasher(e) => e.into(), } } diff --git a/sdk-libs/sdk-types/src/lib.rs b/sdk-libs/sdk-types/src/lib.rs index 7a49b9e1d8..8e9a1da053 100644 --- a/sdk-libs/sdk-types/src/lib.rs +++ b/sdk-libs/sdk-types/src/lib.rs @@ -1,5 +1,6 @@ pub mod address; pub mod constants; +pub mod cpi_accounts; pub mod error; pub mod instruction; @@ -9,6 +10,7 @@ use anchor_lang::{AnchorDeserialize, AnchorSerialize}; #[cfg(not(feature = "anchor"))] use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; pub use constants::*; +pub use cpi_accounts::*; /// Configuration struct containing program ID, CPI signer, and bump for Light Protocol #[derive(Debug, Clone, Copy, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)] diff --git a/sdk-libs/sdk/Cargo.toml b/sdk-libs/sdk/Cargo.toml index 074e412500..41915fc869 100644 --- a/sdk-libs/sdk/Cargo.toml +++ b/sdk-libs/sdk/Cargo.toml @@ -40,7 +40,7 @@ light-sdk-types = { workspace = true } light-macros = { workspace = true } light-compressed-account = { workspace = true } light-hasher = { workspace = true } -light-account-checks = { workspace = true } +light-account-checks = { workspace = true, features = ["solana"] } light-zero-copy = { workspace = true } [dev-dependencies] diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index 5e0cf037a9..410f987b58 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -1,278 +1,109 @@ -#[cfg(feature = "anchor")] -use anchor_lang::{AnchorDeserialize, AnchorSerialize}; -#[cfg(not(feature = "anchor"))] -use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; -use light_sdk_types::CpiSigner; - -use crate::{ - error::{LightSdkError, Result}, - AccountInfo, AccountMeta, Pubkey, +pub use light_sdk_types::CpiAccountsConfig; +use light_sdk_types::{ + CompressionCpiAccountIndex, CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN, }; -#[derive(Debug, Copy, Clone, AnchorSerialize, AnchorDeserialize)] -pub struct CpiAccountsConfig { - pub cpi_context: bool, - pub sol_compression_recipient: bool, - pub sol_pool_pda: bool, - pub cpi_signer: CpiSigner, -} - -impl CpiAccountsConfig { - pub fn new(cpi_signer: CpiSigner) -> Self { - Self { - cpi_context: false, - sol_compression_recipient: false, - sol_pool_pda: false, - cpi_signer, - } - } - - pub fn new_with_cpi_context(cpi_signer: CpiSigner) -> Self { - Self { - cpi_context: true, - sol_compression_recipient: false, - sol_pool_pda: false, - cpi_signer, - } - } - - pub fn cpi_signer(&self) -> [u8; 32] { - self.cpi_signer.cpi_signer - } - - pub fn bump(&self) -> u8 { - self.cpi_signer.bump - } -} - -#[repr(usize)] -pub enum CompressionCpiAccountIndex { - LightSystemProgram, - Authority, - RegisteredProgramPda, - NoopProgram, - AccountCompressionAuthority, - AccountCompressionProgram, - InvokingProgram, - SolPoolPda, - DecompressionRecipent, - SystemProgram, - CpiContext, -} - -pub const SYSTEM_ACCOUNTS_LEN: usize = 11; - -// TODO: add unit tests -pub struct CpiAccounts<'c, 'info> { - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - config: CpiAccountsConfig, -} - -impl<'c, 'info> CpiAccounts<'c, 'info> { - pub fn new( - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - cpi_signer: CpiSigner, - ) -> Result { - let new = Self { - fee_payer, - accounts, - config: CpiAccountsConfig::new(cpi_signer), - }; - if accounts.len() < new.system_accounts_len() { - crate::msg!("accounts len {}", accounts.len()); - return Err(LightSdkError::FewerAccountsThanSystemAccounts); - } - Ok(new) - } - - pub fn new_with_config( - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - config: CpiAccountsConfig, - ) -> Result { - let new = Self { - fee_payer, - accounts, - config, - }; - if accounts.len() < new.system_accounts_len() { - crate::msg!("accounts len {}", accounts.len()); - return Err(LightSdkError::FewerAccountsThanSystemAccounts); - } - Ok(new) - } - - pub fn fee_payer(&self) -> &'c AccountInfo<'info> { - self.fee_payer - } - - pub fn light_system_program(&self) -> &'c AccountInfo<'info> { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::LightSystemProgram as usize) - .unwrap() - } - - pub fn authority(&self) -> &'c AccountInfo<'info> { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::Authority as usize) - .unwrap() - } - - pub fn invoking_program(&self) -> &'c AccountInfo<'info> { - // PANICS: We are sure about the bounds of the slice. - self.accounts - .get(CompressionCpiAccountIndex::InvokingProgram as usize) - .unwrap() - } - - pub fn self_program_id(&self) -> Pubkey { - Pubkey::new_from_array(self.config.cpi_signer.program_id) - } - - pub fn bump(&self) -> u8 { - self.config.cpi_signer.bump - } - - pub fn to_account_infos(&self) -> Vec> { - let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); - account_infos.push(self.fee_payer.clone()); - self.accounts[1..] - .iter() - .for_each(|acc| account_infos.push(acc.clone())); - account_infos - } - - pub fn to_account_metas(&self) -> Vec { - let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); +use crate::{AccountInfo, AccountMeta, Pubkey}; + +pub type CpiAccounts<'c, 'info> = GenericCpiAccounts<'c, AccountInfo<'info>>; + +pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { + let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + account_metas.push(AccountMeta { + pubkey: *cpi_accounts.fee_payer().key, + is_signer: true, + is_writable: true, + }); + account_metas.push(AccountMeta { + pubkey: *cpi_accounts.authority().key, + is_signer: true, + is_writable: false, + }); + let accounts = cpi_accounts.account_infos(); + + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndex::RegisteredProgramPda as usize].key, + is_signer: false, + is_writable: false, + }); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndex::NoopProgram as usize].key, + is_signer: false, + is_writable: false, + }); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndex::AccountCompressionAuthority as usize].key, + is_signer: false, + is_writable: false, + }); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndex::AccountCompressionProgram as usize].key, + is_signer: false, + is_writable: false, + }); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndex::InvokingProgram as usize].key, + is_signer: false, + is_writable: false, + }); + let mut current_index = 7; + if !cpi_accounts.config().sol_pool_pda { account_metas.push(AccountMeta { - pubkey: *self.fee_payer.key, - is_signer: true, - is_writable: true, - }); - account_metas.push(AccountMeta { - pubkey: *self.authority().key, - is_signer: true, - is_writable: false, - }); - - account_metas.push(AccountMeta { - pubkey: *self.accounts[CompressionCpiAccountIndex::RegisteredProgramPda as usize].key, + pubkey: *cpi_accounts.light_system_program().key, is_signer: false, is_writable: false, }); + } else { account_metas.push(AccountMeta { - pubkey: *self.accounts[CompressionCpiAccountIndex::NoopProgram as usize].key, + pubkey: *accounts[current_index].key, is_signer: false, - is_writable: false, + is_writable: true, }); + current_index += 1; + } + + if !cpi_accounts.config().sol_compression_recipient { account_metas.push(AccountMeta { - pubkey: *self.accounts - [CompressionCpiAccountIndex::AccountCompressionAuthority as usize] - .key, + pubkey: *cpi_accounts.light_system_program().key, is_signer: false, is_writable: false, }); + } else { account_metas.push(AccountMeta { - pubkey: *self.accounts[CompressionCpiAccountIndex::AccountCompressionProgram as usize] - .key, + pubkey: *accounts[current_index].key, is_signer: false, - is_writable: false, + is_writable: true, }); + current_index += 1; + } + // System program + account_metas.push(AccountMeta { + pubkey: Pubkey::default(), + is_signer: false, + is_writable: false, + }); + current_index += 1; + + if !cpi_accounts.config().cpi_context { account_metas.push(AccountMeta { - pubkey: *self.accounts[CompressionCpiAccountIndex::InvokingProgram as usize].key, + pubkey: *cpi_accounts.light_system_program().key, is_signer: false, is_writable: false, }); - let mut current_index = 7; - if !self.config.sol_pool_pda { - account_metas.push(AccountMeta { - pubkey: *self.light_system_program().key, - is_signer: false, - is_writable: false, - }); - } else { - account_metas.push(AccountMeta { - pubkey: *self.accounts[current_index].key, - is_signer: false, - is_writable: true, - }); - current_index += 1; - } - - if !self.config.sol_compression_recipient { - account_metas.push(AccountMeta { - pubkey: *self.light_system_program().key, - is_signer: false, - is_writable: false, - }); - } else { - account_metas.push(AccountMeta { - pubkey: *self.accounts[current_index].key, - is_signer: false, - is_writable: true, - }); - current_index += 1; - } - // System program + } else { account_metas.push(AccountMeta { - pubkey: Pubkey::default(), + pubkey: *accounts[current_index].key, is_signer: false, - is_writable: false, + is_writable: true, }); current_index += 1; - - if !self.config.cpi_context { - account_metas.push(AccountMeta { - pubkey: *self.light_system_program().key, - is_signer: false, - is_writable: false, - }); - } else { - account_metas.push(AccountMeta { - pubkey: *self.accounts[current_index].key, - is_signer: false, - is_writable: true, - }); - current_index += 1; - } - //self.system_accounts_len() - self.accounts[current_index..].iter().for_each(|acc| { - account_metas.push(AccountMeta { - pubkey: *acc.key, - is_signer: false, - is_writable: true, - }); - }); - account_metas - } - - pub fn config(&self) -> &CpiAccountsConfig { - &self.config - } - - pub fn system_accounts_len(&self) -> usize { - let mut len = SYSTEM_ACCOUNTS_LEN; - if !self.config.sol_pool_pda { - len -= 1; - } - if !self.config.sol_compression_recipient { - len -= 1; - } - if !self.config.cpi_context { - len -= 1; - } - len - } - - pub fn account_infos(&self) -> &'c [AccountInfo<'info>] { - self.accounts - } - - pub fn tree_accounts(&self) -> &'c [AccountInfo<'info>] { - &self.accounts[self.system_accounts_len()..] } + accounts[current_index..].iter().for_each(|acc| { + account_metas.push(AccountMeta { + pubkey: *acc.key, + is_signer: false, + is_writable: true, + }); + }); + account_metas } diff --git a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs index 763de0156c..26629de079 100644 --- a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs +++ b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs @@ -2,7 +2,7 @@ use crate::{ cpi::{CpiAccountsConfig, CpiSigner}, error::Result, - msg, AccountInfo, AccountMeta, Pubkey, + AccountInfo, AccountMeta, Pubkey, }; #[repr(usize)] @@ -52,7 +52,7 @@ impl<'c, 'info> CpiAccountsSmall<'c, 'info> { accounts: &'c [AccountInfo<'info>], config: CpiAccountsConfig, ) -> Result { - msg!("config {:?}", config); + solana_msg::msg!("config {:?}", config); // if accounts.len() < SYSTEM_ACCOUNTS_LEN { // msg!("accounts len {}", accounts.len()); // return Err(LightSdkError::FewerAccountsThanSystemAccounts); diff --git a/sdk-libs/sdk/src/cpi/invoke.rs b/sdk-libs/sdk/src/cpi/invoke.rs index 30e64fb48e..c4a378731a 100644 --- a/sdk-libs/sdk/src/cpi/invoke.rs +++ b/sdk-libs/sdk/src/cpi/invoke.rs @@ -10,7 +10,7 @@ use light_compressed_account::{ use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM}; use crate::{ - cpi::CpiAccounts, + cpi::{to_account_metas, CpiAccounts}, error::{LightSdkError, Result}, instruction::{account_info::AccountInfoTrait, ValidityProof}, invoke_signed, AccountInfo, AccountMeta, AnchorSerialize, Instruction, @@ -51,17 +51,17 @@ impl CpiInputs { } pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts) -> Result<()> { - let instruction = create_light_system_progam_instruction_invoke_cpi(self, &cpi_accounts)?; - // sol_log_compute_units(); - let account_infos: Vec = cpi_accounts.to_account_infos(); - // sol_log_compute_units(); - invoke_light_system_program(account_infos.as_slice(), instruction, cpi_accounts.bump()) + let bump = cpi_accounts.bump(); + let account_info_refs = cpi_accounts.to_account_infos(); + let instruction = create_light_system_progam_instruction_invoke_cpi(self, cpi_accounts)?; + let account_infos: Vec = account_info_refs.into_iter().cloned().collect(); + invoke_light_system_program(account_infos.as_slice(), instruction, bump) } } pub fn create_light_system_progam_instruction_invoke_cpi( cpi_inputs: CpiInputs, - cpi_accounts: &CpiAccounts, + cpi_accounts: CpiAccounts, ) -> Result { let owner = *cpi_accounts.invoking_program().key; let (input_compressed_accounts_with_merkle_context, output_compressed_accounts) = @@ -114,7 +114,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_metas: Vec = cpi_accounts.to_account_metas(); + let account_metas: Vec = to_account_metas(cpi_accounts); Ok(Instruction { program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), accounts: account_metas, @@ -125,7 +125,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( /// Invokes the light system program to verify and apply a zk-compressed state /// transition. Serializes CPI instruction data, configures necessary accounts, /// and executes the CPI. -pub fn verify_borsh(light_system_accounts: &CpiAccounts, inputs: &T) -> Result<()> +pub fn verify_borsh(light_system_accounts: CpiAccounts, inputs: &T) -> Result<()> where T: AnchorSerialize, { @@ -135,19 +135,17 @@ where data.extend_from_slice(&light_compressed_account::discriminators::DISCRIMINATOR_INVOKE_CPI); data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_infos: Vec = light_system_accounts.to_account_infos(); + let account_info_refs = light_system_accounts.to_account_infos(); + let account_infos: Vec = account_info_refs.into_iter().cloned().collect(); - let account_metas: Vec = light_system_accounts.to_account_metas(); + let bump = light_system_accounts.bump(); + let account_metas: Vec = to_account_metas(light_system_accounts); let instruction = Instruction { program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), accounts: account_metas, data, }; - invoke_light_system_program( - account_infos.as_slice(), - instruction, - light_system_accounts.bump(), - ) + invoke_light_system_program(account_infos.as_slice(), instruction, bump) } #[inline(always)] diff --git a/sdk-libs/sdk/src/error.rs b/sdk-libs/sdk/src/error.rs index ff53ca93fe..bbd9201f68 100644 --- a/sdk-libs/sdk/src/error.rs +++ b/sdk-libs/sdk/src/error.rs @@ -97,6 +97,9 @@ impl From for LightSdkError { LightSdkTypesError::MetaMutOutputIsNone => LightSdkError::MetaMutOutputIsNone, LightSdkTypesError::MetaCloseAddressIsNone => LightSdkError::MetaCloseAddressIsNone, LightSdkTypesError::MetaCloseInputIsNone => LightSdkError::MetaCloseInputIsNone, + LightSdkTypesError::FewerAccountsThanSystemAccounts => { + LightSdkError::FewerAccountsThanSystemAccounts + } LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), } } diff --git a/sdk-libs/sdk/src/lib.rs b/sdk-libs/sdk/src/lib.rs index d21cacc16d..a1e3073d5b 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -131,6 +131,5 @@ pub use light_sdk_types::constants; use solana_account_info::AccountInfo; use solana_cpi::invoke_signed; use solana_instruction::{AccountMeta, Instruction}; -use solana_msg::msg; use solana_program_error::ProgramError; use solana_pubkey::Pubkey; From c2e061b0cb73f2c6993ae52d28303bb735114286 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 07:30:17 +0100 Subject: [PATCH 03/21] feat: add PackedTreeInfo::get_tree_pubkey() --- examples/anchor/counter/src/lib.rs | 4 +--- .../programs/sdk-anchor-test/src/lib.rs | 4 +--- program-tests/sdk-pinocchio-test/src/create_pda.rs | 8 +++----- program-tests/sdk-test/src/create_pda.rs | 9 +++------ sdk-libs/sdk-types/src/instruction/tree_info.rs | 10 +++++++++- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/anchor/counter/src/lib.rs b/examples/anchor/counter/src/lib.rs index dd81f5b14a..84ae7618e1 100644 --- a/examples/anchor/counter/src/lib.rs +++ b/examples/anchor/counter/src/lib.rs @@ -42,9 +42,7 @@ pub mod counter { let (address, address_seed) = derive_address( &[b"counter", ctx.accounts.signer.key().as_ref()], - &light_cpi_accounts.tree_accounts() - [address_tree_info.address_merkle_tree_pubkey_index as usize] - .key(), + &address_tree_info.get_tree_pubkey(&light_cpi_accounts), &crate::ID, ); diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs index aec943b09a..b1ccd950c6 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs @@ -35,9 +35,7 @@ pub mod sdk_anchor_test { let (address, address_seed) = derive_address( &[b"compressed", name.as_bytes()], - &light_cpi_accounts.tree_accounts() - [address_tree_info.address_merkle_tree_pubkey_index as usize] - .key(), + &address_tree_info.get_tree_pubkey(&light_cpi_accounts), &crate::ID, ); let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); diff --git a/program-tests/sdk-pinocchio-test/src/create_pda.rs b/program-tests/sdk-pinocchio-test/src/create_pda.rs index 0948e69f32..a9982c55c5 100644 --- a/program-tests/sdk-pinocchio-test/src/create_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/create_pda.rs @@ -29,18 +29,16 @@ pub fn create_pda( let address_tree_info = instruction_data.address_tree_info; let (address, address_seed) = if BATCHED { - let tree_acounts = cpi_accounts.tree_accounts(); - let index = tree_acounts[instruction_data + let tree_pubkey = instruction_data .address_tree_info - .address_merkle_tree_pubkey_index as usize] - .key(); + .get_tree_pubkey(&cpi_accounts); let address_seed = hashv_to_bn254_field_size_be_const_array::<3>(&[ b"compressed", instruction_data.data.as_slice(), ])?; let address = light_sdk_pinocchio::light_compressed_account::address::derive_address( &address_seed, - index, + &tree_pubkey, &crate::ID, ); (address, address_seed) diff --git a/program-tests/sdk-test/src/create_pda.rs b/program-tests/sdk-test/src/create_pda.rs index 749cb88572..8c21204001 100644 --- a/program-tests/sdk-test/src/create_pda.rs +++ b/program-tests/sdk-test/src/create_pda.rs @@ -34,14 +34,11 @@ pub fn create_pda( instruction_data.data.as_slice(), ]) .unwrap(); + // to_bytes will go away as soon as we have a light_sdk::address::v2::derive_address + let address_tree_pubkey = address_tree_info.get_tree_pubkey(&cpi_accounts).to_bytes(); let address = light_compressed_account::address::derive_address( &address_seed, - &cpi_accounts.tree_accounts()[instruction_data - .address_tree_info - .address_merkle_tree_pubkey_index - as usize] - .key - .to_bytes(), + &address_tree_pubkey, &crate::ID.to_bytes(), ); (address, address_seed) diff --git a/sdk-libs/sdk-types/src/instruction/tree_info.rs b/sdk-libs/sdk-types/src/instruction/tree_info.rs index c65003fabc..51169d0716 100644 --- a/sdk-libs/sdk-types/src/instruction/tree_info.rs +++ b/sdk-libs/sdk-types/src/instruction/tree_info.rs @@ -1,6 +1,7 @@ +use light_account_checks::AccountInfoTrait; use light_compressed_account::instruction_data::data::NewAddressParamsPacked; -use crate::{AnchorDeserialize, AnchorSerialize}; +use crate::{AnchorDeserialize, AnchorSerialize, CpiAccounts}; #[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] pub struct PackedStateTreeInfo { @@ -27,4 +28,11 @@ impl PackedAddressTreeInfo { seed, } } + + pub fn get_tree_pubkey( + &self, + cpi_accounts: &CpiAccounts<'_, T>, + ) -> T::Pubkey { + cpi_accounts.tree_accounts()[self.address_merkle_tree_pubkey_index as usize].pubkey() + } } From 9482df14df59c15b2b843e966463c6f6f41f2b10 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 08:02:16 +0100 Subject: [PATCH 04/21] refactor: small ix into sdk-types --- .../create-address-test-program/Cargo.toml | 2 +- .../create-address-test-program/src/lib.rs | 13 +- scripts/devenv.sh | 4 + sdk-libs/client/src/indexer/types.rs | 2 +- sdk-libs/sdk-pinocchio/Cargo.toml | 3 +- .../sdk-pinocchio/src/cpi/accounts_small.rs | 51 ++++ sdk-libs/sdk-pinocchio/src/cpi/mod.rs | 4 + sdk-libs/sdk-types/Cargo.toml | 2 + sdk-libs/sdk-types/src/cpi_accounts_small.rs | 99 ++++++++ sdk-libs/sdk-types/src/lib.rs | 7 + sdk-libs/sdk/Cargo.toml | 3 +- sdk-libs/sdk/src/cpi/accounts_small_ix.rs | 232 +++++------------- sdk-libs/sdk/src/cpi/mod.rs | 3 +- 13 files changed, 244 insertions(+), 181 deletions(-) create mode 100644 sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs create mode 100644 sdk-libs/sdk-types/src/cpi_accounts_small.rs diff --git a/program-tests/create-address-test-program/Cargo.toml b/program-tests/create-address-test-program/Cargo.toml index 06f62020a7..bd5757d07a 100644 --- a/program-tests/create-address-test-program/Cargo.toml +++ b/program-tests/create-address-test-program/Cargo.toml @@ -24,4 +24,4 @@ anchor-lang = { workspace = true } light-system-program-anchor = { workspace = true, features = ["cpi"] } account-compression = { workspace = true, features = ["cpi"] } light-compressed-account = { workspace = true, features = ["anchor"] } -light-sdk = { workspace = true, features = ["anchor", "v2"] } +light-sdk = { workspace = true, features = ["anchor", "v2", "small_ix"] } diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index d014a4eb00..a00fa95f66 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -25,7 +25,7 @@ pub mod system_cpi_test { use light_sdk::{ constants::PROGRAM_ID_LIGHT_SYSTEM, - cpi::{invoke_light_system_program, to_account_metas}, + cpi::{invoke_light_system_program, to_account_metas, to_account_metas_small}, }; use super::*; @@ -63,11 +63,14 @@ pub mod system_cpi_test { let (account_infos, account_metas) = if small_ix { use light_sdk::cpi::CpiAccountsSmall; let cpi_accounts = - CpiAccountsSmall::new_with_config(&fee_payer, ctx.remaining_accounts, config) - .map_err(ProgramError::from)?; - let account_infos = cpi_accounts.to_account_infos(); + CpiAccountsSmall::new_with_config(&fee_payer, ctx.remaining_accounts, config); + let account_infos = cpi_accounts + .to_account_infos() + .into_iter() + .cloned() + .collect::>(); - let account_metas = cpi_accounts.to_account_metas(); + let account_metas = to_account_metas_small(cpi_accounts); (account_infos, account_metas) } else { use light_sdk::cpi::CpiAccounts; diff --git a/scripts/devenv.sh b/scripts/devenv.sh index cfffd6f612..66c3867751 100755 --- a/scripts/devenv.sh +++ b/scripts/devenv.sh @@ -17,6 +17,7 @@ deactivate () { unset RUSTUP_HOME unset CARGO_HOME unset LIGHT_PROTOCOL_OLD_RUST_PATH + unset CARGO_FEATURES } # Stop early if already in devenv. @@ -67,6 +68,9 @@ export PATH export REDIS_URL="redis://localhost:6379" +# Enable small_ix feature by default in devenv +export CARGO_FEATURES="small_ix" + if [[ "$(uname)" == "Darwin" ]]; then LIGHT_PROTOCOL_OLD_CPATH="${CPATH:-}" export CPATH="/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include:${CPATH:-}" diff --git a/sdk-libs/client/src/indexer/types.rs b/sdk-libs/client/src/indexer/types.rs index 1efb18f908..51d804e40b 100644 --- a/sdk-libs/client/src/indexer/types.rs +++ b/sdk-libs/client/src/indexer/types.rs @@ -147,7 +147,7 @@ impl AccountProofInputs { if value.root_index.prove_by_index { RootIndex::new_none() } else { - Some(value.root_index.root_index) + RootIndex::new_some(value.root_index.root_index) } }; Ok(Self { diff --git a/sdk-libs/sdk-pinocchio/Cargo.toml b/sdk-libs/sdk-pinocchio/Cargo.toml index e24acb671c..29526c8bd1 100644 --- a/sdk-libs/sdk-pinocchio/Cargo.toml +++ b/sdk-libs/sdk-pinocchio/Cargo.toml @@ -8,7 +8,8 @@ edition = "2021" [features] default = [] -v2 = [] +v2 = ["light-sdk-types/v2"] +small_ix = ["light-sdk-types/small_ix"] [dependencies] pinocchio = { workspace = true } diff --git a/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs b/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs new file mode 100644 index 0000000000..b1d33d13a2 --- /dev/null +++ b/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs @@ -0,0 +1,51 @@ +use light_sdk_types::{ + CompressionCpiAccountIndexSmall, CpiAccountsSmall as GenericCpiAccountsSmall, + PROGRAM_ACCOUNTS_LEN, +}; +use pinocchio::{account_info::AccountInfo, instruction::AccountMeta}; + +pub type CpiAccountsSmall<'a> = GenericCpiAccountsSmall<'a, AccountInfo>; + +pub fn to_account_metas_small<'a>(cpi_accounts: &CpiAccountsSmall<'a>) -> Vec> { + let mut account_metas = + Vec::with_capacity(1 + cpi_accounts.account_infos().len() - PROGRAM_ACCOUNTS_LEN); + + account_metas.push(AccountMeta::writable_signer(cpi_accounts.fee_payer().key())); + account_metas.push(AccountMeta::readonly_signer(cpi_accounts.authority().key())); + + let accounts = cpi_accounts.account_infos(); + account_metas.push(AccountMeta::readonly( + accounts[CompressionCpiAccountIndexSmall::RegisteredProgramPda as usize].key(), + )); + account_metas.push(AccountMeta::readonly( + accounts[CompressionCpiAccountIndexSmall::AccountCompressionAuthority as usize].key(), + )); + + let mut index = CompressionCpiAccountIndexSmall::SolPoolPda as usize; + if cpi_accounts.config().sol_pool_pda { + account_metas.push(AccountMeta::writable(accounts[index].key())); + index += 1; + } + + if cpi_accounts.config().sol_compression_recipient { + account_metas.push(AccountMeta::writable(accounts[index].key())); + index += 1; + } + + if cpi_accounts.config().cpi_context { + account_metas.push(AccountMeta::writable(accounts[index].key())); + index += 1; + } + + // Add remaining tree accounts + accounts[index..].iter().for_each(|acc| { + let account_meta = if acc.is_writable() { + AccountMeta::writable(acc.key()) + } else { + AccountMeta::readonly(acc.key()) + }; + account_metas.push(account_meta); + }); + + account_metas +} diff --git a/sdk-libs/sdk-pinocchio/src/cpi/mod.rs b/sdk-libs/sdk-pinocchio/src/cpi/mod.rs index f077b4ab32..d255f3dc19 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/mod.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/mod.rs @@ -1,5 +1,9 @@ pub mod accounts; +#[cfg(feature = "small_ix")] +pub mod accounts_small; pub mod invoke; pub use accounts::*; +#[cfg(feature = "small_ix")] +pub use accounts_small::*; pub use invoke::*; diff --git a/sdk-libs/sdk-types/Cargo.toml b/sdk-libs/sdk-types/Cargo.toml index a0821a9f6e..9116b15a3f 100644 --- a/sdk-libs/sdk-types/Cargo.toml +++ b/sdk-libs/sdk-types/Cargo.toml @@ -8,6 +8,8 @@ description = "Core types for Light Protocol SDK" [features] anchor = ["anchor-lang", "light-compressed-account/anchor"] +v2 = [] +small_ix = [] [dependencies] anchor-lang = { workspace = true, optional = true } diff --git a/sdk-libs/sdk-types/src/cpi_accounts_small.rs b/sdk-libs/sdk-types/src/cpi_accounts_small.rs new file mode 100644 index 0000000000..a048c60ffe --- /dev/null +++ b/sdk-libs/sdk-types/src/cpi_accounts_small.rs @@ -0,0 +1,99 @@ +use light_account_checks::AccountInfoTrait; + +use crate::{CpiAccountsConfig, CpiSigner}; + +#[repr(usize)] +pub enum CompressionCpiAccountIndexSmall { + LightSystemProgram, // Only exposed to outer instruction + AccountCompressionProgram, // Only exposed to outer instruction + SystemProgram, // Only exposed to outer instruction + Authority, // Cpi authority of the custom program, used to invoke the light system program. + RegisteredProgramPda, + AccountCompressionAuthority, + SolPoolPda, // Optional + DecompressionRecipent, // Optional + CpiContext, // Optional +} + +pub const PROGRAM_ACCOUNTS_LEN: usize = 3; +// 6 + 3 program ids, fee payer is extra. +pub const SMALL_SYSTEM_ACCOUNTS_LEN: usize = 9; + +pub struct CpiAccountsSmall<'a, T: AccountInfoTrait> { + fee_payer: &'a T, + accounts: &'a [T], + config: CpiAccountsConfig, +} + +impl<'a, T: AccountInfoTrait> CpiAccountsSmall<'a, T> { + pub fn new(fee_payer: &'a T, accounts: &'a [T], cpi_signer: CpiSigner) -> Self { + Self { + fee_payer, + accounts, + config: CpiAccountsConfig::new(cpi_signer), + } + } + + pub fn new_with_config(fee_payer: &'a T, accounts: &'a [T], config: CpiAccountsConfig) -> Self { + Self { + fee_payer, + accounts, + config, + } + } + + pub fn fee_payer(&self) -> &'a T { + self.fee_payer + } + + pub fn authority(&self) -> &'a T { + // PANICS: We are sure about the bounds of the slice. + self.accounts + .get(CompressionCpiAccountIndexSmall::Authority as usize) + .unwrap() + } + + pub fn self_program_id(&self) -> T::Pubkey { + T::pubkey_from_bytes(self.config.cpi_signer.program_id) + } + + pub fn config(&self) -> &CpiAccountsConfig { + &self.config + } + + pub fn system_accounts_end_offset(&self) -> usize { + let mut len = SMALL_SYSTEM_ACCOUNTS_LEN; + if !self.config.sol_pool_pda { + len -= 1; + } + if !self.config.sol_compression_recipient { + len -= 1; + } + if !self.config.cpi_context { + len -= 1; + } + len + } + + pub fn account_infos(&self) -> &'a [T] { + self.accounts + } + + pub fn tree_accounts(&self) -> &'a [T] { + &self.accounts[self.system_accounts_end_offset()..] + } + + /// Create a vector of account info references + pub fn to_account_infos(&self) -> Vec<&'a T> { + let mut account_infos = Vec::with_capacity(1 + self.accounts.len() - PROGRAM_ACCOUNTS_LEN); + account_infos.push(self.fee_payer()); + self.accounts[PROGRAM_ACCOUNTS_LEN..] + .iter() + .for_each(|acc| account_infos.push(acc)); + account_infos + } + + pub fn account_infos_slice(&self) -> &[T] { + &self.accounts[PROGRAM_ACCOUNTS_LEN..] + } +} diff --git a/sdk-libs/sdk-types/src/lib.rs b/sdk-libs/sdk-types/src/lib.rs index 8e9a1da053..015c8a8e6c 100644 --- a/sdk-libs/sdk-types/src/lib.rs +++ b/sdk-libs/sdk-types/src/lib.rs @@ -1,6 +1,8 @@ pub mod address; pub mod constants; pub mod cpi_accounts; +#[cfg(feature = "small_ix")] +pub mod cpi_accounts_small; pub mod error; pub mod instruction; @@ -11,6 +13,11 @@ use anchor_lang::{AnchorDeserialize, AnchorSerialize}; use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; pub use constants::*; pub use cpi_accounts::*; +#[cfg(feature = "small_ix")] +pub use cpi_accounts_small::{ + CompressionCpiAccountIndexSmall, CpiAccountsSmall, PROGRAM_ACCOUNTS_LEN, + SMALL_SYSTEM_ACCOUNTS_LEN, +}; /// Configuration struct containing program ID, CPI signer, and bump for Light Protocol #[derive(Debug, Clone, Copy, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)] diff --git a/sdk-libs/sdk/Cargo.toml b/sdk-libs/sdk/Cargo.toml index 41915fc869..9adbf95435 100644 --- a/sdk-libs/sdk/Cargo.toml +++ b/sdk-libs/sdk/Cargo.toml @@ -18,7 +18,8 @@ anchor = [ "light-compressed-account/anchor", "light-sdk-types/anchor", ] -v2 = [] +v2 = ["light-sdk-types/v2"] +small_ix = ["light-sdk-types/small_ix"] [dependencies] solana-pubkey = { workspace = true, features = ["borsh", "sha2", "curve25519"] } diff --git a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs index 26629de079..6f030fee1b 100644 --- a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs +++ b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs @@ -1,186 +1,76 @@ -#![cfg(feature = "v2")] -use crate::{ - cpi::{CpiAccountsConfig, CpiSigner}, - error::Result, - AccountInfo, AccountMeta, Pubkey, +use light_sdk_types::{ + CompressionCpiAccountIndexSmall, CpiAccountsSmall as GenericCpiAccountsSmall, + PROGRAM_ACCOUNTS_LEN, }; -#[repr(usize)] -pub enum CompressionCpiAccountIndexSmall { - LightSystemProgram, // Only exposed to outer instruction - AccountCompressionProgram, // Only exposed to outer instruction - SystemProgram, // Only exposed to outer instruction - Authority, // Cpi authority of the custom program, used to invoke the light system program. - RegisteredProgramPda, - AccountCompressionAuthority, - SolPoolPda, // Optional - DecompressionRecipent, // Optional - CpiContext, // Optional -} - -pub const PROGRAM_ACCOUNTS_LEN: usize = 3; -// 6 + 3 program ids, fee payer is extra. -pub const SMALL_SYSTEM_ACCOUNTS_LEN: usize = 9; - -// TODO: add unit tests -pub struct CpiAccountsSmall<'c, 'info> { - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - config: CpiAccountsConfig, -} - -impl<'c, 'info> CpiAccountsSmall<'c, 'info> { - // TODO: consider to pass num of trees to split remaining accounts - pub fn new( - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - cpi_signer: CpiSigner, - ) -> Result { - // if accounts.len() < SYSTEM_ACCOUNTS_LEN { - // msg!("accounts len {}", accounts.len()); - // return Err(LightSdkError::FewerAccountsThanSystemAccounts); - // } - Ok(Self { - fee_payer, - accounts, - config: CpiAccountsConfig::new(cpi_signer), - }) - } - - pub fn new_with_config( - fee_payer: &'c AccountInfo<'info>, - accounts: &'c [AccountInfo<'info>], - config: CpiAccountsConfig, - ) -> Result { - solana_msg::msg!("config {:?}", config); - // if accounts.len() < SYSTEM_ACCOUNTS_LEN { - // msg!("accounts len {}", accounts.len()); - // return Err(LightSdkError::FewerAccountsThanSystemAccounts); - // } - Ok(Self { - fee_payer, - accounts, - config, - }) - } - - pub fn fee_payer(&self) -> &'c AccountInfo<'info> { - self.fee_payer - } - - pub fn authority(&self) -> &'c AccountInfo<'info> { - self.accounts - .get(CompressionCpiAccountIndexSmall::Authority as usize) - .unwrap() - } - - pub fn self_program_id(&self) -> Pubkey { - Pubkey::new_from_array(self.config.cpi_signer.program_id) - } - - /// Account infos for cpi to light system program. - pub fn to_account_infos(&self) -> Vec> { - // TODO: do a version with a const array instead of vector. - let mut account_infos = Vec::with_capacity(1 + self.accounts.len() - PROGRAM_ACCOUNTS_LEN); - account_infos.push(self.fee_payer.clone()); - self.accounts[PROGRAM_ACCOUNTS_LEN..] - .iter() - .for_each(|acc| account_infos.push(acc.clone())); - account_infos - } - - /// Account metas for cpi to light system program. - pub fn to_account_metas(&self) -> Vec { - // TODO: do a version with a const array instead of vector. - let mut account_metas = Vec::with_capacity(1 + self.accounts.len() - PROGRAM_ACCOUNTS_LEN); - +use crate::{AccountInfo, AccountMeta}; + +pub type CpiAccountsSmall<'c, 'info> = GenericCpiAccountsSmall<'c, AccountInfo<'info>>; + +pub fn to_account_metas_small(cpi_accounts: CpiAccountsSmall<'_, '_>) -> Vec { + // TODO: do a version with a const array instead of vector. + let mut account_metas = + Vec::with_capacity(1 + cpi_accounts.account_infos().len() - PROGRAM_ACCOUNTS_LEN); + + account_metas.push(AccountMeta { + pubkey: *cpi_accounts.fee_payer().key, + is_signer: true, + is_writable: true, + }); + account_metas.push(AccountMeta { + pubkey: *cpi_accounts.authority().key, + is_signer: true, + is_writable: false, + }); + + let accounts = cpi_accounts.account_infos(); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndexSmall::RegisteredProgramPda as usize].key, + is_signer: false, + is_writable: false, + }); + account_metas.push(AccountMeta { + pubkey: *accounts[CompressionCpiAccountIndexSmall::AccountCompressionAuthority as usize] + .key, + is_signer: false, + is_writable: false, + }); + + let mut index = CompressionCpiAccountIndexSmall::SolPoolPda as usize; + if cpi_accounts.config().sol_pool_pda { account_metas.push(AccountMeta { - pubkey: *self.fee_payer.key, - is_signer: true, + pubkey: *accounts[index].key, + is_signer: false, is_writable: true, }); - account_metas.push(AccountMeta { - pubkey: *self.authority().key, - is_signer: true, - is_writable: false, - }); + index += 1; + } + if cpi_accounts.config().sol_compression_recipient { account_metas.push(AccountMeta { - pubkey: *self.accounts[CompressionCpiAccountIndexSmall::RegisteredProgramPda as usize] - .key, + pubkey: *accounts[index].key, is_signer: false, - is_writable: false, + is_writable: true, }); + index += 1; + } + + if cpi_accounts.config().cpi_context { account_metas.push(AccountMeta { - pubkey: *self.accounts - [CompressionCpiAccountIndexSmall::AccountCompressionAuthority as usize] - .key, + pubkey: *accounts[index].key, is_signer: false, - is_writable: false, - }); - - let mut index = CompressionCpiAccountIndexSmall::SolPoolPda as usize; - if self.config.sol_pool_pda { - account_metas.push(AccountMeta { - pubkey: *self.accounts[index].key, - is_signer: false, - is_writable: true, - }); - index += 1; - } - - if self.config.sol_compression_recipient { - account_metas.push(AccountMeta { - pubkey: *self.accounts[index].key, - is_signer: false, - is_writable: true, - }); - index += 1; - } - - if self.config.cpi_context { - account_metas.push(AccountMeta { - pubkey: *self.accounts[index].key, - is_signer: false, - is_writable: true, - }); - index += 1; - } - assert_eq!(self.system_accounts_end_offset(), index); - - self.accounts[index..].iter().for_each(|acc| { - account_metas.push(AccountMeta { - pubkey: *acc.key, - is_signer: false, - is_writable: true, - }); + is_writable: true, }); - account_metas + index += 1; } + assert_eq!(cpi_accounts.system_accounts_end_offset(), index); - pub fn config(&self) -> &CpiAccountsConfig { - &self.config - } - - pub fn system_accounts_end_offset(&self) -> usize { - let mut len = SMALL_SYSTEM_ACCOUNTS_LEN; - if !self.config.sol_pool_pda { - len -= 1; - } - if !self.config.sol_compression_recipient { - len -= 1; - } - if !self.config.cpi_context { - len -= 1; - } - len - } - - pub fn account_infos(&self) -> &'c [AccountInfo<'info>] { - self.accounts - } - - pub fn tree_accounts(&self) -> &'c [AccountInfo<'info>] { - &self.accounts[self.system_accounts_end_offset()..] - } + accounts[index..].iter().for_each(|acc| { + account_metas.push(AccountMeta { + pubkey: *acc.key, + is_signer: false, + is_writable: true, + }); + }); + account_metas } diff --git a/sdk-libs/sdk/src/cpi/mod.rs b/sdk-libs/sdk/src/cpi/mod.rs index 6c53697e81..6de32b4c1e 100644 --- a/sdk-libs/sdk/src/cpi/mod.rs +++ b/sdk-libs/sdk/src/cpi/mod.rs @@ -50,11 +50,12 @@ //! ``` mod accounts; +#[cfg(feature = "small_ix")] mod accounts_small_ix; mod invoke; pub use accounts::*; -#[cfg(feature = "v2")] +#[cfg(feature = "small_ix")] pub use accounts_small_ix::*; pub use invoke::*; /// Derives cpi signer and bump to invoke the light system program at compile time. From 4ee970b0c05a409f8c75507cd9f8fd6d7db5c0dc Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 16:56:11 +0100 Subject: [PATCH 05/21] fix: don't run light-client doc test --- sdk-libs/client/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk-libs/client/src/lib.rs b/sdk-libs/client/src/lib.rs index 54f86d72c0..e69f88231f 100644 --- a/sdk-libs/client/src/lib.rs +++ b/sdk-libs/client/src/lib.rs @@ -17,7 +17,7 @@ //! //! ## Example //! -//! ```rust +//! ```no_run //! use light_client::{ //! rpc::{LightClient, RpcConfig, Rpc}, //! indexer::{Indexer, IndexerRpcConfig, RetryConfig}, @@ -37,7 +37,7 @@ //! limit_ledger_size: None, //! }; //! spawn_validator(config).await; -//! +//! //! // Connect to the validator //! let mut rpc = LightClient::new(RpcConfig::local()).await?; //! From 971d9cf4fa4b51be804e890a96c78e0bb36e7be0 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 17:02:35 +0100 Subject: [PATCH 06/21] cleanup --- sdk-libs/sdk/src/cpi/accounts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index 410f987b58..f668a1a10e 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -102,7 +102,7 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { account_metas.push(AccountMeta { pubkey: *acc.key, is_signer: false, - is_writable: true, + is_writable: acc.is_writable, }); }); account_metas From 8ab33987faf69df59855bc8cf3a224e04410b21f Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 20:44:52 +0100 Subject: [PATCH 07/21] refactor: AddressTreeInfo --- sdk-libs/sdk/src/instruction/tree_info.rs | 34 ++++++++++------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/sdk-libs/sdk/src/instruction/tree_info.rs b/sdk-libs/sdk/src/instruction/tree_info.rs index eb0a547983..0281d824df 100644 --- a/sdk-libs/sdk/src/instruction/tree_info.rs +++ b/sdk-libs/sdk/src/instruction/tree_info.rs @@ -6,8 +6,8 @@ use crate::{AccountInfo, AnchorDeserialize, AnchorSerialize, Pubkey}; #[derive(Debug, Clone, Copy, AnchorDeserialize, AnchorSerialize, PartialEq, Default)] pub struct AddressTreeInfo { - pub address_merkle_tree_pubkey: Pubkey, - pub address_queue_pubkey: Pubkey, + pub tree: Pubkey, + pub queue: Pubkey, } #[deprecated(since = "0.13.0", note = "please use PackedStateTreeInfo")] @@ -68,13 +68,9 @@ pub fn pack_address_tree_info( remaining_accounts: &mut PackedAccounts, root_index: u16, ) -> PackedAddressTreeInfo { - let AddressTreeInfo { - address_merkle_tree_pubkey, - address_queue_pubkey, - } = address_tree_info; - let address_merkle_tree_pubkey_index = - remaining_accounts.insert_or_get(*address_merkle_tree_pubkey); - let address_queue_pubkey_index = remaining_accounts.insert_or_get(*address_queue_pubkey); + let AddressTreeInfo { tree, queue } = address_tree_info; + let address_merkle_tree_pubkey_index = remaining_accounts.insert_or_get(*tree); + let address_queue_pubkey_index = remaining_accounts.insert_or_get(*queue); PackedAddressTreeInfo { address_merkle_tree_pubkey_index, @@ -93,8 +89,8 @@ pub fn unpack_address_tree_infos( *remaining_accounts[x.address_merkle_tree_pubkey_index as usize].key; let address_queue_pubkey = *remaining_accounts[x.address_queue_pubkey_index as usize].key; result.push(AddressTreeInfo { - address_merkle_tree_pubkey, - address_queue_pubkey, + tree: address_merkle_tree_pubkey, + queue: address_queue_pubkey, }); } result @@ -203,8 +199,8 @@ mod test { let mut remaining_accounts = PackedAccounts::default(); let address_tree_info = AddressTreeInfo { - address_merkle_tree_pubkey: Pubkey::new_unique(), - address_queue_pubkey: Pubkey::new_unique(), + tree: Pubkey::new_unique(), + queue: Pubkey::new_unique(), }; let packed_address_tree_info = @@ -225,16 +221,16 @@ mod test { use solana_pubkey::Pubkey; let address_tree_infos = [ AddressTreeInfo { - address_merkle_tree_pubkey: Pubkey::new_unique(), - address_queue_pubkey: Pubkey::new_unique(), + tree: Pubkey::new_unique(), + queue: Pubkey::new_unique(), }, AddressTreeInfo { - address_merkle_tree_pubkey: Pubkey::new_unique(), - address_queue_pubkey: Pubkey::new_unique(), + tree: Pubkey::new_unique(), + queue: Pubkey::new_unique(), }, AddressTreeInfo { - address_merkle_tree_pubkey: Pubkey::new_unique(), - address_queue_pubkey: Pubkey::new_unique(), + tree: Pubkey::new_unique(), + queue: Pubkey::new_unique(), }, ]; From ff999427bb276071300b54ffe52273c3260f26db Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 20:51:08 +0100 Subject: [PATCH 08/21] simplify error conversion --- programs/system/src/processor/create_inputs_cpi_data.rs | 2 +- programs/system/src/processor/create_outputs_cpi_data.rs | 4 ++-- programs/system/src/processor/process.rs | 2 +- programs/system/src/processor/verify_proof.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/system/src/processor/create_inputs_cpi_data.rs b/programs/system/src/processor/create_inputs_cpi_data.rs index ae9bf5db2a..7e090ea86d 100644 --- a/programs/system/src/processor/create_inputs_cpi_data.rs +++ b/programs/system/src/processor/create_inputs_cpi_data.rs @@ -112,7 +112,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.nullifiers[j].account_hash; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.nullifiers[j].account_hash]) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; } } // TODO: benchmark the chaining. diff --git a/programs/system/src/processor/create_outputs_cpi_data.rs b/programs/system/src/processor/create_outputs_cpi_data.rs index 9544b88ed5..aadf536f2b 100644 --- a/programs/system/src/processor/create_outputs_cpi_data.rs +++ b/programs/system/src/processor/create_outputs_cpi_data.rs @@ -169,7 +169,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &cpi_ix_data.output_leaf_indices[j].into(), is_batched, ) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; cpi_ix_data.leaves[j].account_index = index_merkle_tree_account_account - 1; if !cpi_ix_data.nullifiers.is_empty() { @@ -177,7 +177,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.leaves[j].leaf; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.leaves[j].leaf]) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; } } context.set_rollover_fee(current_index as u8, rollover_fee); diff --git a/programs/system/src/processor/process.rs b/programs/system/src/processor/process.rs index e3fe64278e..5f2b7ece96 100644 --- a/programs/system/src/processor/process.rs +++ b/programs/system/src/processor/process.rs @@ -209,7 +209,7 @@ pub fn process< &output_compressed_account_hashes, current_slot, ) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; } } // 11. Sum check --------------------------------------------------- diff --git a/programs/system/src/processor/verify_proof.rs b/programs/system/src/processor/verify_proof.rs index fb1a832aef..075539ce18 100644 --- a/programs/system/src/processor/verify_proof.rs +++ b/programs/system/src/processor/verify_proof.rs @@ -169,9 +169,9 @@ pub fn verify_proof( let public_input_hash = if !leaves.is_empty() && !addresses.is_empty() { // combined inclusion & non-inclusion proof let inclusion_hash = create_two_inputs_hash_chain(roots, leaves) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; let non_inclusion_hash = create_two_inputs_hash_chain(address_roots, addresses) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(|e| SystemProgramError::from(e).into())?; create_hash_chain_from_slice(&[inclusion_hash, non_inclusion_hash]) .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? } else if !leaves.is_empty() { From 0c5e6bdc8e2a295b3242a9d4a483245edca5de9b Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 20:51:36 +0100 Subject: [PATCH 09/21] fix: typo --- sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs index da60fcea03..00ba585649 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs @@ -118,11 +118,11 @@ impl CpiInputs { } pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts) -> Result<()> { - light_system_progam_instruction_invoke_cpi(self, &cpi_accounts) + light_system_program_instruction_invoke_cpi(self, &cpi_accounts) } } -pub fn light_system_progam_instruction_invoke_cpi( +pub fn light_system_program_instruction_invoke_cpi( cpi_inputs: CpiInputs, cpi_accounts: &CpiAccounts, ) -> Result<()> { From 0a9b0322ca5a695da8e97a1cb7dd081b54f832b0 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 20:55:06 +0100 Subject: [PATCH 10/21] unify program id constants --- sdk-libs/sdk-types/src/constants.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/sdk-libs/sdk-types/src/constants.rs b/sdk-libs/sdk-types/src/constants.rs index 8cb8ad3252..37415e3f8e 100644 --- a/sdk-libs/sdk-types/src/constants.rs +++ b/sdk-libs/sdk-types/src/constants.rs @@ -1,25 +1,20 @@ use light_macros::pubkey_array; +/// ID of the account-compression program. pub const ACCOUNT_COMPRESSION_PROGRAM_ID: [u8; 32] = pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); +/// ID of the light-system program. pub const SYSTEM_PROGRAM_ID: [u8; 32] = pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); pub const REGISTERED_PROGRAM_PDA: [u8; 32] = pubkey_array!("35hkDgaAKwMCaxRz2ocSZ6NaUrtKkyNqU6c4RV3tYJRh"); +/// ID of the light-compressed-token program. +pub const C_TOKEN_PROGRAM_ID: [u8; 32] = + pubkey_array!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); /// Seed of the CPI authority. pub const CPI_AUTHORITY_PDA_SEED: &[u8] = b"cpi_authority"; - -/// ID of the account-compression program. -pub const PROGRAM_ID_ACCOUNT_COMPRESSION: [u8; 32] = - pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); -pub const PROGRAM_ID_NOOP: [u8; 32] = pubkey_array!("noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"); -/// ID of the light-system program. -pub const PROGRAM_ID_LIGHT_SYSTEM: [u8; 32] = - pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); -/// ID of the light-compressed-token program. -pub const PROGRAM_ID_LIGHT_TOKEN: [u8; 32] = - pubkey_array!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); +pub const NOOP_PROGRAM_ID: [u8; 32] = pubkey_array!("noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"); pub const STATE_MERKLE_TREE_HEIGHT: usize = 26; pub const STATE_MERKLE_TREE_CHANGELOG: usize = 1400; From c13ec7296e3a54acfea503e0331d8fdf585c68b3 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 20:56:30 +0100 Subject: [PATCH 11/21] fix enum typo --- sdk-libs/sdk-types/src/cpi_accounts.rs | 2 +- sdk-libs/sdk-types/src/cpi_accounts_small.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk-libs/sdk-types/src/cpi_accounts.rs b/sdk-libs/sdk-types/src/cpi_accounts.rs index 58ebb62f0d..e409394b50 100644 --- a/sdk-libs/sdk-types/src/cpi_accounts.rs +++ b/sdk-libs/sdk-types/src/cpi_accounts.rs @@ -52,7 +52,7 @@ pub enum CompressionCpiAccountIndex { AccountCompressionProgram, InvokingProgram, SolPoolPda, - DecompressionRecipent, + DecompressionRecipient, SystemProgram, CpiContext, } diff --git a/sdk-libs/sdk-types/src/cpi_accounts_small.rs b/sdk-libs/sdk-types/src/cpi_accounts_small.rs index a048c60ffe..a3f5b773d6 100644 --- a/sdk-libs/sdk-types/src/cpi_accounts_small.rs +++ b/sdk-libs/sdk-types/src/cpi_accounts_small.rs @@ -10,9 +10,9 @@ pub enum CompressionCpiAccountIndexSmall { Authority, // Cpi authority of the custom program, used to invoke the light system program. RegisteredProgramPda, AccountCompressionAuthority, - SolPoolPda, // Optional - DecompressionRecipent, // Optional - CpiContext, // Optional + SolPoolPda, // Optional + DecompressionRecipient, // Optional + CpiContext, // Optional } pub const PROGRAM_ACCOUNTS_LEN: usize = 3; From f5f4b0696aaf25f8853b671d8cf78b6866e66cbc Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 21:30:44 +0100 Subject: [PATCH 12/21] Revert "simplify error conversion" This reverts commit ff999427bb276071300b54ffe52273c3260f26db. --- programs/system/src/processor/create_inputs_cpi_data.rs | 2 +- programs/system/src/processor/create_outputs_cpi_data.rs | 4 ++-- programs/system/src/processor/process.rs | 2 +- programs/system/src/processor/verify_proof.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/system/src/processor/create_inputs_cpi_data.rs b/programs/system/src/processor/create_inputs_cpi_data.rs index 7e090ea86d..ae9bf5db2a 100644 --- a/programs/system/src/processor/create_inputs_cpi_data.rs +++ b/programs/system/src/processor/create_inputs_cpi_data.rs @@ -112,7 +112,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.nullifiers[j].account_hash; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.nullifiers[j].account_hash]) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } // TODO: benchmark the chaining. diff --git a/programs/system/src/processor/create_outputs_cpi_data.rs b/programs/system/src/processor/create_outputs_cpi_data.rs index aadf536f2b..9544b88ed5 100644 --- a/programs/system/src/processor/create_outputs_cpi_data.rs +++ b/programs/system/src/processor/create_outputs_cpi_data.rs @@ -169,7 +169,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &cpi_ix_data.output_leaf_indices[j].into(), is_batched, ) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; cpi_ix_data.leaves[j].account_index = index_merkle_tree_account_account - 1; if !cpi_ix_data.nullifiers.is_empty() { @@ -177,7 +177,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.leaves[j].leaf; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.leaves[j].leaf]) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } context.set_rollover_fee(current_index as u8, rollover_fee); diff --git a/programs/system/src/processor/process.rs b/programs/system/src/processor/process.rs index 5f2b7ece96..e3fe64278e 100644 --- a/programs/system/src/processor/process.rs +++ b/programs/system/src/processor/process.rs @@ -209,7 +209,7 @@ pub fn process< &output_compressed_account_hashes, current_slot, ) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; } } // 11. Sum check --------------------------------------------------- diff --git a/programs/system/src/processor/verify_proof.rs b/programs/system/src/processor/verify_proof.rs index 075539ce18..fb1a832aef 100644 --- a/programs/system/src/processor/verify_proof.rs +++ b/programs/system/src/processor/verify_proof.rs @@ -169,9 +169,9 @@ pub fn verify_proof( let public_input_hash = if !leaves.is_empty() && !addresses.is_empty() { // combined inclusion & non-inclusion proof let inclusion_hash = create_two_inputs_hash_chain(roots, leaves) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; let non_inclusion_hash = create_two_inputs_hash_chain(address_roots, addresses) - .map_err(|e| SystemProgramError::from(e).into())?; + .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; create_hash_chain_from_slice(&[inclusion_hash, non_inclusion_hash]) .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? } else if !leaves.is_empty() { From b728e570aba1668248cd1655fedb681e2edf2c33 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 21:33:27 +0100 Subject: [PATCH 13/21] fixed stuff --- Cargo.lock | 8 ++++++++ examples/anchor/counter/Cargo.toml | 1 + examples/anchor/token-escrow/Cargo.toml | 1 + forester-utils/src/registry.rs | 2 +- program-tests/client-test/Cargo.toml | 1 + .../create-address-test-program/Cargo.toml | 1 + .../create-address-test-program/src/lib.rs | 15 ++++++++------- .../programs/sdk-anchor-test/Cargo.toml | 1 + program-tests/sdk-pinocchio-test/Cargo.toml | 1 + program-tests/system-cpi-test/Cargo.toml | 1 + program-tests/system-cpi-v2-test/Cargo.toml | 1 + sdk-libs/macros/src/cpi_signer.rs | 4 ++-- sdk-libs/program-test/src/accounts/initialize.rs | 2 +- sdk-libs/program-test/src/accounts/state_tree.rs | 2 +- .../program-test/src/accounts/state_tree_v2.rs | 2 +- .../program-test/src/accounts/test_accounts.rs | 4 ++-- .../src/utils/setup_light_programs.rs | 8 +++----- sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 4 ++-- sdk-libs/sdk-types/src/constants.rs | 2 +- .../sdk-types/src/instruction/account_info.rs | 6 +++--- sdk-libs/sdk/src/cpi/invoke.rs | 6 +++--- sdk-libs/sdk/src/instruction/system_accounts.rs | 16 ++++++++-------- 22 files changed, 52 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf50bd7e28..0a7d131746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1241,6 +1241,7 @@ dependencies = [ "light-prover-client", "light-sdk", "light-sdk-pinocchio", + "light-sdk-types", "light-test-utils", "light-zero-copy", "rand 0.8.5", @@ -1408,6 +1409,7 @@ dependencies = [ "light-hasher", "light-program-test", "light-sdk", + "light-sdk-types", "solana-sdk", "tokio", ] @@ -1439,6 +1441,7 @@ dependencies = [ "light-compressed-account", "light-hasher", "light-sdk", + "light-sdk-types", "light-system-program-anchor", ] @@ -5319,6 +5322,7 @@ dependencies = [ "light-program-test", "light-prover-client", "light-sdk", + "light-sdk-types", "light-test-utils", "solana-sdk", "tokio", @@ -5335,6 +5339,7 @@ dependencies = [ "light-program-test", "light-sdk", "light-sdk-pinocchio", + "light-sdk-types", "pinocchio", "solana-sdk", "tokio", @@ -8892,6 +8897,7 @@ dependencies = [ "light-prover-client", "light-registry", "light-sdk", + "light-sdk-types", "light-system-program-anchor", "light-test-utils", "light-verifier", @@ -8920,6 +8926,7 @@ dependencies = [ "light-prover-client", "light-registry", "light-sdk", + "light-sdk-types", "light-system-program-anchor", "light-test-utils", "light-verifier", @@ -9209,6 +9216,7 @@ dependencies = [ "light-program-test", "light-prover-client", "light-sdk", + "light-sdk-types", "light-system-program-anchor", "light-test-utils", "light-verifier", diff --git a/examples/anchor/counter/Cargo.toml b/examples/anchor/counter/Cargo.toml index a357412257..9d9815bc51 100644 --- a/examples/anchor/counter/Cargo.toml +++ b/examples/anchor/counter/Cargo.toml @@ -23,6 +23,7 @@ idl-build = ["anchor-lang/idl-build", "light-sdk/idl-build"] anchor-lang = { workspace = true } light-hasher = { workspace = true, features = ["solana"] } light-sdk = { workspace = true, features = ["anchor"] } +light-sdk-types = { workspace = true } light-compressed-account = { workspace = true } [dev-dependencies] diff --git a/examples/anchor/token-escrow/Cargo.toml b/examples/anchor/token-escrow/Cargo.toml index 24b8311f74..56f10b2f67 100644 --- a/examples/anchor/token-escrow/Cargo.toml +++ b/examples/anchor/token-escrow/Cargo.toml @@ -27,6 +27,7 @@ account-compression = { workspace = true, features = ["cpi"] } light-hasher = { workspace = true } light-sdk = { workspace = true } light-compressed-account = { workspace = true, features = ["anchor"] } +light-sdk-types = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] solana-sdk = { workspace = true } diff --git a/forester-utils/src/registry.rs b/forester-utils/src/registry.rs index 130c7e3017..b52547d398 100644 --- a/forester-utils/src/registry.rs +++ b/forester-utils/src/registry.rs @@ -324,7 +324,7 @@ pub async fn create_rollover_state_merkle_tree_instructions( rpc.get_minimum_balance_for_rent_exemption(account_size) .await .unwrap(), - &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + &Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID), Some(new_cpi_context_keypair), ); let instruction = create_rollover_state_merkle_tree_instruction( diff --git a/program-tests/client-test/Cargo.toml b/program-tests/client-test/Cargo.toml index 05b173aad4..6580607de4 100644 --- a/program-tests/client-test/Cargo.toml +++ b/program-tests/client-test/Cargo.toml @@ -20,6 +20,7 @@ light-prover-client = { workspace = true, features = ["devenv"] } light-test-utils = { workspace = true } light-sdk = { workspace = true } light-sdk-pinocchio = { workspace = true } +light-sdk-types = { workspace = true } light-zero-copy = { workspace = true } light-hasher = { workspace = true } light-compressed-account = { workspace = true } diff --git a/program-tests/create-address-test-program/Cargo.toml b/program-tests/create-address-test-program/Cargo.toml index bd5757d07a..f25726d3be 100644 --- a/program-tests/create-address-test-program/Cargo.toml +++ b/program-tests/create-address-test-program/Cargo.toml @@ -25,3 +25,4 @@ light-system-program-anchor = { workspace = true, features = ["cpi"] } account-compression = { workspace = true, features = ["cpi"] } light-compressed-account = { workspace = true, features = ["anchor"] } light-sdk = { workspace = true, features = ["anchor", "v2", "small_ix"] } +light-sdk-types = { workspace = true } diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index a00fa95f66..81ea6fdece 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -14,7 +14,13 @@ pub use create_pda::*; use light_compressed_account::instruction_data::{ compressed_proof::CompressedProof, data::NewAddressParamsPacked, }; -use light_sdk::cpi::CpiAccountsConfig; +use light_sdk::{ + constants::LIGHT_SYSTEM_PROGRAM_ID, + cpi::{ + invoke_light_system_program, to_account_metas, to_account_metas_small, CpiAccountsConfig, + }, +}; + declare_id!("FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy"); pub const LIGHT_CPI_SIGNER: CpiSigner = @@ -23,11 +29,6 @@ pub const LIGHT_CPI_SIGNER: CpiSigner = #[program] pub mod system_cpi_test { - use light_sdk::{ - constants::PROGRAM_ID_LIGHT_SYSTEM, - cpi::{invoke_light_system_program, to_account_metas, to_account_metas_small}, - }; - use super::*; pub fn create_compressed_pda<'info>( @@ -86,7 +87,7 @@ pub mod system_cpi_test { (account_infos, account_metas) }; let instruction = Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), + program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, data: inputs, }; diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml index 691faa2826..f0faca1233 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml @@ -24,6 +24,7 @@ idl-build = ["anchor-lang/idl-build", "light-sdk/idl-build"] light-hasher = { workspace = true, features = ["solana"] } anchor-lang = { workspace = true } light-sdk = { workspace = true, features = ["anchor", "v2"] } +light-sdk-types = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] solana-sdk = { workspace = true } diff --git a/program-tests/sdk-pinocchio-test/Cargo.toml b/program-tests/sdk-pinocchio-test/Cargo.toml index 1cc149c56c..66a47f253c 100644 --- a/program-tests/sdk-pinocchio-test/Cargo.toml +++ b/program-tests/sdk-pinocchio-test/Cargo.toml @@ -20,6 +20,7 @@ default = [] [dependencies] light-sdk-pinocchio = { workspace = true, features = ["v2"] } +light-sdk-types = { workspace = true } light-hasher = { workspace = true } pinocchio = { workspace = true } light-macros = { workspace = true } diff --git a/program-tests/system-cpi-test/Cargo.toml b/program-tests/system-cpi-test/Cargo.toml index a2bfa77ddb..351c309767 100644 --- a/program-tests/system-cpi-test/Cargo.toml +++ b/program-tests/system-cpi-test/Cargo.toml @@ -39,6 +39,7 @@ light-test-utils = { workspace = true, features = ["devenv"] } [dev-dependencies] light-client = { workspace = true, features = ["devenv"] } light-sdk = { workspace = true, features = ["anchor"] } +light-sdk-types = { workspace = true } light-program-test = { workspace = true, features = ["devenv"] } tokio = { workspace = true } light-prover-client = { workspace = true, features = ["devenv"] } diff --git a/program-tests/system-cpi-v2-test/Cargo.toml b/program-tests/system-cpi-v2-test/Cargo.toml index e022981312..6c0fb0c08d 100644 --- a/program-tests/system-cpi-v2-test/Cargo.toml +++ b/program-tests/system-cpi-v2-test/Cargo.toml @@ -31,6 +31,7 @@ light-account-checks = { workspace = true } solana-sdk = { workspace = true } light-client = { workspace = true, features = ["devenv"] } light-sdk = { workspace = true, features = ["anchor"] } +light-sdk-types = { workspace = true } light-program-test = { workspace = true, features = ["devenv"] } light-test-utils = { workspace = true, features = ["devenv"] } tokio = { workspace = true } diff --git a/sdk-libs/macros/src/cpi_signer.rs b/sdk-libs/macros/src/cpi_signer.rs index a16084aafa..d27403df1d 100644 --- a/sdk-libs/macros/src/cpi_signer.rs +++ b/sdk-libs/macros/src/cpi_signer.rs @@ -82,8 +82,8 @@ pub fn derive_light_cpi_signer(input: TokenStream) -> TokenStream { let output = quote! { { - // Use the CpiSigner type from the current scope (should be imported) - CpiSigner { + // Use the CpiSigner type with absolute path to avoid import dependency + ::light_sdk_types::CpiSigner { program_id: [#(#program_id_literals),*], cpi_signer: [#(#cpi_signer_literals),*], bump: #bump, diff --git a/sdk-libs/program-test/src/accounts/initialize.rs b/sdk-libs/program-test/src/accounts/initialize.rs index 6c065a1c41..c70e3f8ba7 100644 --- a/sdk-libs/program-test/src/accounts/initialize.rs +++ b/sdk-libs/program-test/src/accounts/initialize.rs @@ -206,7 +206,7 @@ pub async fn initialize_accounts( } let registered_system_program_pda = - get_registered_program_pda(&Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM)); + get_registered_program_pda(&Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID)); let registered_registry_program_pda = get_registered_program_pda(&light_registry::ID); let forester_epoch = if *register_forester_and_advance_to_active_phase { let mut registered_epoch = Epoch::register( diff --git a/sdk-libs/program-test/src/accounts/state_tree.rs b/sdk-libs/program-test/src/accounts/state_tree.rs index f39bab2eb4..8f5d226934 100644 --- a/sdk-libs/program-test/src/accounts/state_tree.rs +++ b/sdk-libs/program-test/src/accounts/state_tree.rs @@ -157,7 +157,7 @@ pub async fn create_state_merkle_tree_and_queue_account( &payer.pubkey(), ProtocolConfig::default().cpi_context_size as usize, rent_cpi_config, - &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + &Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID), Some(cpi_context_keypair), ); diff --git a/sdk-libs/program-test/src/accounts/state_tree_v2.rs b/sdk-libs/program-test/src/accounts/state_tree_v2.rs index f27b2e2d5f..a6458015ac 100644 --- a/sdk-libs/program-test/src/accounts/state_tree_v2.rs +++ b/sdk-libs/program-test/src/accounts/state_tree_v2.rs @@ -60,7 +60,7 @@ pub async fn create_batched_state_merkle_tree( &payer.pubkey(), ProtocolConfig::default().cpi_context_size as usize, rent_cpi_config, - &Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + &Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID), Some(cpi_context_keypair), ); let instruction = if registry { diff --git a/sdk-libs/program-test/src/accounts/test_accounts.rs b/sdk-libs/program-test/src/accounts/test_accounts.rs index 1fc0b2d84c..115577c71c 100644 --- a/sdk-libs/program-test/src/accounts/test_accounts.rs +++ b/sdk-libs/program-test/src/accounts/test_accounts.rs @@ -65,7 +65,7 @@ impl TestAccounts { group_pda: Pubkey::default(), forester: Keypair::from_bytes(&FORESTER_TEST_KEYPAIR).unwrap(), registered_program_pda: get_registered_program_pda(&Pubkey::from( - light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, + light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID, )), registered_registry_program_pda: get_registered_program_pda(&light_registry::ID), registered_forester_pda: Pubkey::default(), @@ -103,7 +103,7 @@ impl TestAccounts { payer.pubkey(), protocol_config_pda, group_pda, - Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID), ); let cpi_context_keypair = Keypair::from_bytes(&SIGNATURE_CPI_TEST_KEYPAIR).unwrap(); diff --git a/sdk-libs/program-test/src/utils/setup_light_programs.rs b/sdk-libs/program-test/src/utils/setup_light_programs.rs index 1b9213ab09..839dd56b8d 100644 --- a/sdk-libs/program-test/src/utils/setup_light_programs.rs +++ b/sdk-libs/program-test/src/utils/setup_light_programs.rs @@ -74,7 +74,7 @@ pub fn setup_light_programs( let path = format!("{}/light_system_program_pinocchio.so", light_bin_path); program_test .add_program_from_file( - light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM.into(), + light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID.into(), path.clone(), ) .inspect_err(|_| { @@ -89,7 +89,7 @@ pub fn setup_light_programs( { let path = format!("{}/light_system_program.so", light_bin_path); program_test.add_program_from_file( - Pubkey::from(light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM), + Pubkey::from(light_sdk::constants::LIGHT_SYSTEM_PROGRAM_ID), path, )?; } @@ -97,9 +97,7 @@ pub fn setup_light_programs( let registered_program = registered_program_test_account_system_program(); program_test .set_account( - Pubkey::new_from_array(REGISTERED_PROGRAM_PDA), // get_registered_program_pda(&Pubkey::from( - // light_sdk::constants::PROGRAM_ID_LIGHT_SYSTEM, - // )) + Pubkey::new_from_array(REGISTERED_PROGRAM_PDA), registered_program, ) .map_err(|e| { diff --git a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs index 00ba585649..7e5370a469 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs @@ -10,7 +10,7 @@ use light_compressed_account::{ with_account_info::CompressedAccountInfo, }, }; -use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM}; +use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID}; use pinocchio::{cpi::slice_invoke_signed, log::sol_log_compute_units, msg, pubkey::Pubkey}; use crate::{ @@ -188,7 +188,7 @@ pub fn light_system_program_instruction_invoke_cpi( sol_log_compute_units(); let instruction = Instruction { - program_id: &PROGRAM_ID_LIGHT_SYSTEM, + program_id: &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID), accounts: &account_metas, data: &data, }; diff --git a/sdk-libs/sdk-types/src/constants.rs b/sdk-libs/sdk-types/src/constants.rs index 37415e3f8e..455cbdfd22 100644 --- a/sdk-libs/sdk-types/src/constants.rs +++ b/sdk-libs/sdk-types/src/constants.rs @@ -4,7 +4,7 @@ use light_macros::pubkey_array; pub const ACCOUNT_COMPRESSION_PROGRAM_ID: [u8; 32] = pubkey_array!("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"); /// ID of the light-system program. -pub const SYSTEM_PROGRAM_ID: [u8; 32] = +pub const LIGHT_SYSTEM_PROGRAM_ID: [u8; 32] = pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); pub const REGISTERED_PROGRAM_PDA: [u8; 32] = pubkey_array!("35hkDgaAKwMCaxRz2ocSZ6NaUrtKkyNqU6c4RV3tYJRh"); diff --git a/sdk-libs/sdk-types/src/instruction/account_info.rs b/sdk-libs/sdk-types/src/instruction/account_info.rs index 1b5ab89814..4a7ac827eb 100644 --- a/sdk-libs/sdk-types/src/instruction/account_info.rs +++ b/sdk-libs/sdk-types/src/instruction/account_info.rs @@ -121,7 +121,7 @@ impl AccountInfoTrait for CompressedAccountInfo { return Err(LightSdkTypesError::MetaMutAddressIsNone); } } else { - return Err(LightSdkTypesError::MetaCloseAddressIsNone); + return Err(LightSdkTypesError::MetaMutAddressIsNone); } if let Some(input) = self.input.as_mut() { @@ -155,7 +155,7 @@ impl AccountInfoTrait for CompressedAccountInfo { if let Some(address) = input_account_meta.get_address() { self_address.copy_from_slice(&address); } else { - return Err(LightSdkTypesError::MetaMutAddressIsNone); + return Err(LightSdkTypesError::MetaCloseAddressIsNone); } } else { return Err(LightSdkTypesError::MetaCloseAddressIsNone); @@ -164,7 +164,7 @@ impl AccountInfoTrait for CompressedAccountInfo { if let Some(input) = self.input.as_mut() { input.input_meta(input_account_meta, input_data_hash, discriminator); } else { - return Err(LightSdkTypesError::MetaMutInputIsNone); + return Err(LightSdkTypesError::MetaCloseInputIsNone); } Ok(()) diff --git a/sdk-libs/sdk/src/cpi/invoke.rs b/sdk-libs/sdk/src/cpi/invoke.rs index c4a378731a..1fd6b8f339 100644 --- a/sdk-libs/sdk/src/cpi/invoke.rs +++ b/sdk-libs/sdk/src/cpi/invoke.rs @@ -7,7 +7,7 @@ use light_compressed_account::{ with_account_info::CompressedAccountInfo, }, }; -use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM}; +use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID}; use crate::{ cpi::{to_account_metas, CpiAccounts}, @@ -116,7 +116,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( let account_metas: Vec = to_account_metas(cpi_accounts); Ok(Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), + program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, data, }) @@ -141,7 +141,7 @@ where let bump = light_system_accounts.bump(); let account_metas: Vec = to_account_metas(light_system_accounts); let instruction = Instruction { - program_id: PROGRAM_ID_LIGHT_SYSTEM.into(), + program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, data, }; diff --git a/sdk-libs/sdk/src/instruction/system_accounts.rs b/sdk-libs/sdk/src/instruction/system_accounts.rs index 2227da20b7..8859068603 100644 --- a/sdk-libs/sdk/src/instruction/system_accounts.rs +++ b/sdk-libs/sdk/src/instruction/system_accounts.rs @@ -1,6 +1,6 @@ use light_sdk_types::constants::{ - CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_ACCOUNT_COMPRESSION, PROGRAM_ID_LIGHT_SYSTEM, - PROGRAM_ID_NOOP, + ACCOUNT_COMPRESSION_PROGRAM_ID, CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID, + NOOP_PROGRAM_ID, }; use crate::{find_cpi_signer_macro, AccountMeta, Pubkey}; @@ -47,20 +47,20 @@ pub struct SystemAccountPubkeys { impl Default for SystemAccountPubkeys { fn default() -> Self { Self { - light_sytem_program: Pubkey::from(PROGRAM_ID_LIGHT_SYSTEM), + light_sytem_program: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID), system_program: Pubkey::default(), - account_compression_program: Pubkey::from(PROGRAM_ID_ACCOUNT_COMPRESSION), + account_compression_program: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID), account_compression_authority: Pubkey::find_program_address( &[CPI_AUTHORITY_PDA_SEED], - &Pubkey::from(PROGRAM_ID_LIGHT_SYSTEM), + &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID), ) .0, registered_program_pda: Pubkey::find_program_address( - &[PROGRAM_ID_LIGHT_SYSTEM.as_slice()], - &Pubkey::from(PROGRAM_ID_ACCOUNT_COMPRESSION), + &[LIGHT_SYSTEM_PROGRAM_ID.as_slice()], + &Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID), ) .0, - noop_program: Pubkey::from(PROGRAM_ID_NOOP), + noop_program: Pubkey::from(NOOP_PROGRAM_ID), // TODO: add correct pubkey sol_pool_pda: Pubkey::default(), } From 4b7a5c6720a554ffdddcf935229ebe8ec9f38ec5 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 21:35:51 +0100 Subject: [PATCH 14/21] refactor: rename duplicate AccountInfoTrait -> CompressedAccountInfoTrait --- .../token-escrow/src/escrow_with_pda/sdk.rs | 4 +-- programs/account-compression/src/errors.rs | 36 ------------------- .../sdk-types/src/instruction/account_info.rs | 4 +-- sdk-libs/sdk/src/cpi/invoke.rs | 2 +- 4 files changed, 4 insertions(+), 42 deletions(-) diff --git a/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs b/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs index 11998515c1..724c07f8b8 100644 --- a/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs +++ b/examples/anchor/token-escrow/src/escrow_with_pda/sdk.rs @@ -133,9 +133,7 @@ pub fn create_withdrawal_escrow_instruction( ); let merkle_tree_indices = add_and_get_remaining_account_indices( - input_params.output_compressed_account_merkle_tree_pubkeys, // .iter() - // .map(|pubkey| anchor_lang::prelude::Pubkey::from(pubkey)) - // .collect::>() + input_params.output_compressed_account_merkle_tree_pubkeys, &mut remaining_accounts, ); diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index c90e91f3cc..3ada11ac35 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -70,40 +70,4 @@ pub enum AccountCompressionErrorCode { UnsupportedHeight, UnsupportedParameters, V1AccountMarkedAsProofByIndex, - #[msg("MerkleTreeMetadataError")] - MerkleTreeMetadataError, - #[msg("BatchedMerkleTreeError")] - BatchedMerkleTreeError, - #[msg("ConcurrentMerkleTreeError")] - ConcurrentMerkleTreeError, - #[msg("IndexedMerkleTreeError")] - IndexedMerkleTreeError, -} - -impl From for AccountCompressionErrorCode { - fn from(err: MerkleTreeMetadataError) -> Self { - msg!("Merkle tree metadata error {}", err); - AccountCompressionErrorCode::MerkleTreeMetadataError - } -} - -impl From for AccountCompressionErrorCode { - fn from(err: BatchedMerkleTreeError) -> Self { - msg!("Batched merkle tree error {}", err); - AccountCompressionErrorCode::BatchedMerkleTreeError - } -} - -impl From for AccountCompressionErrorCode { - fn from(err: ConcurrentMerkleTreeError) -> Self { - msg!("Concurrent merkle tree error {}", err); - AccountCompressionErrorCode::ConcurrentMerkleTreeError - } -} - -impl From for AccountCompressionErrorCode { - fn from(err: IndexedMerkleTreeError) -> Self { - msg!("Indexed merkle tree error {}", err); - AccountCompressionErrorCode::IndexedMerkleTreeError - } } diff --git a/sdk-libs/sdk-types/src/instruction/account_info.rs b/sdk-libs/sdk-types/src/instruction/account_info.rs index 4a7ac827eb..d420efb249 100644 --- a/sdk-libs/sdk-types/src/instruction/account_info.rs +++ b/sdk-libs/sdk-types/src/instruction/account_info.rs @@ -47,7 +47,7 @@ impl InAccountInfoTrait for InAccountInfo { } } -pub trait AccountInfoTrait { +pub trait CompressedAccountInfoTrait { fn init( &mut self, discriminator: [u8; 8], @@ -79,7 +79,7 @@ pub trait AccountInfoTrait { ) -> Result, LightSdkTypesError>; } -impl AccountInfoTrait for CompressedAccountInfo { +impl CompressedAccountInfoTrait for CompressedAccountInfo { /// Initializes a compressed account info with address. /// 1. The account is zeroed, data has to be added in a separate step. /// 2. Once data is added the data hash has to be added. diff --git a/sdk-libs/sdk/src/cpi/invoke.rs b/sdk-libs/sdk/src/cpi/invoke.rs index 1fd6b8f339..705a0ba6f0 100644 --- a/sdk-libs/sdk/src/cpi/invoke.rs +++ b/sdk-libs/sdk/src/cpi/invoke.rs @@ -12,7 +12,7 @@ use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID use crate::{ cpi::{to_account_metas, CpiAccounts}, error::{LightSdkError, Result}, - instruction::{account_info::AccountInfoTrait, ValidityProof}, + instruction::{account_info::CompressedAccountInfoTrait, ValidityProof}, invoke_signed, AccountInfo, AccountMeta, AnchorSerialize, Instruction, }; From 7f13c92072d58e22c93e250eda10bf591254f515 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 21:42:17 +0100 Subject: [PATCH 15/21] revert: remove program changes from pinocchio-sdk commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts only the changes made to the programs/ directory in commit b3f14e7fc while preserving all other changes from that commit. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- Cargo.lock | 2 -- .../tests/address_merkle_tree_tests.rs | 10 ++++------ .../tests/merkle_tree_tests.rs | 18 ++++++++---------- programs/account-compression/Cargo.toml | 1 - programs/account-compression/src/errors.rs | 4 ---- .../rollover_address_merkle_tree_and_queue.rs | 5 ++--- .../rollover_batched_state_merkle_tree.rs | 6 +----- .../rollover_state_merkle_tree_and_queue.rs | 10 +++------- .../src/processor/initialize_address_queue.rs | 11 ++++------- .../initialize_concurrent_merkle_tree.rs | 11 ++++------- programs/compressed-token/src/freeze.rs | 2 +- .../compressed-token/src/process_transfer.rs | 6 +++--- programs/system/Cargo.toml | 1 - programs/system/src/errors.rs | 19 ------------------- .../src/processor/create_inputs_cpi_data.rs | 4 ++-- .../src/processor/create_outputs_cpi_data.rs | 4 ++-- programs/system/src/processor/process.rs | 2 +- programs/system/src/processor/verify_proof.rs | 14 ++++++-------- sdk-libs/sdk/src/address.rs | 16 ++++------------ 19 files changed, 45 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a7d131746..39b673ca65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,6 @@ dependencies = [ "rand 0.8.5", "solana-sdk", "solana-security-txt", - "thiserror 2.0.12", "zerocopy 0.8.25", ] @@ -3743,7 +3742,6 @@ dependencies = [ "pinocchio", "pinocchio-system", "rand 0.8.5", - "solana-msg", "solana-pubkey", "solana-security-txt", "thiserror 2.0.12", diff --git a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs index 8d44feb49f..9d566f451a 100644 --- a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs @@ -1,6 +1,7 @@ #![cfg(feature = "test-sbf")] use std::mem; +use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use account_compression::{ errors::AccountCompressionErrorCode, @@ -1284,8 +1285,7 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -1304,8 +1304,7 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -1357,8 +1356,7 @@ async fn address_merkle_tree_and_queue_rollover( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), + MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), ) .unwrap(); } diff --git a/program-tests/account-compression-test/tests/merkle_tree_tests.rs b/program-tests/account-compression-test/tests/merkle_tree_tests.rs index e10ed079f4..63c426a058 100644 --- a/program-tests/account-compression-test/tests/merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/merkle_tree_tests.rs @@ -1,5 +1,6 @@ #![cfg(feature = "test-sbf")] use std::{collections::HashMap, mem}; +use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use account_compression::{ self, @@ -521,7 +522,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); let nullifier_2 = [2u8; 32]; @@ -538,7 +539,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); // CHECK 4.1: pass non Merkle tree account @@ -579,7 +580,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); } @@ -731,8 +732,7 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -751,8 +751,7 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); @@ -808,8 +807,7 @@ async fn test_init_and_rollover_state_merkle_tree( assert_rpc_error( result, 2, - AccountCompressionErrorCode::MerkleTreeMetadataError.into(), - // MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), + MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver.into(), ) .unwrap(); } @@ -1195,7 +1193,7 @@ async fn test_nullify_leaves( assert_rpc_error( result, 0, - AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), + MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); } diff --git a/programs/account-compression/Cargo.toml b/programs/account-compression/Cargo.toml index 222110f664..36bbe1021c 100644 --- a/programs/account-compression/Cargo.toml +++ b/programs/account-compression/Cargo.toml @@ -41,7 +41,6 @@ light-batched-merkle-tree = { workspace = true, features = ["solana"] } light-merkle-tree-metadata = { workspace = true, features = ["anchor"] } light-zero-copy = { workspace = true } zerocopy = { workspace = true, features = ["derive"] } -thiserror = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] solana-sdk = { workspace = true } diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index 3ada11ac35..c39071adf0 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -1,8 +1,4 @@ use anchor_lang::prelude::*; -use light_batched_merkle_tree::errors::BatchedMerkleTreeError; -use light_concurrent_merkle_tree::errors::ConcurrentMerkleTreeError; -use light_indexed_merkle_tree::errors::IndexedMerkleTreeError; -use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; #[error_code] pub enum AccountCompressionErrorCode { diff --git a/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs b/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs index 115b15c91c..42e6842152 100644 --- a/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs +++ b/programs/account-compression/src/instructions/rollover_address_merkle_tree_and_queue.rs @@ -3,7 +3,6 @@ use light_account_checks::checks::check_account_balance_is_rent_exempt; use crate::{ address_merkle_tree_from_bytes_zero_copy, - errors::AccountCompressionErrorCode, processor::{ initialize_address_merkle_tree::process_initialize_address_merkle_tree, initialize_address_queue::process_initialize_address_queue, @@ -86,14 +85,14 @@ pub fn process_rollover_address_merkle_tree_and_queue<'a, 'b, 'c: 'info, 'info>( ctx.accounts.old_queue.key().into(), ctx.accounts.new_address_merkle_tree.key().into(), ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; queue_account_loaded .metadata .rollover( ctx.accounts.old_address_merkle_tree.key().into(), ctx.accounts.new_queue.key().into(), ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; let merkle_tree_metadata = merkle_tree_account_loaded.metadata; let queue_metadata = queue_account_loaded.metadata; diff --git a/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs b/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs index 1b15020cbe..275d5db3dd 100644 --- a/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs +++ b/programs/account-compression/src/instructions/rollover_batched_state_merkle_tree.rs @@ -6,7 +6,6 @@ use light_batched_merkle_tree::{ use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use crate::{ - errors::AccountCompressionErrorCode, utils::{ check_signer_is_registered_or_authority::{ check_signer_is_registered_or_authority, GroupAccounts, @@ -92,10 +91,7 @@ pub fn process_rollover_batched_state_merkle_tree<'a, 'b, 'c: 'info, 'info>( rent, )?; if ctx.accounts.old_output_queue.to_account_info().lamports() == 0 { - return Err(AccountCompressionErrorCode::from( - MerkleTreeMetadataError::NotReadyForRollover, - ) - .into()); + return Err(ProgramError::from(MerkleTreeMetadataError::NotReadyForRollover).into()); } Ok(()) } diff --git a/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs b/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs index 6e98354331..7c8c5bb306 100644 --- a/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs +++ b/programs/account-compression/src/instructions/rollover_state_merkle_tree_and_queue.rs @@ -3,7 +3,6 @@ use light_account_checks::checks::check_account_balance_is_rent_exempt; use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use crate::{ - errors::AccountCompressionErrorCode, processor::{ initialize_concurrent_merkle_tree::process_initialize_state_merkle_tree, initialize_nullifier_queue::process_initialize_nullifier_queue, @@ -92,14 +91,14 @@ pub fn process_rollover_state_merkle_tree_nullifier_queue_pair<'a, 'b, 'c: 'info ctx.accounts.old_nullifier_queue.key().into(), ctx.accounts.new_state_merkle_tree.key().into(), ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; queue_account_loaded .metadata .rollover( ctx.accounts.old_state_merkle_tree.key().into(), ctx.accounts.new_nullifier_queue.key().into(), ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; let merkle_tree_metadata = merkle_tree_account_loaded.metadata; let queue_metadata = queue_account_loaded.metadata; @@ -191,10 +190,7 @@ pub fn process_rollover_state_merkle_tree_nullifier_queue_pair<'a, 'b, 'c: 'info .lamports() == 0 { - return Err(AccountCompressionErrorCode::from( - MerkleTreeMetadataError::NotReadyForRollover, - ) - .into()); + return Err(ProgramError::from(MerkleTreeMetadataError::NotReadyForRollover).into()); } Ok(()) } diff --git a/programs/account-compression/src/processor/initialize_address_queue.rs b/programs/account-compression/src/processor/initialize_address_queue.rs index 8db3425427..316982b3d8 100644 --- a/programs/account-compression/src/processor/initialize_address_queue.rs +++ b/programs/account-compression/src/processor/initialize_address_queue.rs @@ -6,10 +6,7 @@ use light_merkle_tree_metadata::{ QueueType, }; -use crate::{ - errors::AccountCompressionErrorCode, - state::{queue_from_bytes_zero_copy_init, QueueAccount}, -}; +use crate::state::{queue_from_bytes_zero_copy_init, QueueAccount}; pub fn process_initialize_address_queue<'info>( queue_account_info: &AccountInfo<'info>, @@ -36,9 +33,9 @@ pub fn process_initialize_address_queue<'info>( let queue_rent = queue_account_info.lamports(); let rollover_fee = if let Some(rollover_threshold) = rollover_threshold { let rollover_fee = compute_rollover_fee(rollover_threshold, height, merkle_tree_rent) - .map_err(AccountCompressionErrorCode::from)? + .map_err(ProgramError::from)? + compute_rollover_fee(rollover_threshold, height, queue_rent) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; check_rollover_fee_sufficient( rollover_fee, queue_rent, @@ -46,7 +43,7 @@ pub fn process_initialize_address_queue<'info>( rollover_threshold, height, ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; msg!("address queue rollover_fee: {}", rollover_fee); rollover_fee } else { diff --git a/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs b/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs index 4823f67b53..a120826e4b 100644 --- a/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs +++ b/programs/account-compression/src/processor/initialize_concurrent_merkle_tree.rs @@ -5,10 +5,7 @@ use light_merkle_tree_metadata::{ rollover::{check_rollover_fee_sufficient, RolloverMetadata}, }; -use crate::{ - errors::AccountCompressionErrorCode, state::StateMerkleTreeAccount, - state_merkle_tree_from_bytes_zero_copy_init, -}; +use crate::{state::StateMerkleTreeAccount, state_merkle_tree_from_bytes_zero_copy_init}; #[allow(unused_variables)] pub fn process_initialize_state_merkle_tree( @@ -36,9 +33,9 @@ pub fn process_initialize_state_merkle_tree( Some(rollover_threshold) => { let rollover_fee = compute_rollover_fee(rollover_threshold, *height, merkle_tree_rent) - .map_err(AccountCompressionErrorCode::from)? + .map_err(ProgramError::from)? + compute_rollover_fee(rollover_threshold, *height, queue_rent) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; check_rollover_fee_sufficient( rollover_fee, queue_rent, @@ -46,7 +43,7 @@ pub fn process_initialize_state_merkle_tree( rollover_threshold, *height, ) - .map_err(AccountCompressionErrorCode::from)?; + .map_err(ProgramError::from)?; msg!(" state Merkle tree rollover_fee: {}", rollover_fee); rollover_fee } diff --git a/programs/compressed-token/src/freeze.rs b/programs/compressed-token/src/freeze.rs index dc8d160e15..bb43ea9b89 100644 --- a/programs/compressed-token/src/freeze.rs +++ b/programs/compressed-token/src/freeze.rs @@ -175,7 +175,7 @@ fn create_token_output_accounts( BATCHED_DISCRIMINATOR => token_data.hash(), _ => panic!(), } - .map_err(|_| crate::ErrorCode::HashToFieldError)?; + .map_err(ProgramError::from)?; let data: CompressedAccountData = CompressedAccountData { discriminator: TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR, diff --git a/programs/compressed-token/src/process_transfer.rs b/programs/compressed-token/src/process_transfer.rs index baeafd9c32..e6f097b73c 100644 --- a/programs/compressed-token/src/process_transfer.rs +++ b/programs/compressed-token/src/process_transfer.rs @@ -272,7 +272,7 @@ pub fn create_output_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(|_| crate::ErrorCode::HashToFieldError)?; + .map_err(ProgramError::from)?; let data = CompressedAccountData { discriminator: TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR, data: token_data_bytes, @@ -361,7 +361,7 @@ pub fn add_data_hash_to_input_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(|_| crate::ErrorCode::HashToFieldError)? + .map_err(ProgramError::from)? } else { TokenData::hash_frozen_with_hashed_values( hashed_mint, @@ -369,7 +369,7 @@ pub fn add_data_hash_to_input_compressed_accounts( &amount_bytes, &hashed_delegate, ) - .map_err(|_| crate::ErrorCode::HashToFieldError)? + .map_err(ProgramError::from)? }; } Ok(()) diff --git a/programs/system/Cargo.toml b/programs/system/Cargo.toml index 6e81012759..a214c0fbd1 100644 --- a/programs/system/Cargo.toml +++ b/programs/system/Cargo.toml @@ -40,7 +40,6 @@ light-account-checks = { workspace = true, features = ["pinocchio"] } pinocchio = { workspace = true } pinocchio-system = { version = "0.2.3" } solana-pubkey = { workspace = true, features = ["curve25519", "sha2"] } -solana-msg = { workspace = true } [dev-dependencies] rand = { workspace = true } diff --git a/programs/system/src/errors.rs b/programs/system/src/errors.rs index 149d569e0c..2c6e06b27b 100644 --- a/programs/system/src/errors.rs +++ b/programs/system/src/errors.rs @@ -1,5 +1,4 @@ use pinocchio::program_error::ProgramError; -use solana_msg::msg; use thiserror::Error; #[derive(Debug, Error, PartialEq)] @@ -108,10 +107,6 @@ pub enum SystemProgramError { InvalidTreeHeight, #[error("TooManyOutputAccounts")] TooManyOutputAccounts, - #[error("CompressedAccountError")] - CompressedAccountError, - #[error("HasherError")] - HasherError, } impl From for ProgramError { @@ -119,17 +114,3 @@ impl From for ProgramError { ProgramError::Custom(e as u32 + 6000) } } - -impl From for SystemProgramError { - fn from(err: light_compressed_account::CompressedAccountError) -> Self { - msg!("Compressed account error {}", err); - SystemProgramError::CompressedAccountError - } -} - -impl From for SystemProgramError { - fn from(err: light_hasher::HasherError) -> Self { - msg!("Hasher error {}", err); - SystemProgramError::HasherError - } -} diff --git a/programs/system/src/processor/create_inputs_cpi_data.rs b/programs/system/src/processor/create_inputs_cpi_data.rs index ae9bf5db2a..824e62952c 100644 --- a/programs/system/src/processor/create_inputs_cpi_data.rs +++ b/programs/system/src/processor/create_inputs_cpi_data.rs @@ -102,7 +102,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &merkle_context.leaf_index.into(), is_batched, ) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?, + .map_err(ProgramError::from)?, leaf_index: merkle_context.leaf_index, prove_by_index: merkle_context.prove_by_index() as u8, queue_index, @@ -112,7 +112,7 @@ pub fn create_inputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.nullifiers[j].account_hash; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.nullifiers[j].account_hash]) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(ProgramError::from)?; } } // TODO: benchmark the chaining. diff --git a/programs/system/src/processor/create_outputs_cpi_data.rs b/programs/system/src/processor/create_outputs_cpi_data.rs index 9544b88ed5..97a2489f69 100644 --- a/programs/system/src/processor/create_outputs_cpi_data.rs +++ b/programs/system/src/processor/create_outputs_cpi_data.rs @@ -169,7 +169,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( &cpi_ix_data.output_leaf_indices[j].into(), is_batched, ) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(ProgramError::from)?; cpi_ix_data.leaves[j].account_index = index_merkle_tree_account_account - 1; if !cpi_ix_data.nullifiers.is_empty() { @@ -177,7 +177,7 @@ pub fn create_outputs_cpi_data<'a, 'info, T: InstructionData<'a>>( hash_chain = cpi_ix_data.leaves[j].leaf; } else { hash_chain = Poseidon::hashv(&[&hash_chain, &cpi_ix_data.leaves[j].leaf]) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(ProgramError::from)?; } } context.set_rollover_fee(current_index as u8, rollover_fee); diff --git a/programs/system/src/processor/process.rs b/programs/system/src/processor/process.rs index e3fe64278e..872ae6d9df 100644 --- a/programs/system/src/processor/process.rs +++ b/programs/system/src/processor/process.rs @@ -209,7 +209,7 @@ pub fn process< &output_compressed_account_hashes, current_slot, ) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(ProgramError::from)?; } } // 11. Sum check --------------------------------------------------- diff --git a/programs/system/src/processor/verify_proof.rs b/programs/system/src/processor/verify_proof.rs index fb1a832aef..51bbbfabb7 100644 --- a/programs/system/src/processor/verify_proof.rs +++ b/programs/system/src/processor/verify_proof.rs @@ -168,20 +168,18 @@ pub fn verify_proof( { let public_input_hash = if !leaves.is_empty() && !addresses.is_empty() { // combined inclusion & non-inclusion proof - let inclusion_hash = create_two_inputs_hash_chain(roots, leaves) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + let inclusion_hash = + create_two_inputs_hash_chain(roots, leaves).map_err(ProgramError::from)?; let non_inclusion_hash = create_two_inputs_hash_chain(address_roots, addresses) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))?; + .map_err(ProgramError::from)?; create_hash_chain_from_slice(&[inclusion_hash, non_inclusion_hash]) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? + .map_err(ProgramError::from)? } else if !leaves.is_empty() { // inclusion proof - create_two_inputs_hash_chain(roots, leaves) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? + create_two_inputs_hash_chain(roots, leaves).map_err(ProgramError::from)? } else { // non-inclusion proof - create_two_inputs_hash_chain(address_roots, addresses) - .map_err(|e| ProgramError::from(SystemProgramError::from(e)))? + create_two_inputs_hash_chain(address_roots, addresses).map_err(ProgramError::from)? }; let vk = select_verifying_key(leaves.len(), addresses.len()) diff --git a/sdk-libs/sdk/src/address.rs b/sdk-libs/sdk/src/address.rs index 90eb9fd819..2da39916d0 100644 --- a/sdk-libs/sdk/src/address.rs +++ b/sdk-libs/sdk/src/address.rs @@ -129,8 +129,8 @@ mod test { #[test] fn test_derive_address() { let address_tree_info = AddressTreeInfo { - address_merkle_tree_pubkey: pubkey!("11111111111111111111111111111111"), - address_queue_pubkey: pubkey!("22222222222222222222222222222222222222222222"), + tree: pubkey!("11111111111111111111111111111111"), + queue: pubkey!("22222222222222222222222222222222222222222222"), }; let program_id = pubkey!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz"); @@ -143,11 +143,7 @@ mod test { let address_seed = derive_address_seed(seeds, &program_id); assert_eq!(address_seed, expected_address_seed); - let (address, address_seed) = derive_address( - seeds, - &address_tree_info.address_merkle_tree_pubkey, - &program_id, - ); + let (address, address_seed) = derive_address(seeds, &address_tree_info.tree, &program_id); assert_eq!(address_seed, expected_address_seed); assert_eq!(address, expected_address.to_bytes()); @@ -160,11 +156,7 @@ mod test { let address_seed = derive_address_seed(seeds, &program_id); assert_eq!(address_seed, expected_address_seed); - let (address, address_seed) = derive_address( - seeds, - &address_tree_info.address_merkle_tree_pubkey, - &program_id, - ); + let (address, address_seed) = derive_address(seeds, &address_tree_info.tree, &program_id); assert_eq!(address_seed, expected_address_seed); assert_eq!(address, expected_address.to_bytes()); } From 518b6dbbf67769cdb605c10ff846941412a5d185 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 22:18:56 +0100 Subject: [PATCH 16/21] cleanup --- Cargo.lock | 1 - program-libs/account-checks/Cargo.toml | 2 -- program-libs/batched-merkle-tree/Cargo.toml | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39b673ca65..d611c7be53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3242,7 +3242,6 @@ dependencies = [ "pinocchio", "rand 0.8.5", "solana-account-info", - "solana-instruction", "solana-program-error", "solana-pubkey", "solana-sysvar", diff --git a/program-libs/account-checks/Cargo.toml b/program-libs/account-checks/Cargo.toml index 6d0933e266..fa95073710 100644 --- a/program-libs/account-checks/Cargo.toml +++ b/program-libs/account-checks/Cargo.toml @@ -13,7 +13,6 @@ solana = [ "solana-sysvar", "solana-account-info", "solana-pubkey", - "solana-instruction", ] pinocchio = ["dep:pinocchio"] test-only = ["dep:rand"] @@ -26,7 +25,6 @@ solana-pubkey = { workspace = true, optional = true, features = [ "curve25519", "sha2", ] } -solana-instruction = { workspace = true, optional = true } pinocchio = { workspace = true, optional = true } thiserror = { workspace = true } rand = { workspace = true, optional = true } diff --git a/program-libs/batched-merkle-tree/Cargo.toml b/program-libs/batched-merkle-tree/Cargo.toml index 414ee91400..92f98ba869 100644 --- a/program-libs/batched-merkle-tree/Cargo.toml +++ b/program-libs/batched-merkle-tree/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" edition = "2021" [features] -default = [] +default = ["solana"] test-only = [] solana = [ "solana-program-error", From 8c2543c1a7a933d0652f1baaad1d94663b49d36f Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 22:41:20 +0100 Subject: [PATCH 17/21] feat: add select_state_tree_info, chore: return Result from get_random_state_tree_info --- examples/anchor/counter/tests/test.rs | 2 +- .../tests/address_merkle_tree_tests.rs | 2 +- .../tests/merkle_tree_tests.rs | 3 +- .../programs/sdk-anchor-test/tests/test.rs | 1 + .../sdk-pinocchio-test/tests/test.rs | 2 +- program-tests/sdk-test/tests/test.rs | 2 +- sdk-libs/client/src/rpc/client.rs | 49 ++++++++++++++++--- sdk-libs/client/src/rpc/errors.rs | 4 ++ sdk-libs/client/src/rpc/rpc_trait.rs | 2 +- sdk-libs/program-test/src/program_test/rpc.rs | 24 ++++++--- sdk-libs/sdk/src/address.rs | 4 +- 11 files changed, 73 insertions(+), 22 deletions(-) diff --git a/examples/anchor/counter/tests/test.rs b/examples/anchor/counter/tests/test.rs index c7832dd86a..bc45fc3751 100644 --- a/examples/anchor/counter/tests/test.rs +++ b/examples/anchor/counter/tests/test.rs @@ -137,7 +137,7 @@ where .value; let output_state_tree_index = rpc - .get_random_state_tree_info() + .get_random_state_tree_info()? .pack_output_tree_index(&mut remaining_accounts)?; let packed_address_tree_info = rpc_result .pack_tree_infos(&mut remaining_accounts) diff --git a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs index 9d566f451a..1a67f70052 100644 --- a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs @@ -1,7 +1,6 @@ #![cfg(feature = "test-sbf")] use std::mem; -use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use account_compression::{ errors::AccountCompressionErrorCode, @@ -19,6 +18,7 @@ use light_concurrent_merkle_tree::errors::ConcurrentMerkleTreeError; use light_hash_set::{HashSet, HashSetError}; use light_hasher::{bigint::bigint_to_be_bytes_array, Poseidon}; use light_indexed_merkle_tree::errors::IndexedMerkleTreeError; +use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use light_program_test::{ accounts::address_tree::create_initialize_address_merkle_tree_and_queue_instruction, indexer::address_tree::AddressMerkleTreeBundle, program_test::LightProgramTest, diff --git a/program-tests/account-compression-test/tests/merkle_tree_tests.rs b/program-tests/account-compression-test/tests/merkle_tree_tests.rs index 63c426a058..9e1e4dbee7 100644 --- a/program-tests/account-compression-test/tests/merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/merkle_tree_tests.rs @@ -1,6 +1,5 @@ #![cfg(feature = "test-sbf")] use std::{collections::HashMap, mem}; -use light_merkle_tree_metadata::errors::MerkleTreeMetadataError; use account_compression::{ self, @@ -22,7 +21,7 @@ use light_hash_set::HashSetError; use light_hasher::{ bigint::bigint_to_be_bytes_array, zero_bytes::poseidon::ZERO_BYTES, Hasher, Poseidon, }; -use light_merkle_tree_metadata::QueueType; +use light_merkle_tree_metadata::{errors::MerkleTreeMetadataError, QueueType}; use light_merkle_tree_reference::MerkleTree; use light_program_test::{ accounts::state_tree::{ diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs index e49867558b..96949c26e5 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs @@ -111,6 +111,7 @@ async fn create_compressed_account( let output_tree_index = rpc .get_random_state_tree_info() + .unwrap() .pack_output_tree_index(&mut remaining_accounts) .unwrap(); diff --git a/program-tests/sdk-pinocchio-test/tests/test.rs b/program-tests/sdk-pinocchio-test/tests/test.rs index cfafdd0854..2276cd0c37 100644 --- a/program-tests/sdk-pinocchio-test/tests/test.rs +++ b/program-tests/sdk-pinocchio-test/tests/test.rs @@ -51,7 +51,7 @@ async fn test_sdk_test() { ); println!("address {:?}", address); println!("address tree pubkey: {:?}", address_tree_pubkey.to_bytes()); - let output_queue = rpc.get_random_state_tree_info().queue; + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; println!("output_queue tree pubkey: {:?}", output_queue.to_bytes()); create_pda( diff --git a/program-tests/sdk-test/tests/test.rs b/program-tests/sdk-test/tests/test.rs index a52e497299..5465d7b37a 100644 --- a/program-tests/sdk-test/tests/test.rs +++ b/program-tests/sdk-test/tests/test.rs @@ -43,7 +43,7 @@ async fn test_sdk_test() { &address_tree_pubkey.to_bytes(), &sdk_test::ID.to_bytes(), ); - let ouput_queue = rpc.get_random_state_tree_info().queue; + let ouput_queue = rpc.get_random_state_tree_info().unwrap().queue; create_pda( &payer, &mut rpc, diff --git a/sdk-libs/client/src/rpc/client.rs b/sdk-libs/client/src/rpc/client.rs index f5d7fc2f0b..bd4b8b49e7 100644 --- a/sdk-libs/client/src/rpc/client.rs +++ b/sdk-libs/client/src/rpc/client.rs @@ -89,7 +89,7 @@ pub struct LightClient { pub payer: Keypair, pub retry_config: RetryConfig, pub indexer: Option, - pub active_state_merkle_trees: Vec, + pub state_merkle_trees: Vec, } impl Debug for LightClient { @@ -130,7 +130,7 @@ impl LightClient { payer, retry_config, indexer, - active_state_merkle_trees: Vec::new(), + state_merkle_trees: Vec::new(), }; if config.fetch_active_tree { new.get_latest_active_state_trees().await?; @@ -682,22 +682,26 @@ impl Rpc for LightClient { &res[0].nullify_table, ) .await?; - self.active_state_merkle_trees = res.clone(); + self.state_merkle_trees = res.clone(); Ok(res) } /// Fetch the latest state tree addresses from the cluster. fn get_state_tree_infos(&self) -> Vec { - self.active_state_merkle_trees.to_vec() + self.state_merkle_trees.to_vec() } /// Gets a random active state tree. /// State trees are cached and have to be fetched or set. - fn get_random_state_tree_info(&self) -> TreeInfo { + fn get_random_state_tree_info(&self) -> Result { + if self.state_merkle_trees.is_empty() { + return Err(RpcError::NoStateTreesAvailable); + } + use rand::Rng; let mut rng = rand::thread_rng(); - self.active_state_merkle_trees[rng.gen_range(0..self.active_state_merkle_trees.len())] + Ok(self.state_merkle_trees[rng.gen_range(0..self.state_merkle_trees.len())]) } fn get_address_tree_v1(&self) -> TreeInfo { @@ -712,3 +716,36 @@ impl Rpc for LightClient { } impl MerkleTreeExt for LightClient {} + +/// Selects a random state tree from the provided list. +/// +/// This function should be used together with `get_state_tree_infos()` to first +/// retrieve the list of state trees, then select one randomly. +/// +/// # Arguments +/// * `rng` - A mutable reference to a random number generator +/// * `state_trees` - A slice of `TreeInfo` representing state trees +/// +/// # Returns +/// A randomly selected `TreeInfo` from the provided list, or an error if the list is empty +/// +/// # Errors +/// Returns `RpcError::NoStateTreesAvailable` if the provided slice is empty +/// +/// # Example +/// ```ignore +/// use rand::thread_rng; +/// let tree_infos = client.get_state_tree_infos(); +/// let mut rng = thread_rng(); +/// let selected_tree = select_state_tree_info(&mut rng, &tree_infos)?; +/// ``` +pub fn select_state_tree_info( + rng: &mut R, + state_trees: &[TreeInfo], +) -> Result { + if state_trees.is_empty() { + return Err(RpcError::NoStateTreesAvailable); + } + + Ok(state_trees[rng.gen_range(0..state_trees.len())]) +} diff --git a/sdk-libs/client/src/rpc/errors.rs b/sdk-libs/client/src/rpc/errors.rs index 23305b7402..e42a9aaf87 100644 --- a/sdk-libs/client/src/rpc/errors.rs +++ b/sdk-libs/client/src/rpc/errors.rs @@ -51,6 +51,9 @@ pub enum RpcError { #[error("Indexer error: {0}")] IndexerError(#[from] IndexerError), + + #[error("No state trees available, use rpc.get_latest_active_state_trees() to fetch state trees")] + NoStateTreesAvailable, } impl From for RpcError { @@ -77,6 +80,7 @@ impl Clone for RpcError { RpcError::StateTreeLookupTableNotFound => RpcError::StateTreeLookupTableNotFound, RpcError::InvalidStateTreeLookupTable => RpcError::InvalidStateTreeLookupTable, RpcError::NullifyTableNotFound => RpcError::NullifyTableNotFound, + RpcError::NoStateTreesAvailable => RpcError::NoStateTreesAvailable, } } } diff --git a/sdk-libs/client/src/rpc/rpc_trait.rs b/sdk-libs/client/src/rpc/rpc_trait.rs index 019deb76fb..6ee3b19d73 100644 --- a/sdk-libs/client/src/rpc/rpc_trait.rs +++ b/sdk-libs/client/src/rpc/rpc_trait.rs @@ -195,7 +195,7 @@ pub trait Rpc: Send + Sync + Debug + 'static { /// Gets a random state tree info. /// State trees are cached and have to be fetched or set. - fn get_random_state_tree_info(&self) -> TreeInfo; + fn get_random_state_tree_info(&self) -> Result; fn get_address_tree_v1(&self) -> TreeInfo; diff --git a/sdk-libs/program-test/src/program_test/rpc.rs b/sdk-libs/program-test/src/program_test/rpc.rs index 4ba05963c7..1838678819 100644 --- a/sdk-libs/program-test/src/program_test/rpc.rs +++ b/sdk-libs/program-test/src/program_test/rpc.rs @@ -266,17 +266,27 @@ impl Rpc for LightProgramTest { /// Gets a random active state tree. /// State trees are cached and have to be fetched or set. - fn get_random_state_tree_info(&self) -> TreeInfo { + fn get_random_state_tree_info(&self) -> Result { use rand::Rng; let mut rng = rand::thread_rng(); #[cfg(not(feature = "v2"))] - return self.test_accounts.v1_state_trees - [rng.gen_range(0..self.test_accounts.v1_state_trees.len())] - .into(); + { + if self.test_accounts.v1_state_trees.is_empty() { + return Err(RpcError::NoStateTreesAvailable); + } + Ok(self.test_accounts.v1_state_trees + [rng.gen_range(0..self.test_accounts.v1_state_trees.len())] + .into()) + } #[cfg(feature = "v2")] - return self.test_accounts.v2_state_trees - [rng.gen_range(0..self.test_accounts.v2_state_trees.len())] - .into(); + { + if self.test_accounts.v2_state_trees.is_empty() { + return Err(RpcError::NoStateTreesAvailable); + } + Ok(self.test_accounts.v2_state_trees + [rng.gen_range(0..self.test_accounts.v2_state_trees.len())] + .into()) + } } fn get_address_tree_v1(&self) -> TreeInfo { diff --git a/sdk-libs/sdk/src/address.rs b/sdk-libs/sdk/src/address.rs index 2da39916d0..fbc9cd8530 100644 --- a/sdk-libs/sdk/src/address.rs +++ b/sdk-libs/sdk/src/address.rs @@ -17,9 +17,9 @@ //! ### Create address example //! ```ignore //! let packed_address_tree_info = instruction_data.address_tree_info; -//! let tree_acounts = cpi_accounts.tree_accounts(); +//! let tree_accounts = cpi_accounts.tree_accounts(); //! -//! let address_tree_pubkey = tree_acounts[address_tree_info +//! let address_tree_pubkey = tree_accounts[address_tree_info //! .address_merkle_tree_pubkey_index as usize] //! .key(); //! From ece1d75c823a336a5bdaa3ae70b5a18260543bef Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 22:55:27 +0100 Subject: [PATCH 18/21] fix: account compression tests --- .../tests/merkle_tree_tests.rs | 8 +++---- sdk-libs/client/src/rpc/errors.rs | 4 +++- sdk-libs/sdk/src/cpi/accounts.rs | 23 +++++++------------ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/program-tests/account-compression-test/tests/merkle_tree_tests.rs b/program-tests/account-compression-test/tests/merkle_tree_tests.rs index 9e1e4dbee7..13c6b09a5b 100644 --- a/program-tests/account-compression-test/tests/merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/merkle_tree_tests.rs @@ -521,7 +521,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); let nullifier_2 = [2u8; 32]; @@ -538,7 +538,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); // CHECK 4.1: pass non Merkle tree account @@ -579,7 +579,7 @@ async fn failing_queue( assert_rpc_error( result, 0, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); } @@ -1192,7 +1192,7 @@ async fn test_nullify_leaves( assert_rpc_error( result, 0, - MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into(), + AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated.into(), ) .unwrap(); } diff --git a/sdk-libs/client/src/rpc/errors.rs b/sdk-libs/client/src/rpc/errors.rs index e42a9aaf87..2875b0ecb2 100644 --- a/sdk-libs/client/src/rpc/errors.rs +++ b/sdk-libs/client/src/rpc/errors.rs @@ -52,7 +52,9 @@ pub enum RpcError { #[error("Indexer error: {0}")] IndexerError(#[from] IndexerError), - #[error("No state trees available, use rpc.get_latest_active_state_trees() to fetch state trees")] + #[error( + "No state trees available, use rpc.get_latest_active_state_trees() to fetch state trees" + )] NoStateTreesAvailable, } diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index f668a1a10e..2b06f8d2d5 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -47,12 +47,13 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { is_writable: false, }); let mut current_index = 7; + let anchor_non_account_meta = AccountMeta { + pubkey: *cpi_accounts.light_system_program().key, + is_signer: false, + is_writable: false, + }; if !cpi_accounts.config().sol_pool_pda { - account_metas.push(AccountMeta { - pubkey: *cpi_accounts.light_system_program().key, - is_signer: false, - is_writable: false, - }); + account_metas.push(anchor_non_account_meta.clone()); } else { account_metas.push(AccountMeta { pubkey: *accounts[current_index].key, @@ -63,11 +64,7 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { } if !cpi_accounts.config().sol_compression_recipient { - account_metas.push(AccountMeta { - pubkey: *cpi_accounts.light_system_program().key, - is_signer: false, - is_writable: false, - }); + account_metas.push(anchor_non_account_meta.clone()); } else { account_metas.push(AccountMeta { pubkey: *accounts[current_index].key, @@ -85,11 +82,7 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { current_index += 1; if !cpi_accounts.config().cpi_context { - account_metas.push(AccountMeta { - pubkey: *cpi_accounts.light_system_program().key, - is_signer: false, - is_writable: false, - }); + account_metas.push(anchor_non_account_meta); } else { account_metas.push(AccountMeta { pubkey: *accounts[current_index].key, From 71d1b993cd14ec10dba53c42453ca55897c7d9d9 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 23:29:05 +0100 Subject: [PATCH 19/21] refactor: add result to handle out of bounds errors --- examples/anchor/counter/src/lib.rs | 4 +- .../create-address-test-program/src/lib.rs | 6 +- .../programs/sdk-anchor-test/src/lib.rs | 4 +- .../sdk-pinocchio-test/src/create_pda.rs | 6 +- program-tests/sdk-test/src/create_pda.rs | 6 +- sdk-libs/sdk-pinocchio/src/cpi/accounts.rs | 94 +++++++-------- .../sdk-pinocchio/src/cpi/accounts_small.rs | 36 ++++-- sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 6 +- sdk-libs/sdk-pinocchio/src/error.rs | 6 + sdk-libs/sdk-types/src/cpi_accounts.rs | 107 +++++++++++++++--- sdk-libs/sdk-types/src/cpi_accounts_small.rs | 72 ++++++++++-- sdk-libs/sdk-types/src/error.rs | 5 +- .../sdk-types/src/instruction/tree_info.rs | 6 +- sdk-libs/sdk/src/cpi/accounts.rs | 51 +++++---- sdk-libs/sdk/src/cpi/accounts_small_ix.rs | 33 ++++-- sdk-libs/sdk/src/cpi/invoke.rs | 6 +- sdk-libs/sdk/src/cpi/mod.rs | 4 +- sdk-libs/sdk/src/error.rs | 6 + sdk-libs/sdk/src/lib.rs | 4 +- 19 files changed, 318 insertions(+), 144 deletions(-) diff --git a/examples/anchor/counter/src/lib.rs b/examples/anchor/counter/src/lib.rs index 84ae7618e1..e23d545efd 100644 --- a/examples/anchor/counter/src/lib.rs +++ b/examples/anchor/counter/src/lib.rs @@ -42,7 +42,9 @@ pub mod counter { let (address, address_seed) = derive_address( &[b"counter", ctx.accounts.signer.key().as_ref()], - &address_tree_info.get_tree_pubkey(&light_cpi_accounts), + &address_tree_info + .get_tree_pubkey(&light_cpi_accounts) + .map_err(|_| ErrorCode::AccountNotEnoughKeys)?, &crate::ID, ); diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index 81ea6fdece..b2c1c4ec6c 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -71,7 +71,8 @@ pub mod system_cpi_test { .cloned() .collect::>(); - let account_metas = to_account_metas_small(cpi_accounts); + let account_metas = to_account_metas_small(cpi_accounts) + .map_err(|_| ErrorCode::AccountNotEnoughKeys)?; (account_infos, account_metas) } else { use light_sdk::cpi::CpiAccounts; @@ -83,7 +84,8 @@ pub mod system_cpi_test { .cloned() .collect::>(); - let account_metas = to_account_metas(cpi_accounts); + let account_metas = + to_account_metas(cpi_accounts).map_err(|_| ErrorCode::AccountNotEnoughKeys)?; (account_infos, account_metas) }; let instruction = Instruction { diff --git a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs index b1ccd950c6..9f5f0367d5 100644 --- a/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs +++ b/program-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs @@ -35,7 +35,9 @@ pub mod sdk_anchor_test { let (address, address_seed) = derive_address( &[b"compressed", name.as_bytes()], - &address_tree_info.get_tree_pubkey(&light_cpi_accounts), + &address_tree_info + .get_tree_pubkey(&light_cpi_accounts) + .map_err(|_| ErrorCode::AccountNotEnoughKeys)?, &crate::ID, ); let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); diff --git a/program-tests/sdk-pinocchio-test/src/create_pda.rs b/program-tests/sdk-pinocchio-test/src/create_pda.rs index a9982c55c5..0b732750ea 100644 --- a/program-tests/sdk-pinocchio-test/src/create_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/create_pda.rs @@ -31,7 +31,7 @@ pub fn create_pda( let (address, address_seed) = if BATCHED { let tree_pubkey = instruction_data .address_tree_info - .get_tree_pubkey(&cpi_accounts); + .get_tree_pubkey(&cpi_accounts)?; let address_seed = hashv_to_bn254_field_size_be_const_array::<3>(&[ b"compressed", instruction_data.data.as_slice(), @@ -45,9 +45,7 @@ pub fn create_pda( } else { light_sdk_pinocchio::address::v1::derive_address( &[b"compressed", instruction_data.data.as_slice()], - cpi_accounts.tree_accounts() - [address_tree_info.address_merkle_tree_pubkey_index as usize] - .key(), + &address_tree_info.get_tree_pubkey(&cpi_accounts)?, &crate::ID, ) }; diff --git a/program-tests/sdk-test/src/create_pda.rs b/program-tests/sdk-test/src/create_pda.rs index 8c21204001..ecd900fddf 100644 --- a/program-tests/sdk-test/src/create_pda.rs +++ b/program-tests/sdk-test/src/create_pda.rs @@ -35,7 +35,7 @@ pub fn create_pda( ]) .unwrap(); // to_bytes will go away as soon as we have a light_sdk::address::v2::derive_address - let address_tree_pubkey = address_tree_info.get_tree_pubkey(&cpi_accounts).to_bytes(); + let address_tree_pubkey = address_tree_info.get_tree_pubkey(&cpi_accounts)?.to_bytes(); let address = light_compressed_account::address::derive_address( &address_seed, &address_tree_pubkey, @@ -45,9 +45,7 @@ pub fn create_pda( } else { light_sdk::address::v1::derive_address( &[b"compressed", instruction_data.data.as_slice()], - cpi_accounts.tree_accounts() - [address_tree_info.address_merkle_tree_pubkey_index as usize] - .key, + &address_tree_info.get_tree_pubkey(&cpi_accounts)?, &crate::ID, ) }; diff --git a/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs index a4c9a6f198..1ebe42c28c 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/accounts.rs @@ -1,56 +1,47 @@ -use light_sdk_types::{ - CompressionCpiAccountIndex, CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN, -}; +use light_sdk_types::{CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN}; pub use light_sdk_types::{CpiAccountsConfig, CpiSigner}; use pinocchio::{account_info::AccountInfo, instruction::AccountMeta}; +use crate::error::{LightSdkError, Result}; + pub type CpiAccounts<'a> = GenericCpiAccounts<'a, AccountInfo>; -pub fn to_account_metas<'a>(cpi_accounts: &CpiAccounts<'a>) -> Vec> { +pub fn to_account_metas<'a>(cpi_accounts: &CpiAccounts<'a>) -> Result>> { let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); account_metas.push(AccountMeta::writable_signer(cpi_accounts.fee_payer().key())); - account_metas.push(AccountMeta::readonly_signer(cpi_accounts.authority().key())); - - account_metas.push(AccountMeta::readonly( - cpi_accounts.account_infos()[CompressionCpiAccountIndex::RegisteredProgramPda as usize] - .key(), + account_metas.push(AccountMeta::readonly_signer( + cpi_accounts.authority()?.key(), )); + account_metas.push(AccountMeta::readonly( - cpi_accounts.account_infos()[CompressionCpiAccountIndex::NoopProgram as usize].key(), + cpi_accounts.registered_program_pda()?.key(), )); + account_metas.push(AccountMeta::readonly(cpi_accounts.noop_program()?.key())); account_metas.push(AccountMeta::readonly( - cpi_accounts.account_infos() - [CompressionCpiAccountIndex::AccountCompressionAuthority as usize] - .key(), + cpi_accounts.account_compression_authority()?.key(), )); account_metas.push(AccountMeta::readonly( - cpi_accounts.account_infos() - [CompressionCpiAccountIndex::AccountCompressionProgram as usize] - .key(), + cpi_accounts.account_compression_program()?.key(), )); account_metas.push(AccountMeta::readonly( - cpi_accounts.account_infos()[CompressionCpiAccountIndex::InvokingProgram as usize].key(), + cpi_accounts.invoking_program()?.key(), )); let mut current_index = 7; + let light_system_program_key = cpi_accounts.light_system_program()?.key(); + if !cpi_accounts.config().sol_pool_pda { - account_metas.push(AccountMeta::readonly( - cpi_accounts.light_system_program().key(), - )); + account_metas.push(AccountMeta::readonly(light_system_program_key)); } else { - account_metas.push(AccountMeta::writable( - cpi_accounts.account_infos()[current_index].key(), - )); + let account = cpi_accounts.get_account_info(current_index)?; + account_metas.push(AccountMeta::writable(account.key())); current_index += 1; } if !cpi_accounts.config().sol_compression_recipient { - account_metas.push(AccountMeta::readonly( - cpi_accounts.light_system_program().key(), - )); + account_metas.push(AccountMeta::readonly(light_system_program_key)); } else { - account_metas.push(AccountMeta::writable( - cpi_accounts.account_infos()[current_index].key(), - )); + let account = cpi_accounts.get_account_info(current_index)?; + account_metas.push(AccountMeta::writable(account.key())); current_index += 1; } @@ -59,32 +50,33 @@ pub fn to_account_metas<'a>(cpi_accounts: &CpiAccounts<'a>) -> Vec(cpi_accounts: &CpiAccounts<'a>) -> Vec<&'a AccountInfo> { +pub fn to_account_infos_for_invoke<'a>( + cpi_accounts: &CpiAccounts<'a>, +) -> Result> { let mut account_infos = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); account_infos.push(cpi_accounts.fee_payer()); // Skip the first account (light_system_program) and add the rest @@ -93,19 +85,19 @@ pub fn to_account_infos_for_invoke<'a>(cpi_accounts: &CpiAccounts<'a>) -> Vec<&' .for_each(|acc| account_infos.push(acc)); let mut current_index = 7; if !cpi_accounts.config().sol_pool_pda { - account_infos.insert(current_index, cpi_accounts.light_system_program()); + account_infos.insert(current_index, cpi_accounts.light_system_program()?); } current_index += 1; if !cpi_accounts.config().sol_compression_recipient { - account_infos.insert(current_index, cpi_accounts.light_system_program()); + account_infos.insert(current_index, cpi_accounts.light_system_program()?); } current_index += 1; // system program current_index += 1; if !cpi_accounts.config().cpi_context { - account_infos.insert(current_index, cpi_accounts.light_system_program()); + account_infos.insert(current_index, cpi_accounts.light_system_program()?); } - account_infos + Ok(account_infos) } diff --git a/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs b/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs index b1d33d13a2..6f59d524b0 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs @@ -4,41 +4,57 @@ use light_sdk_types::{ }; use pinocchio::{account_info::AccountInfo, instruction::AccountMeta}; +use crate::error::Result; + pub type CpiAccountsSmall<'a> = GenericCpiAccountsSmall<'a, AccountInfo>; -pub fn to_account_metas_small<'a>(cpi_accounts: &CpiAccountsSmall<'a>) -> Vec> { +pub fn to_account_metas_small<'a>( + cpi_accounts: &CpiAccountsSmall<'a>, +) -> Result>> { let mut account_metas = Vec::with_capacity(1 + cpi_accounts.account_infos().len() - PROGRAM_ACCOUNTS_LEN); account_metas.push(AccountMeta::writable_signer(cpi_accounts.fee_payer().key())); - account_metas.push(AccountMeta::readonly_signer(cpi_accounts.authority().key())); + account_metas.push(AccountMeta::readonly_signer( + cpi_accounts.authority()?.key(), + )); - let accounts = cpi_accounts.account_infos(); account_metas.push(AccountMeta::readonly( - accounts[CompressionCpiAccountIndexSmall::RegisteredProgramPda as usize].key(), + cpi_accounts.registered_program_pda()?.key(), )); account_metas.push(AccountMeta::readonly( - accounts[CompressionCpiAccountIndexSmall::AccountCompressionAuthority as usize].key(), + cpi_accounts.account_compression_authority()?.key(), )); + let accounts = cpi_accounts.account_infos(); let mut index = CompressionCpiAccountIndexSmall::SolPoolPda as usize; + if cpi_accounts.config().sol_pool_pda { - account_metas.push(AccountMeta::writable(accounts[index].key())); + let account = cpi_accounts.get_account_info(index)?; + account_metas.push(AccountMeta::writable(account.key())); index += 1; } if cpi_accounts.config().sol_compression_recipient { - account_metas.push(AccountMeta::writable(accounts[index].key())); + let account = cpi_accounts.get_account_info(index)?; + account_metas.push(AccountMeta::writable(account.key())); index += 1; } if cpi_accounts.config().cpi_context { - account_metas.push(AccountMeta::writable(accounts[index].key())); + let account = cpi_accounts.get_account_info(index)?; + account_metas.push(AccountMeta::writable(account.key())); index += 1; } // Add remaining tree accounts - accounts[index..].iter().for_each(|acc| { + let tree_accounts = + accounts + .get(index..) + .ok_or(crate::error::LightSdkError::CpiAccountsIndexOutOfBounds( + index, + ))?; + tree_accounts.iter().for_each(|acc| { let account_meta = if acc.is_writable() { AccountMeta::writable(acc.key()) } else { @@ -47,5 +63,5 @@ pub fn to_account_metas_small<'a>(cpi_accounts: &CpiAccountsSmall<'a>) -> Vec Result<()> { - let owner = *cpi_accounts.invoking_program().key(); + let owner = *cpi_accounts.invoking_program()?.key(); let (input_compressed_accounts_with_merkle_context, output_compressed_accounts) = if let Some(account_infos) = cpi_inputs.account_infos.as_ref() { let mut input_compressed_accounts_with_merkle_context = @@ -170,7 +170,7 @@ pub fn light_system_program_instruction_invoke_cpi( data.extend(inputs); let account_metas: Vec = - crate::cpi::accounts::to_account_metas(cpi_accounts); + crate::cpi::accounts::to_account_metas(cpi_accounts)?; // Create instruction with owned data and immediately invoke it use pinocchio::instruction::{Instruction, Seed, Signer}; @@ -193,7 +193,7 @@ pub fn light_system_program_instruction_invoke_cpi( data: &data, }; sol_log_compute_units(); - let account_infos = crate::cpi::accounts::to_account_infos_for_invoke(cpi_accounts); + let account_infos = crate::cpi::accounts::to_account_infos_for_invoke(cpi_accounts)?; sol_log_compute_units(); match slice_invoke_signed(&instruction, &account_infos, &[signer]) { diff --git a/sdk-libs/sdk-pinocchio/src/error.rs b/sdk-libs/sdk-pinocchio/src/error.rs index 02f131561a..5ae8ad1ce7 100644 --- a/sdk-libs/sdk-pinocchio/src/error.rs +++ b/sdk-libs/sdk-pinocchio/src/error.rs @@ -66,6 +66,8 @@ pub enum LightSdkError { MetaCloseAddressIsNone, #[error("Input is none during meta close")] MetaCloseInputIsNone, + #[error("CPI accounts index out of bounds: {0}")] + CpiAccountsIndexOutOfBounds(usize), #[error(transparent)] Hasher(#[from] HasherError), #[error(transparent)] @@ -106,6 +108,9 @@ impl From for LightSdkError { LightSdkTypesError::FewerAccountsThanSystemAccounts => { LightSdkError::FewerAccountsThanSystemAccounts } + LightSdkTypesError::CpiAccountsIndexOutOfBounds(index) => { + LightSdkError::CpiAccountsIndexOutOfBounds(index) + } } } } @@ -142,6 +147,7 @@ impl From for u32 { LightSdkError::MetaMutOutputIsNone => 14027, LightSdkError::MetaCloseAddressIsNone => 14028, LightSdkError::MetaCloseInputIsNone => 14029, + LightSdkError::CpiAccountsIndexOutOfBounds(_) => 14031, LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32, diff --git a/sdk-libs/sdk-types/src/cpi_accounts.rs b/sdk-libs/sdk-types/src/cpi_accounts.rs index e409394b50..c50874de69 100644 --- a/sdk-libs/sdk-types/src/cpi_accounts.rs +++ b/sdk-libs/sdk-types/src/cpi_accounts.rs @@ -4,7 +4,10 @@ use anchor_lang::{AnchorDeserialize, AnchorSerialize}; use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; use light_account_checks::AccountInfoTrait; -use crate::CpiSigner; +use crate::{ + error::{LightSdkTypesError, Result}, + CpiSigner, +}; #[derive(Debug, Copy, Clone, AnchorSerialize, AnchorDeserialize)] pub struct CpiAccountsConfig { @@ -86,25 +89,81 @@ impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { self.fee_payer } - pub fn light_system_program(&self) -> &'a T { - // PANICS: We are sure about the bounds of the slice. + pub fn light_system_program(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::LightSystemProgram as usize; self.accounts - .get(CompressionCpiAccountIndex::LightSystemProgram as usize) - .unwrap() + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) } - pub fn authority(&self) -> &'a T { - // PANICS: We are sure about the bounds of the slice. + pub fn authority(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::Authority as usize; self.accounts - .get(CompressionCpiAccountIndex::Authority as usize) - .unwrap() + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) } - pub fn invoking_program(&self) -> &'a T { - // PANICS: We are sure about the bounds of the slice. + pub fn invoking_program(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::InvokingProgram as usize; self.accounts - .get(CompressionCpiAccountIndex::InvokingProgram as usize) - .unwrap() + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn registered_program_pda(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::RegisteredProgramPda as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn noop_program(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::NoopProgram as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn account_compression_authority(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::AccountCompressionAuthority as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn account_compression_program(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::AccountCompressionProgram as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn sol_pool_pda(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::SolPoolPda as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn decompression_recipient(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::DecompressionRecipient as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn system_program(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::SystemProgram as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn cpi_context(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndex::CpiContext as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) } pub fn self_program_id(&self) -> T::Pubkey { @@ -137,8 +196,26 @@ impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { self.accounts } - pub fn tree_accounts(&self) -> &'a [T] { - &self.accounts[self.system_accounts_len()..] + pub fn get_account_info(&self, index: usize) -> Result<&'a T> { + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn tree_accounts(&self) -> Result<&'a [T]> { + let system_len = self.system_accounts_len(); + self.accounts + .get(system_len..) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(system_len)) + } + + pub fn get_tree_account_info(&self, tree_index: usize) -> Result<&'a T> { + let tree_accounts = self.tree_accounts()?; + tree_accounts + .get(tree_index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds( + self.system_accounts_len() + tree_index, + )) } /// Create a vector of account info references diff --git a/sdk-libs/sdk-types/src/cpi_accounts_small.rs b/sdk-libs/sdk-types/src/cpi_accounts_small.rs index a3f5b773d6..517b84a4a6 100644 --- a/sdk-libs/sdk-types/src/cpi_accounts_small.rs +++ b/sdk-libs/sdk-types/src/cpi_accounts_small.rs @@ -1,6 +1,9 @@ use light_account_checks::AccountInfoTrait; -use crate::{CpiAccountsConfig, CpiSigner}; +use crate::{ + error::{LightSdkTypesError, Result}, + CpiAccountsConfig, CpiSigner, +}; #[repr(usize)] pub enum CompressionCpiAccountIndexSmall { @@ -46,11 +49,46 @@ impl<'a, T: AccountInfoTrait> CpiAccountsSmall<'a, T> { self.fee_payer } - pub fn authority(&self) -> &'a T { - // PANICS: We are sure about the bounds of the slice. + pub fn authority(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::Authority as usize; self.accounts - .get(CompressionCpiAccountIndexSmall::Authority as usize) - .unwrap() + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn registered_program_pda(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::RegisteredProgramPda as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn account_compression_authority(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::AccountCompressionAuthority as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn sol_pool_pda(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::SolPoolPda as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn decompression_recipient(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::DecompressionRecipient as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn cpi_context(&self) -> Result<&'a T> { + let index = CompressionCpiAccountIndexSmall::CpiContext as usize; + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) } pub fn self_program_id(&self) -> T::Pubkey { @@ -79,8 +117,28 @@ impl<'a, T: AccountInfoTrait> CpiAccountsSmall<'a, T> { self.accounts } - pub fn tree_accounts(&self) -> &'a [T] { - &self.accounts[self.system_accounts_end_offset()..] + pub fn get_account_info(&self, index: usize) -> Result<&'a T> { + self.accounts + .get(index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) + } + + pub fn tree_accounts(&self) -> Result<&'a [T]> { + let system_offset = self.system_accounts_end_offset(); + self.accounts + .get(system_offset..) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds( + system_offset, + )) + } + + pub fn get_tree_account_info(&self, tree_index: usize) -> Result<&'a T> { + let tree_accounts = self.tree_accounts()?; + tree_accounts + .get(tree_index) + .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds( + self.system_accounts_end_offset() + tree_index, + )) } /// Create a vector of account info references diff --git a/sdk-libs/sdk-types/src/error.rs b/sdk-libs/sdk-types/src/error.rs index b64ada0bcf..6ce017258a 100644 --- a/sdk-libs/sdk-types/src/error.rs +++ b/sdk-libs/sdk-types/src/error.rs @@ -25,6 +25,8 @@ pub enum LightSdkTypesError { MetaCloseInputIsNone, #[error("Fewer accounts than system accounts")] FewerAccountsThanSystemAccounts, + #[error("CPI accounts index out of bounds: {0}")] + CpiAccountsIndexOutOfBounds(usize), #[error(transparent)] Hasher(#[from] HasherError), } @@ -41,7 +43,8 @@ impl From for u32 { LightSdkTypesError::MetaMutOutputIsNone => 14027, LightSdkTypesError::MetaCloseAddressIsNone => 14028, LightSdkTypesError::MetaCloseInputIsNone => 14029, - LightSdkTypesError::FewerAccountsThanSystemAccounts => 14030, + LightSdkTypesError::FewerAccountsThanSystemAccounts => 14017, + LightSdkTypesError::CpiAccountsIndexOutOfBounds(_) => 14031, LightSdkTypesError::Hasher(e) => e.into(), } } diff --git a/sdk-libs/sdk-types/src/instruction/tree_info.rs b/sdk-libs/sdk-types/src/instruction/tree_info.rs index 51169d0716..8cdcc7fed0 100644 --- a/sdk-libs/sdk-types/src/instruction/tree_info.rs +++ b/sdk-libs/sdk-types/src/instruction/tree_info.rs @@ -32,7 +32,9 @@ impl PackedAddressTreeInfo { pub fn get_tree_pubkey( &self, cpi_accounts: &CpiAccounts<'_, T>, - ) -> T::Pubkey { - cpi_accounts.tree_accounts()[self.address_merkle_tree_pubkey_index as usize].pubkey() + ) -> Result { + let account = + cpi_accounts.get_tree_account_info(self.address_merkle_tree_pubkey_index as usize)?; + Ok(account.pubkey()) } } diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index 2b06f8d2d5..d1bddcb78f 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -1,13 +1,14 @@ pub use light_sdk_types::CpiAccountsConfig; -use light_sdk_types::{ - CompressionCpiAccountIndex, CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN, -}; +use light_sdk_types::{CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN}; -use crate::{AccountInfo, AccountMeta, Pubkey}; +use crate::{ + error::{LightSdkError, Result}, + AccountInfo, AccountMeta, Pubkey, +}; pub type CpiAccounts<'c, 'info> = GenericCpiAccounts<'c, AccountInfo<'info>>; -pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { +pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Result> { let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); account_metas.push(AccountMeta { pubkey: *cpi_accounts.fee_payer().key, @@ -15,48 +16,48 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { is_writable: true, }); account_metas.push(AccountMeta { - pubkey: *cpi_accounts.authority().key, + pubkey: *cpi_accounts.authority()?.key, is_signer: true, is_writable: false, }); - let accounts = cpi_accounts.account_infos(); account_metas.push(AccountMeta { - pubkey: *accounts[CompressionCpiAccountIndex::RegisteredProgramPda as usize].key, + pubkey: *cpi_accounts.registered_program_pda()?.key, is_signer: false, is_writable: false, }); account_metas.push(AccountMeta { - pubkey: *accounts[CompressionCpiAccountIndex::NoopProgram as usize].key, + pubkey: *cpi_accounts.noop_program()?.key, is_signer: false, is_writable: false, }); account_metas.push(AccountMeta { - pubkey: *accounts[CompressionCpiAccountIndex::AccountCompressionAuthority as usize].key, + pubkey: *cpi_accounts.account_compression_authority()?.key, is_signer: false, is_writable: false, }); account_metas.push(AccountMeta { - pubkey: *accounts[CompressionCpiAccountIndex::AccountCompressionProgram as usize].key, + pubkey: *cpi_accounts.account_compression_program()?.key, is_signer: false, is_writable: false, }); account_metas.push(AccountMeta { - pubkey: *accounts[CompressionCpiAccountIndex::InvokingProgram as usize].key, + pubkey: *cpi_accounts.invoking_program()?.key, is_signer: false, is_writable: false, }); let mut current_index = 7; - let anchor_non_account_meta = AccountMeta { - pubkey: *cpi_accounts.light_system_program().key, + let anchor_none_account_meta = AccountMeta { + pubkey: *cpi_accounts.light_system_program()?.key, is_signer: false, is_writable: false, }; if !cpi_accounts.config().sol_pool_pda { - account_metas.push(anchor_non_account_meta.clone()); + account_metas.push(anchor_none_account_meta.clone()); } else { + let account = cpi_accounts.get_account_info(current_index)?; account_metas.push(AccountMeta { - pubkey: *accounts[current_index].key, + pubkey: *account.key, is_signer: false, is_writable: true, }); @@ -64,10 +65,11 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { } if !cpi_accounts.config().sol_compression_recipient { - account_metas.push(anchor_non_account_meta.clone()); + account_metas.push(anchor_none_account_meta.clone()); } else { + let account = cpi_accounts.get_account_info(current_index)?; account_metas.push(AccountMeta { - pubkey: *accounts[current_index].key, + pubkey: *account.key, is_signer: false, is_writable: true, }); @@ -82,21 +84,26 @@ pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Vec { current_index += 1; if !cpi_accounts.config().cpi_context { - account_metas.push(anchor_non_account_meta); + account_metas.push(anchor_none_account_meta); } else { + let account = cpi_accounts.get_account_info(current_index)?; account_metas.push(AccountMeta { - pubkey: *accounts[current_index].key, + pubkey: *account.key, is_signer: false, is_writable: true, }); current_index += 1; } - accounts[current_index..].iter().for_each(|acc| { + let tree_accounts = cpi_accounts + .account_infos() + .get(current_index..) + .ok_or(LightSdkError::CpiAccountsIndexOutOfBounds(current_index))?; + tree_accounts.iter().for_each(|acc| { account_metas.push(AccountMeta { pubkey: *acc.key, is_signer: false, is_writable: acc.is_writable, }); }); - account_metas + Ok(account_metas) } diff --git a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs index 6f030fee1b..c4e4f7c144 100644 --- a/sdk-libs/sdk/src/cpi/accounts_small_ix.rs +++ b/sdk-libs/sdk/src/cpi/accounts_small_ix.rs @@ -3,11 +3,11 @@ use light_sdk_types::{ PROGRAM_ACCOUNTS_LEN, }; -use crate::{AccountInfo, AccountMeta}; +use crate::{error::Result, AccountInfo, AccountMeta}; pub type CpiAccountsSmall<'c, 'info> = GenericCpiAccountsSmall<'c, AccountInfo<'info>>; -pub fn to_account_metas_small(cpi_accounts: CpiAccountsSmall<'_, '_>) -> Vec { +pub fn to_account_metas_small(cpi_accounts: CpiAccountsSmall<'_, '_>) -> Result> { // TODO: do a version with a const array instead of vector. let mut account_metas = Vec::with_capacity(1 + cpi_accounts.account_infos().len() - PROGRAM_ACCOUNTS_LEN); @@ -18,28 +18,29 @@ pub fn to_account_metas_small(cpi_accounts: CpiAccountsSmall<'_, '_>) -> Vec) -> Vec) -> Vec) -> Vec Result { - let owner = *cpi_accounts.invoking_program().key; + let owner = *cpi_accounts.invoking_program()?.key; let (input_compressed_accounts_with_merkle_context, output_compressed_accounts) = if let Some(account_infos) = cpi_inputs.account_infos.as_ref() { let mut input_compressed_accounts_with_merkle_context = @@ -114,7 +114,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_metas: Vec = to_account_metas(cpi_accounts); + let account_metas: Vec = to_account_metas(cpi_accounts)?; Ok(Instruction { program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, @@ -139,7 +139,7 @@ where let account_infos: Vec = account_info_refs.into_iter().cloned().collect(); let bump = light_system_accounts.bump(); - let account_metas: Vec = to_account_metas(light_system_accounts); + let account_metas: Vec = to_account_metas(light_system_accounts)?; let instruction = Instruction { program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, diff --git a/sdk-libs/sdk/src/cpi/mod.rs b/sdk-libs/sdk/src/cpi/mod.rs index 6de32b4c1e..e1329328df 100644 --- a/sdk-libs/sdk/src/cpi/mod.rs +++ b/sdk-libs/sdk/src/cpi/mod.rs @@ -17,9 +17,7 @@ //! //! let (address, address_seed) = derive_address( //! &[b"compressed", name.as_bytes()], -//! &light_cpi_accounts.tree_accounts() -//! [address_tree_info.address_merkle_tree_pubkey_index as usize] -//! .key(), +//! &address_tree_info.get_tree_pubkey(&light_cpi_accounts)?, //! &crate::ID, //! ); //! let new_address_params = address_tree_info.into_new_address_params_packed(address_seed); diff --git a/sdk-libs/sdk/src/error.rs b/sdk-libs/sdk/src/error.rs index bbd9201f68..51630f80c9 100644 --- a/sdk-libs/sdk/src/error.rs +++ b/sdk-libs/sdk/src/error.rs @@ -67,6 +67,8 @@ pub enum LightSdkError { MetaCloseAddressIsNone, #[error("Input is none during meta close")] MetaCloseInputIsNone, + #[error("CPI accounts index out of bounds: {0}")] + CpiAccountsIndexOutOfBounds(usize), #[error(transparent)] Hasher(#[from] HasherError), #[error(transparent)] @@ -100,6 +102,9 @@ impl From for LightSdkError { LightSdkTypesError::FewerAccountsThanSystemAccounts => { LightSdkError::FewerAccountsThanSystemAccounts } + LightSdkTypesError::CpiAccountsIndexOutOfBounds(index) => { + LightSdkError::CpiAccountsIndexOutOfBounds(index) + } LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), } } @@ -137,6 +142,7 @@ impl From for u32 { LightSdkError::MetaMutOutputIsNone => 14027, LightSdkError::MetaCloseAddressIsNone => 14028, LightSdkError::MetaCloseInputIsNone => 14029, + LightSdkError::CpiAccountsIndexOutOfBounds(_) => 14031, LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32, diff --git a/sdk-libs/sdk/src/lib.rs b/sdk-libs/sdk/src/lib.rs index a1e3073d5b..b8eef1be97 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -57,9 +57,7 @@ //! //! let (address, address_seed) = derive_address( //! &[b"counter", ctx.accounts.fee_payer.key().as_ref()], -//! &light_cpi_accounts.tree_accounts() -//! [address_tree_info.address_merkle_tree_pubkey_index as usize] -//! .key(), +//! &address_tree_info.get_tree_pubkey(&light_cpi_accounts)?, //! &crate::ID, //! ); //! let new_address_params = address_tree_info From a52b2a3e84986d0e13af10bdb30763c199cc4417 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 11 Jun 2025 23:57:47 +0100 Subject: [PATCH 20/21] cleanup --- program-tests/sdk-pinocchio-test/tests/test.rs | 5 +---- sdk-libs/client/src/rpc/client.rs | 8 +------- sdk-libs/sdk-pinocchio/src/cpi/invoke.rs | 7 +------ 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/program-tests/sdk-pinocchio-test/tests/test.rs b/program-tests/sdk-pinocchio-test/tests/test.rs index 2276cd0c37..0b18f9c436 100644 --- a/program-tests/sdk-pinocchio-test/tests/test.rs +++ b/program-tests/sdk-pinocchio-test/tests/test.rs @@ -43,16 +43,13 @@ async fn test_sdk_test() { // ); // Batched trees let address_seed = hashv_to_bn254_field_size_be(&[b"compressed", account_data.as_slice()]); - println!("seed {:?}", address_seed); let address = derive_address( &address_seed, &address_tree_pubkey.to_bytes(), &sdk_pinocchio_test::ID, ); - println!("address {:?}", address); - println!("address tree pubkey: {:?}", address_tree_pubkey.to_bytes()); + let output_queue = rpc.get_random_state_tree_info().unwrap().queue; - println!("output_queue tree pubkey: {:?}", output_queue.to_bytes()); create_pda( &payer, diff --git a/sdk-libs/client/src/rpc/client.rs b/sdk-libs/client/src/rpc/client.rs index bd4b8b49e7..4a95783607 100644 --- a/sdk-libs/client/src/rpc/client.rs +++ b/sdk-libs/client/src/rpc/client.rs @@ -694,14 +694,8 @@ impl Rpc for LightClient { /// Gets a random active state tree. /// State trees are cached and have to be fetched or set. fn get_random_state_tree_info(&self) -> Result { - if self.state_merkle_trees.is_empty() { - return Err(RpcError::NoStateTreesAvailable); - } - - use rand::Rng; let mut rng = rand::thread_rng(); - - Ok(self.state_merkle_trees[rng.gen_range(0..self.state_merkle_trees.len())]) + select_state_tree_info(&mut rng, &self.state_merkle_trees) } fn get_address_tree_v1(&self) -> TreeInfo { diff --git a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs index 87c762311c..150413680f 100644 --- a/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs +++ b/sdk-libs/sdk-pinocchio/src/cpi/invoke.rs @@ -11,7 +11,7 @@ use light_compressed_account::{ }, }; use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID}; -use pinocchio::{cpi::slice_invoke_signed, log::sol_log_compute_units, msg, pubkey::Pubkey}; +use pinocchio::{cpi::slice_invoke_signed, msg, pubkey::Pubkey}; use crate::{ cpi::CpiAccounts, @@ -174,27 +174,22 @@ pub fn light_system_program_instruction_invoke_cpi( // Create instruction with owned data and immediately invoke it use pinocchio::instruction::{Instruction, Seed, Signer}; - sol_log_compute_units(); // Use the precomputed CPI signer and bump from the config let bump = cpi_accounts.bump(); - sol_log_compute_units(); let bump_seed = [bump]; let seed_array = [ Seed::from(CPI_AUTHORITY_PDA_SEED), Seed::from(bump_seed.as_slice()), ]; let signer = Signer::from(&seed_array); - sol_log_compute_units(); let instruction = Instruction { program_id: &Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID), accounts: &account_metas, data: &data, }; - sol_log_compute_units(); let account_infos = crate::cpi::accounts::to_account_infos_for_invoke(cpi_accounts)?; - sol_log_compute_units(); match slice_invoke_signed(&instruction, &account_infos, &[signer]) { Ok(()) => {} From 3999288778e0e1b0ac907bbe033b119d102efef8 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Thu, 12 Jun 2025 00:25:07 +0100 Subject: [PATCH 21/21] refactor: migrate light sdk errors to 16k namespace --- sdk-libs/sdk-pinocchio/src/error.rs | 60 ++++++++++++++--------------- sdk-libs/sdk/src/error.rs | 60 ++++++++++++++--------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/sdk-libs/sdk-pinocchio/src/error.rs b/sdk-libs/sdk-pinocchio/src/error.rs index 5ae8ad1ce7..89663d599b 100644 --- a/sdk-libs/sdk-pinocchio/src/error.rs +++ b/sdk-libs/sdk-pinocchio/src/error.rs @@ -118,36 +118,36 @@ impl From for LightSdkError { impl From for u32 { fn from(e: LightSdkError) -> Self { match e { - LightSdkError::ConstraintViolation => 14001, - LightSdkError::InvalidLightSystemProgram => 14002, - LightSdkError::ExpectedAccounts => 14003, - LightSdkError::ExpectedAddressTreeInfo => 14004, - LightSdkError::ExpectedAddressRootIndex => 14005, - LightSdkError::ExpectedData => 14006, - LightSdkError::ExpectedDiscriminator => 14007, - LightSdkError::ExpectedHash => 14008, - LightSdkError::ExpectedLightSystemAccount(_) => 14009, - LightSdkError::ExpectedMerkleContext => 14010, - LightSdkError::ExpectedRootIndex => 14011, - LightSdkError::TransferFromNoInput => 14012, - LightSdkError::TransferFromNoLamports => 14013, - LightSdkError::TransferFromInsufficientLamports => 14014, - LightSdkError::TransferIntegerOverflow => 14015, - LightSdkError::Borsh => 14016, - LightSdkError::FewerAccountsThanSystemAccounts => 14017, - LightSdkError::InvalidCpiSignerAccount => 14018, - LightSdkError::MissingField(_) => 14019, - LightSdkError::OutputStateTreeIndexIsNone => 14020, - LightSdkError::InitAddressIsNone => 14021, - LightSdkError::InitWithAddressIsNone => 14022, - LightSdkError::InitWithAddressOutputIsNone => 14023, - LightSdkError::MetaMutAddressIsNone => 14024, - LightSdkError::MetaMutInputIsNone => 14025, - LightSdkError::MetaMutOutputLamportsIsNone => 14026, - LightSdkError::MetaMutOutputIsNone => 14027, - LightSdkError::MetaCloseAddressIsNone => 14028, - LightSdkError::MetaCloseInputIsNone => 14029, - LightSdkError::CpiAccountsIndexOutOfBounds(_) => 14031, + LightSdkError::ConstraintViolation => 16001, + LightSdkError::InvalidLightSystemProgram => 16002, + LightSdkError::ExpectedAccounts => 16003, + LightSdkError::ExpectedAddressTreeInfo => 16004, + LightSdkError::ExpectedAddressRootIndex => 16005, + LightSdkError::ExpectedData => 16006, + LightSdkError::ExpectedDiscriminator => 16007, + LightSdkError::ExpectedHash => 16008, + LightSdkError::ExpectedLightSystemAccount(_) => 16009, + LightSdkError::ExpectedMerkleContext => 16010, + LightSdkError::ExpectedRootIndex => 16011, + LightSdkError::TransferFromNoInput => 16012, + LightSdkError::TransferFromNoLamports => 16013, + LightSdkError::TransferFromInsufficientLamports => 16014, + LightSdkError::TransferIntegerOverflow => 16015, + LightSdkError::Borsh => 16016, + LightSdkError::FewerAccountsThanSystemAccounts => 16017, + LightSdkError::InvalidCpiSignerAccount => 16018, + LightSdkError::MissingField(_) => 16019, + LightSdkError::OutputStateTreeIndexIsNone => 16020, + LightSdkError::InitAddressIsNone => 16021, + LightSdkError::InitWithAddressIsNone => 16022, + LightSdkError::InitWithAddressOutputIsNone => 16023, + LightSdkError::MetaMutAddressIsNone => 16024, + LightSdkError::MetaMutInputIsNone => 16025, + LightSdkError::MetaMutOutputLamportsIsNone => 16026, + LightSdkError::MetaMutOutputIsNone => 16027, + LightSdkError::MetaCloseAddressIsNone => 16028, + LightSdkError::MetaCloseInputIsNone => 16029, + LightSdkError::CpiAccountsIndexOutOfBounds(_) => 16031, LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32, diff --git a/sdk-libs/sdk/src/error.rs b/sdk-libs/sdk/src/error.rs index 51630f80c9..51c6ae3e5b 100644 --- a/sdk-libs/sdk/src/error.rs +++ b/sdk-libs/sdk/src/error.rs @@ -113,36 +113,36 @@ impl From for LightSdkError { impl From for u32 { fn from(e: LightSdkError) -> Self { match e { - LightSdkError::ConstraintViolation => 14001, - LightSdkError::InvalidLightSystemProgram => 14002, - LightSdkError::ExpectedAccounts => 14003, - LightSdkError::ExpectedAddressTreeInfo => 14004, - LightSdkError::ExpectedAddressRootIndex => 14005, - LightSdkError::ExpectedData => 14006, - LightSdkError::ExpectedDiscriminator => 14007, - LightSdkError::ExpectedHash => 14008, - LightSdkError::ExpectedLightSystemAccount(_) => 14009, - LightSdkError::ExpectedMerkleContext => 14010, - LightSdkError::ExpectedRootIndex => 14011, - LightSdkError::TransferFromNoInput => 14012, - LightSdkError::TransferFromNoLamports => 14013, - LightSdkError::TransferFromInsufficientLamports => 14014, - LightSdkError::TransferIntegerOverflow => 14015, - LightSdkError::Borsh => 14016, - LightSdkError::FewerAccountsThanSystemAccounts => 14017, - LightSdkError::InvalidCpiSignerAccount => 14018, - LightSdkError::MissingField(_) => 14019, - LightSdkError::OutputStateTreeIndexIsNone => 14020, - LightSdkError::InitAddressIsNone => 14021, - LightSdkError::InitWithAddressIsNone => 14022, - LightSdkError::InitWithAddressOutputIsNone => 14023, - LightSdkError::MetaMutAddressIsNone => 14024, - LightSdkError::MetaMutInputIsNone => 14025, - LightSdkError::MetaMutOutputLamportsIsNone => 14026, - LightSdkError::MetaMutOutputIsNone => 14027, - LightSdkError::MetaCloseAddressIsNone => 14028, - LightSdkError::MetaCloseInputIsNone => 14029, - LightSdkError::CpiAccountsIndexOutOfBounds(_) => 14031, + LightSdkError::ConstraintViolation => 16001, + LightSdkError::InvalidLightSystemProgram => 16002, + LightSdkError::ExpectedAccounts => 16003, + LightSdkError::ExpectedAddressTreeInfo => 16004, + LightSdkError::ExpectedAddressRootIndex => 16005, + LightSdkError::ExpectedData => 16006, + LightSdkError::ExpectedDiscriminator => 16007, + LightSdkError::ExpectedHash => 16008, + LightSdkError::ExpectedLightSystemAccount(_) => 16009, + LightSdkError::ExpectedMerkleContext => 16010, + LightSdkError::ExpectedRootIndex => 16011, + LightSdkError::TransferFromNoInput => 16012, + LightSdkError::TransferFromNoLamports => 16013, + LightSdkError::TransferFromInsufficientLamports => 16014, + LightSdkError::TransferIntegerOverflow => 16015, + LightSdkError::Borsh => 16016, + LightSdkError::FewerAccountsThanSystemAccounts => 16017, + LightSdkError::InvalidCpiSignerAccount => 16018, + LightSdkError::MissingField(_) => 16019, + LightSdkError::OutputStateTreeIndexIsNone => 16020, + LightSdkError::InitAddressIsNone => 16021, + LightSdkError::InitWithAddressIsNone => 16022, + LightSdkError::InitWithAddressOutputIsNone => 16023, + LightSdkError::MetaMutAddressIsNone => 16024, + LightSdkError::MetaMutInputIsNone => 16025, + LightSdkError::MetaMutOutputLamportsIsNone => 16026, + LightSdkError::MetaMutOutputIsNone => 16027, + LightSdkError::MetaCloseAddressIsNone => 16028, + LightSdkError::MetaCloseInputIsNone => 16029, + LightSdkError::CpiAccountsIndexOutOfBounds(_) => 16031, LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32,