From 9b5544710e51606f43fca4a873fe8d34e778a9ea Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 1 Sep 2025 12:53:32 +0100 Subject: [PATCH 1/6] stash --- .github/workflows/light-examples-tests.yml | 2 +- .../workflows/light-system-programs-tests.yml | 2 +- .github/workflows/rust.yml | 3 +- Cargo.lock | 641 +++++---- Cargo.toml | 25 +- program-libs/heap/src/lib.rs | 8 + scripts/devenv.sh | 1 + scripts/format.sh | 2 +- sdk-libs/program-test/Cargo.toml | 7 + .../program-test/src/indexer/test_indexer.rs | 142 +- sdk-libs/program-test/src/lib.rs | 1 + sdk-libs/program-test/src/logging/config.rs | 109 ++ sdk-libs/program-test/src/logging/decoder.rs | 979 +++++++++++++ .../program-test/src/logging/decoder.rs:70:15 | 0 .../src/logging/decoder.rs:946:15 | 0 .../program-test/src/logging/formatter.rs | 1235 +++++++++++++++++ .../src/logging/formatter.rs:1218:19 | 0 sdk-libs/program-test/src/logging/mod.rs | 201 +++ sdk-libs/program-test/src/logging/types.rs | 480 +++++++ .../program-test/src/logging/types.rs:473:15 | 0 .../program-test/src/program_test/config.rs | 36 +- .../src/program_test/light_program_test.rs | 9 +- sdk-libs/program-test/src/program_test/rpc.rs | 136 +- .../src/utils/setup_light_programs.rs | 2 +- sdk-tests/client-test/Cargo.toml | 5 +- .../client-test/tests/light_program_test.rs | 1 - sdk-tests/sdk-anchor-test/package.json | 2 +- .../programs/sdk-anchor-test/tests/test.rs | 6 +- .../{sdk-test => sdk-native-test}/Cargo.toml | 4 +- .../{sdk-test => sdk-native-test}/Xargo.toml | 0 .../src/create_pda.rs | 0 .../{sdk-test => sdk-native-test}/src/lib.rs | 0 .../src/update_pda.rs | 0 .../tests/test.rs | 31 +- sdk-tests/sdk-pinocchio-test/tests/test.rs | 16 +- 35 files changed, 3589 insertions(+), 497 deletions(-) create mode 100644 sdk-libs/program-test/src/logging/config.rs create mode 100644 sdk-libs/program-test/src/logging/decoder.rs create mode 100644 sdk-libs/program-test/src/logging/decoder.rs:70:15 create mode 100644 sdk-libs/program-test/src/logging/decoder.rs:946:15 create mode 100644 sdk-libs/program-test/src/logging/formatter.rs create mode 100644 sdk-libs/program-test/src/logging/formatter.rs:1218:19 create mode 100644 sdk-libs/program-test/src/logging/mod.rs create mode 100644 sdk-libs/program-test/src/logging/types.rs create mode 100644 sdk-libs/program-test/src/logging/types.rs:473:15 rename sdk-tests/{sdk-test => sdk-native-test}/Cargo.toml (95%) rename sdk-tests/{sdk-test => sdk-native-test}/Xargo.toml (100%) rename sdk-tests/{sdk-test => sdk-native-test}/src/create_pda.rs (100%) rename sdk-tests/{sdk-test => sdk-native-test}/src/lib.rs (100%) rename sdk-tests/{sdk-test => sdk-native-test}/src/update_pda.rs (100%) rename sdk-tests/{sdk-test => sdk-native-test}/tests/test.rs (88%) diff --git a/.github/workflows/light-examples-tests.yml b/.github/workflows/light-examples-tests.yml index ea79b3fd81..fada8d6fca 100644 --- a/.github/workflows/light-examples-tests.yml +++ b/.github/workflows/light-examples-tests.yml @@ -48,7 +48,7 @@ jobs: matrix: include: - program: sdk-test-program - sub-tests: '["cargo-test-sbf -p sdk-test"]' + sub-tests: '["cargo-test-sbf -p sdk-native-test"]' - program: sdk-anchor-test-program sub-tests: '["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk-pinocchio-test"]' diff --git a/.github/workflows/light-system-programs-tests.yml b/.github/workflows/light-system-programs-tests.yml index 0be5d51a2b..a106c91af9 100644 --- a/.github/workflows/light-system-programs-tests.yml +++ b/.github/workflows/light-system-programs-tests.yml @@ -64,7 +64,7 @@ jobs: - program: light-compressed-token sub-tests: '["cargo-test-sbf -p compressed-token-test"]' - program: system-cpi-test - sub-tests: '["cargo-test-sbf -p system-cpi-test"]' + sub-tests: '["cargo-test-sbf -p system-cpi-test", "cargo test -p light-system-program-pinocchio --all-features"]' - program: system-cpi-test-v2-event sub-tests: '["cargo-test-sbf -p system-cpi-v2-test -- event::parse"]' - program: system-cpi-test-v2-functional diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dc0842f74d..737037a766 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -50,6 +50,7 @@ jobs: cargo test -p aligned-sized cargo test -p light-hasher --all-features cargo test -p light-compressed-account --all-features + cargo test -p light-compressed-account --features new-unique cargo test -p light-account-checks --all-features cargo test -p light-verifier --all-features cargo test -p light-merkle-tree-metadata --all-features @@ -71,7 +72,7 @@ jobs: cargo test -p light-sdk cargo test -p light-program-test cargo test -p light-client - cargo test-sbf -p client-test + cargo test -p client-test cargo test -p light-sparse-merkle-tree cargo test -p light-batched-merkle-tree --features test-only -- --skip test_simulate_transactions --skip test_e2e diff --git a/Cargo.lock b/Cargo.lock index dda4686d71..a1ca7eac59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,7 +150,7 @@ version = "1.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -565,7 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -591,7 +591,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -666,7 +666,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -760,14 +760,13 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.27" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" +checksum = "977eb15ea9efd848bb8a4a1a2500347ed7f0bf794edf0dc3ddcf439f43d36b23" dependencies = [ - "brotli", - "flate2", + "compression-codecs", + "compression-core", "futures-core", - "memchr", "pin-project-lite", "tokio", ] @@ -802,7 +801,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -813,7 +812,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -907,9 +906,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -1001,7 +1000,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1028,9 +1027,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.1" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1095,7 +1094,7 @@ checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1132,9 +1131,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.32" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -1149,9 +1148,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -1167,7 +1166,7 @@ checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1212,9 +1211,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.45" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -1222,9 +1221,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -1241,7 +1240,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1349,6 +1348,24 @@ dependencies = [ "tokio", ] +[[package]] +name = "compression-codecs" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1563,7 +1580,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1587,7 +1604,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1598,7 +1615,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1650,9 +1667,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -1757,7 +1774,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1780,7 +1797,7 @@ checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1872,7 +1889,7 @@ dependencies = [ "enum-ordinalize 4.3.0", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1913,7 +1930,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1926,7 +1943,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -1946,7 +1963,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -2030,14 +2047,14 @@ dependencies = [ [[package]] name = "fastbloom" -version = "0.9.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +checksum = "18c1ddb9231d8554c2d6bdf4cfaabf0c59251658c68b6c95cd52dd0c513a912a" dependencies = [ "getrandom 0.3.3", + "libm", "rand 0.9.2", "siphasher 1.0.1", - "wide", ] [[package]] @@ -2116,7 +2133,7 @@ dependencies = [ "bb8", "borsh 0.10.4", "bs58", - "clap 4.5.45", + "clap 4.5.46", "create-address-test-program", "dashmap 6.1.0", "dotenvy", @@ -2153,7 +2170,7 @@ dependencies = [ "solana-program", "solana-sdk", "solana-transaction-status", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tracing", "tracing-appender", @@ -2193,16 +2210,16 @@ dependencies = [ "serde", "serde_json", "solana-sdk", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tracing", ] [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2269,7 +2286,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -2364,7 +2381,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", "wasm-bindgen", ] @@ -2450,7 +2467,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.10.0", + "indexmap 2.11.0", "slab", "tokio", "tokio-util 0.7.16", @@ -2469,7 +2486,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.10.0", + "indexmap 2.11.0", "slab", "tokio", "tokio-util 0.7.16", @@ -2724,19 +2741,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2763,7 +2782,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "rustls 0.23.31", "rustls-pki-types", @@ -2794,7 +2813,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "native-tls", "tokio", @@ -2815,7 +2834,7 @@ dependencies = [ "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.7.0", "ipnet", "libc", "percent-encoding", @@ -2946,9 +2965,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2978,9 +2997,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown 0.15.5", @@ -3011,11 +3030,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -3105,7 +3124,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -3132,9 +3151,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -3186,13 +3205,19 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "libredox" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", ] @@ -3255,7 +3280,7 @@ dependencies = [ "solana-program-error", "solana-pubkey", "solana-sysvar", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3283,7 +3308,7 @@ dependencies = [ "solana-program-error", "solana-pubkey", "solana-sysvar", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "zerocopy", ] @@ -3299,7 +3324,7 @@ dependencies = [ "rand 0.8.5", "solana-nostd-keccak", "solana-program-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3354,7 +3379,7 @@ dependencies = [ "solana-transaction", "solana-transaction-error", "solana-transaction-status-client-types", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -3377,7 +3402,7 @@ dependencies = [ "rand 0.8.5", "solana-program-error", "solana-pubkey", - "thiserror 2.0.14", + "thiserror 2.0.16", "zerocopy", ] @@ -3419,7 +3444,7 @@ dependencies = [ "pinocchio", "rand 0.8.5", "solana-program-error", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -3434,7 +3459,7 @@ dependencies = [ "num-traits", "rand 0.8.5", "solana-program-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3454,7 +3479,7 @@ dependencies = [ "solana-nostd-keccak", "solana-program-error", "solana-pubkey", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3472,7 +3497,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "rand 0.8.5", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3489,7 +3514,7 @@ dependencies = [ "pinocchio", "rand 0.8.5", "solana-program-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3499,7 +3524,7 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -3514,7 +3539,7 @@ dependencies = [ "solana-msg", "solana-program-error", "solana-sysvar", - "thiserror 2.0.14", + "thiserror 2.0.16", "zerocopy", ] @@ -3527,7 +3552,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "rand 0.8.5", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3562,7 +3587,9 @@ dependencies = [ "anchor-lang", "async-trait", "borsh 0.10.4", + "bs58", "bytemuck", + "chrono", "light-batched-merkle-tree", "light-client", "light-compressed-account", @@ -3576,6 +3603,7 @@ dependencies = [ "light-prover-client", "light-registry", "light-sdk", + "light-sdk-types", "litesvm", "log", "num-bigint 0.4.6", @@ -3583,6 +3611,8 @@ dependencies = [ "photon-api", "rand 0.8.5", "reqwest 0.12.23", + "serde", + "serde_json", "solana-account", "solana-banks-client", "solana-compute-budget", @@ -3591,7 +3621,9 @@ dependencies = [ "solana-rpc-client-api", "solana-sdk", "solana-transaction", + "solana-transaction-status", "solana-transaction-status-client-types", + "tabled", "tokio", ] @@ -3615,7 +3647,7 @@ dependencies = [ "serde_json", "serial_test", "solana-bn254", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -3654,7 +3686,7 @@ dependencies = [ "solana-msg", "solana-program-error", "solana-pubkey", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3671,7 +3703,7 @@ dependencies = [ "proc-macro2", "quote", "solana-pubkey", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -3688,7 +3720,7 @@ dependencies = [ "light-zero-copy", "pinocchio", "solana-pubkey", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3704,7 +3736,7 @@ dependencies = [ "light-zero-copy", "solana-msg", "solana-pubkey", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3717,7 +3749,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "rand 0.8.5", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3754,7 +3786,7 @@ dependencies = [ "rand 0.8.5", "solana-pubkey", "solana-security-txt", - "thiserror 2.0.14", + "thiserror 2.0.16", "zerocopy", ] @@ -3793,7 +3825,7 @@ dependencies = [ "solana-sdk", "spl-token", "spl-token-2022 7.0.0", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3805,7 +3837,7 @@ dependencies = [ "pinocchio", "solana-msg", "solana-program-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3830,7 +3862,7 @@ dependencies = [ "proc-macro2", "quote", "rand 0.8.5", - "syn 2.0.105", + "syn 2.0.106", "trybuild", "zerocopy", ] @@ -3855,7 +3887,7 @@ checksum = "bb7e5f4462f34439adcfcab58099bc7a89c67a17f8240b84a993b8b705c1becb" dependencies = [ "ansi_term", "bincode", - "indexmap 2.10.0", + "indexmap 2.11.0", "itertools 0.14.0", "log", "solana-account", @@ -3907,7 +3939,7 @@ dependencies = [ "solana-transaction-context", "solana-transaction-error", "solana-vote-program", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -3934,11 +3966,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -4060,7 +4092,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "libc", @@ -4091,12 +4123,11 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4160,7 +4191,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4233,7 +4264,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4284,7 +4315,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "foreign-types", "libc", @@ -4301,7 +4332,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4347,12 +4378,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "papergrid" version = "0.17.0" @@ -4428,9 +4453,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "percentage" @@ -4471,7 +4496,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4547,9 +4572,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -4577,12 +4602,12 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "prettyplease" -version = "0.2.36" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4622,14 +4647,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.97" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -4672,7 +4697,7 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -4692,9 +4717,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -4703,8 +4728,8 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls 0.23.31", - "socket2 0.5.10", - "thiserror 2.0.14", + "socket2 0.6.0", + "thiserror 2.0.16", "tokio", "tracing", "web-time", @@ -4712,9 +4737,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "fastbloom", @@ -4727,7 +4752,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "slab", - "thiserror 2.0.14", + "thiserror 2.0.16", "tinyvec", "tracing", "web-time", @@ -4735,16 +4760,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4874,7 +4899,7 @@ version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -4903,7 +4928,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -4925,7 +4950,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -4945,52 +4970,37 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "registry-test" @@ -5076,7 +5086,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-rustls 0.27.7", "hyper-tls 0.6.0", "hyper-util", @@ -5199,7 +5209,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys", @@ -5265,9 +5275,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", @@ -5280,7 +5290,7 @@ dependencies = [ "rustls-webpki 0.103.4", "security-framework 3.3.0", "security-framework-sys", - "webpki-root-certs 0.26.11", + "webpki-root-certs", "windows-sys 0.59.0", ] @@ -5323,15 +5333,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "same-file" version = "1.0.6" @@ -5343,9 +5344,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" dependencies = [ "sdd", ] @@ -5429,7 +5430,7 @@ dependencies = [ ] [[package]] -name = "sdk-pinocchio-test" +name = "sdk-native-test" version = "1.0.0" dependencies = [ "borsh 0.10.4", @@ -5438,15 +5439,15 @@ dependencies = [ "light-macros", "light-program-test", "light-sdk", - "light-sdk-pinocchio", "light-sdk-types", - "pinocchio", + "light-zero-copy", + "solana-program", "solana-sdk", "tokio", ] [[package]] -name = "sdk-test" +name = "sdk-pinocchio-test" version = "1.0.0" dependencies = [ "borsh 0.10.4", @@ -5455,9 +5456,9 @@ dependencies = [ "light-macros", "light-program-test", "light-sdk", + "light-sdk-pinocchio", "light-sdk-types", - "light-zero-copy", - "solana-program", + "pinocchio", "solana-sdk", "tokio", ] @@ -5468,7 +5469,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5481,7 +5482,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5539,14 +5540,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -5594,7 +5595,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.11.0", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -5613,7 +5614,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -5622,7 +5623,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.0", "itoa", "ryu", "serde", @@ -5651,7 +5652,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -5842,7 +5843,7 @@ dependencies = [ "spl-token-2022 7.0.0", "spl-token-group-interface", "spl-token-metadata-interface", - "thiserror 2.0.14", + "thiserror 2.0.16", "zstd", ] @@ -5864,9 +5865,9 @@ dependencies = [ [[package]] name = "solana-account-info" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c17d606a298a205fae325489fbed88ee6dc4463c111672172327e741c8905d" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" dependencies = [ "bincode", "serde", @@ -5914,7 +5915,7 @@ dependencies = [ "solana-pubkey", "solana-system-interface", "solana-transaction-context", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -5938,7 +5939,7 @@ dependencies = [ "solana-program", "solana-sdk", "tarpc", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tokio-serde", ] @@ -5991,9 +5992,9 @@ dependencies = [ [[package]] name = "solana-bn254" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9abc69625158faaab02347370b91c0d8e0fe347bf9287239f0fbe8f5864d91da" +checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -6001,7 +6002,7 @@ dependencies = [ "ark-serialize 0.4.2", "bytemuck", "solana-define-syscall", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6017,8 +6018,7 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" version = "2.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6931e8893b48e3a1c8124938f580fff857d84895582578cc7dbf100dd08d2c8f" +source = "git+https://github.com/Lightprotocol/agave?rev=580e29f03e4176a4a5525abc188a948c6595c47f#580e29f03e4176a4a5525abc188a948c6595c47f" dependencies = [ "bincode", "libsecp256k1", @@ -6060,7 +6060,7 @@ dependencies = [ "solana-timings", "solana-transaction-context", "solana-type-overrides", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6131,7 +6131,7 @@ dependencies = [ "solana-seed-phrase", "solana-signature", "solana-signer", - "thiserror 2.0.14", + "thiserror 2.0.16", "tiny-bip39", "uriparse", "url", @@ -6207,7 +6207,7 @@ dependencies = [ "dashmap 5.5.3", "futures", "futures-util", - "indexmap 2.10.0", + "indexmap 2.11.0", "indicatif", "log", "quinn", @@ -6238,7 +6238,7 @@ dependencies = [ "solana-transaction", "solana-transaction-error", "solana-udp-client", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -6325,7 +6325,7 @@ dependencies = [ "solana-sdk-ids", "solana-svm-transaction", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6385,7 +6385,7 @@ dependencies = [ "bincode", "crossbeam-channel", "futures-util", - "indexmap 2.10.0", + "indexmap 2.11.0", "log", "rand 0.8.5", "rayon", @@ -6395,7 +6395,7 @@ dependencies = [ "solana-net-utils", "solana-time-utils", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -6424,7 +6424,7 @@ dependencies = [ "curve25519-dalek 4.1.3", "solana-define-syscall", "subtle", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6534,7 +6534,7 @@ dependencies = [ "solana-pubkey", "solana-sdk-ids", "solana-system-interface", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6707,7 +6707,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "solana-account-info", "solana-instruction", "solana-program-error", @@ -6898,7 +6898,7 @@ dependencies = [ "solana-cluster-type", "solana-sha256-hasher", "solana-time-utils", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -6996,7 +6996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004f2d2daf407b3ec1a1ca5ec34b3ccdfd6866dd2d3c7d0715004a96e4b6d127" dependencies = [ "bincode", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg_eval", "serde", "serde_derive", @@ -7047,14 +7047,14 @@ dependencies = [ [[package]] name = "solana-poseidon" -version = "2.2.4" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2908b48b3828bc04b752d1ff36122f5a06de043258da88df5f8ce64791d208" +checksum = "230110ef64d44bc5f261249e45dde64cc176598c5c05728a118b8979e0c865ff" dependencies = [ "ark-bn254 0.4.0", "light-poseidon 0.2.0", "solana-define-syscall", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -7069,9 +7069,9 @@ dependencies = [ [[package]] name = "solana-precompiles" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a460ab805ec063802105b463ecb5eb02c3ffe469e67a967eea8a6e778e0bc06" +checksum = "36e92768a57c652edb0f5d1b30a7d0bc64192139c517967c18600debe9ae3832" dependencies = [ "lazy_static", "solana-ed25519-program", @@ -7171,7 +7171,7 @@ dependencies = [ "solana-sysvar", "solana-sysvar-id", "solana-vote-interface", - "thiserror 2.0.14", + "thiserror 2.0.16", "wasm-bindgen", ] @@ -7205,11 +7205,10 @@ dependencies = [ [[package]] name = "solana-program-memory" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0268f6c89825fb634a34bd0c3b8fdaeaecfc3728be1d622a8ee6dd577b60d4" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" dependencies = [ - "num-traits", "solana-define-syscall", ] @@ -7231,8 +7230,7 @@ dependencies = [ [[package]] name = "solana-program-runtime" version = "2.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce0a9acc6049c2ae8a2a2dd0b63269ab1a6d8fab4dead1aae75a9bcdd4aa6f05" +source = "git+https://github.com/Lightprotocol/agave?rev=580e29f03e4176a4a5525abc188a948c6595c47f#580e29f03e4176a4a5525abc188a948c6595c47f" dependencies = [ "base64 0.22.1", "bincode", @@ -7266,7 +7264,7 @@ dependencies = [ "solana-timings", "solana-transaction-context", "solana-type-overrides", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -7315,7 +7313,7 @@ dependencies = [ "solana-pubkey", "solana-rpc-client-api", "solana-signature", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tokio-stream", "tokio-tungstenite 0.20.1", @@ -7350,7 +7348,7 @@ dependencies = [ "solana-streamer", "solana-tls-utils", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -7393,7 +7391,7 @@ dependencies = [ "solana-pubkey", "solana-signature", "solana-signer", - "thiserror 2.0.14", + "thiserror 2.0.16", "uriparse", ] @@ -7525,7 +7523,7 @@ dependencies = [ "solana-transaction-error", "solana-transaction-status-client-types", "solana-version", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -7542,7 +7540,7 @@ dependencies = [ "solana-pubkey", "solana-rpc-client", "solana-sdk-ids", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -7635,7 +7633,7 @@ dependencies = [ "solana-transaction-context", "solana-transaction-error", "solana-validator-exit", - "thiserror 2.0.14", + "thiserror 2.0.16", "wasm-bindgen", ] @@ -7657,7 +7655,7 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -7687,7 +7685,7 @@ dependencies = [ "borsh 1.5.7", "libsecp256k1", "solana-define-syscall", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -7916,7 +7914,7 @@ dependencies = [ "futures-util", "governor 0.6.3", "histogram", - "indexmap 2.10.0", + "indexmap 2.11.0", "itertools 0.12.1", "libc", "log", @@ -7943,7 +7941,7 @@ dependencies = [ "solana-tls-utils", "solana-transaction-error", "solana-transaction-metrics-tracker", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tokio-util 0.7.16", "x509-parser", @@ -8104,9 +8102,9 @@ checksum = "6af261afb0e8c39252a04d026e3ea9c405342b08c871a2ad8aa5448e068c784c" [[package]] name = "solana-timings" -version = "2.2.4" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224f93327d9d3178a30cd6c057e1ac6ca85e95287dd7355064dfa6b9c49f5671" +checksum = "1939a33d02905beaa4d657e9ade9cfcac6f9676a4a6178829a617503d45ab3b8" dependencies = [ "eager", "enum-iterator", @@ -8135,7 +8133,7 @@ dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 2.10.0", + "indexmap 2.11.0", "indicatif", "log", "rayon", @@ -8156,7 +8154,7 @@ dependencies = [ "solana-signer", "solana-transaction", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -8271,7 +8269,7 @@ dependencies = [ "spl-token-2022 7.0.0", "spl-token-group-interface", "spl-token-metadata-interface", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8294,7 +8292,7 @@ dependencies = [ "solana-transaction", "solana-transaction-context", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8319,7 +8317,7 @@ dependencies = [ "solana-net-utils", "solana-streamer", "solana-transaction-error", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -8398,7 +8396,7 @@ dependencies = [ "solana-transaction", "solana-transaction-context", "solana-vote-interface", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8449,7 +8447,7 @@ dependencies = [ "solana-signature", "solana-signer", "subtle", - "thiserror 2.0.14", + "thiserror 2.0.16", "wasm-bindgen", "zeroize", ] @@ -8503,7 +8501,7 @@ dependencies = [ "solana-signature", "solana-signer", "subtle", - "thiserror 2.0.14", + "thiserror 2.0.16", "zeroize", ] @@ -8568,7 +8566,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -8580,7 +8578,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.105", + "syn 2.0.106", "thiserror 1.0.69", ] @@ -8628,7 +8626,7 @@ dependencies = [ "solana-program-option", "solana-pubkey", "solana-zk-sdk", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8653,7 +8651,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -8746,7 +8744,7 @@ dependencies = [ "spl-token-metadata-interface", "spl-transfer-hook-interface", "spl-type-length-value", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8772,7 +8770,7 @@ dependencies = [ "solana-program", "solana-zk-sdk", "spl-pod", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8794,7 +8792,7 @@ checksum = "0e3597628b0d2fe94e7900fd17cdb4cfbb31ee35c66f82809d27d86e44b2848b" dependencies = [ "curve25519-dalek 4.1.3", "solana-zk-sdk", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -8923,9 +8921,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.105" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -8967,7 +8965,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -8987,7 +8985,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -9118,7 +9116,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -9179,15 +9177,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9228,11 +9226,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.14", + "thiserror-impl 2.0.16", ] [[package]] @@ -9243,18 +9241,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -9268,12 +9266,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -9283,15 +9280,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" dependencies = [ "num-conv", "time-core", @@ -9328,9 +9325,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -9369,7 +9366,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -9511,7 +9508,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.0", "serde", "serde_spanned 1.0.0", "toml_datetime 0.7.0", @@ -9544,7 +9541,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -9594,7 +9591,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "futures-util", "http 1.3.1", @@ -9650,7 +9647,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -9699,14 +9696,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "serde", "serde_json", "sharded-slab", @@ -9873,13 +9870,14 @@ dependencies = [ [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -10004,11 +10002,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -10033,7 +10031,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -10068,7 +10066,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10102,15 +10100,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.2", -] - [[package]] name = "webpki-root-certs" version = "1.0.2" @@ -10144,16 +10133,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "winapi" version = "0.3.9" @@ -10172,11 +10151,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -10206,7 +10185,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -10217,7 +10196,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -10545,9 +10524,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -10563,13 +10542,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" @@ -10612,7 +10588,7 @@ dependencies = [ "anyhow", "ark-bn254 0.5.0", "ark-ff 0.5.0", - "clap 4.5.45", + "clap 4.5.46", "dirs", "groth16-solana", "light-batched-merkle-tree", @@ -10659,7 +10635,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", "synstructure 0.13.2", ] @@ -10680,7 +10656,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -10700,7 +10676,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", "synstructure 0.13.2", ] @@ -10721,7 +10697,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -10754,7 +10730,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.105", + "syn 2.0.106", ] [[package]] @@ -10784,3 +10760,8 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "solana-program-memory" +version = "2.2.1" +source = "git+https://github.com/anza-xyz/solana-sdk?rev=1c1d667f161666f12f5a43ebef8eda9470a8c6ee#1c1d667f161666f12f5a43ebef8eda9470a8c6ee" diff --git a/Cargo.toml b/Cargo.toml index 881233cd9c..0a74629966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,13 +34,13 @@ members = [ "program-tests/system-cpi-test", "program-tests/system-cpi-v2-test", "program-tests/system-test", - "sdk-tests/sdk-anchor-test/programs/sdk-anchor-test", - "sdk-tests/sdk-test", - "sdk-tests/sdk-pinocchio-test", "program-tests/create-address-test-program", "program-tests/utils", "program-tests/merkle-tree", "sdk-tests/client-test", + "sdk-tests/sdk-anchor-test/programs/sdk-anchor-test", + "sdk-tests/sdk-pinocchio-test", + "sdk-tests/sdk-native-test", "forester-utils", "forester", "sparse-merkle-tree", @@ -56,6 +56,10 @@ strip = "none" [profile.release] overflow-checks = true +[workspace.package] +version = "0.1.0" +edition = "2021" + [workspace.dependencies] solana-banks-client = { version = "2.2" } solana-banks-interface = { version = "2.2" } @@ -100,8 +104,10 @@ solana-compute-budget = { version = "2.2" } solana-system-interface = { version = "1" } solana-security-txt = "1.1.1" spl-token = "7.0.0" -spl-token-2022 = { version = "7", features = ["no-entrypoint"] } +spl-token-2022 = { version = "7.0.0", features = ["no-entrypoint"] } +spl-pod = "0.5.1" pinocchio = { version = "0.8.4" } +pinocchio-pubkey = { version = "0.3.0" } bs58 = "^0.5.1" litesvm = "0.6.1" # Anchor @@ -172,6 +178,7 @@ account-compression = { path = "programs/account-compression", version = "2.0.0" light-compressed-token = { path = "programs/compressed-token", version = "2.0.0", features = [ "cpi", ] } +light-compressed-token-types = { path = "sdk-libs/compressed-token-types", version = "0.1.0" } light-system-program-anchor = { path = "anchor-programs/system", version = "2.0.0", features = [ "cpi", ] } @@ -190,6 +197,7 @@ light-bounded-vec = { version = "2.0.0" } light-poseidon = { version = "0.3.0" } light-test-utils = { path = "program-tests/utils", version = "1.2.1" } light-indexed-array = { path = "program-libs/indexed-array", version = "0.1.0" } +light-program-profiler = { git = "https://github.com/Lightprotocol/light-program-profiler", rev = "36a75e14f54dd862bf2f338c97435ffc7e3e8de9", version = "0.1.0" } create-address-program-test = { path = "program-tests/create-address-test-program", version = "1.0.0" } groth16-solana = { version = "0.2.0" } bytemuck = { version = "1.19.0" } @@ -197,6 +205,7 @@ arrayvec = "0.7" # Math and crypto num-bigint = "0.4.6" +tabled = "0.20" num-traits = "0.2.19" zerocopy = { version = "0.8.25" } base64 = "0.13" @@ -213,3 +222,11 @@ governor = "0.8.0" # Random rand = "0.8.5" + +[patch.crates-io] +# Profiling logs and state is handled here +solana-program-runtime = { git = "https://github.com/Lightprotocol/agave", rev = "580e29f03e4176a4a5525abc188a948c6595c47f" } +# Profiling syscalls are defined here +solana-bpf-loader-program = { git = "https://github.com/Lightprotocol/agave", rev = "580e29f03e4176a4a5525abc188a948c6595c47f" } +# Patch solana-program-memory to use older version where is_nonoverlapping is public +solana-program-memory = { git = "https://github.com/anza-xyz/solana-sdk", rev = "1c1d667f161666f12f5a43ebef8eda9470a8c6ee" } diff --git a/program-libs/heap/src/lib.rs b/program-libs/heap/src/lib.rs index 4656de6bda..c3bf26fde0 100644 --- a/program-libs/heap/src/lib.rs +++ b/program-libs/heap/src/lib.rs @@ -65,6 +65,14 @@ impl BumpAllocator { msg!("{}: total heap used: {}", msg, heap_used); heap_used } + #[cfg(target_os = "solana")] + pub fn get_used_heap(&self) -> u64 { + const HEAP_END_ADDRESS: u64 = HEAP_START_ADDRESS as u64 + HEAP_LENGTH as u64; + + let heap_start = unsafe { self.pos() } as u64; + let heap_used = HEAP_END_ADDRESS - heap_start; + heap_used + } #[cfg(target_os = "solana")] pub fn get_heap_pos(&self) -> usize { diff --git a/scripts/devenv.sh b/scripts/devenv.sh index 0349400d9c..eaaab82235 100755 --- a/scripts/devenv.sh +++ b/scripts/devenv.sh @@ -87,6 +87,7 @@ export CARGO_HOME export NPM_CONFIG_PREFIX export LIGHT_PROTOCOL_TOPLEVEL export LIGHT_PROTOCOL_DEVENV +export SBF_OUT_DIR=target/deploy # Set Redis URL if not already set export REDIS_URL="${REDIS_URL:-redis://localhost:6379}" diff --git a/scripts/format.sh b/scripts/format.sh index 58968a906f..c6637070f2 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -26,7 +26,7 @@ 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 sdk-test --no-run +cargo test-sbf -p sdk-native-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/program-test/Cargo.toml b/sdk-libs/program-test/Cargo.toml index c9a826ebc7..8ee936eddf 100644 --- a/sdk-libs/program-test/Cargo.toml +++ b/sdk-libs/program-test/Cargo.toml @@ -51,3 +51,10 @@ solana-account = { workspace = true } solana-compute-budget = { workspace = true } rand = { workspace = true } bytemuck = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +solana-transaction-status = { workspace = true } +bs58 = { workspace = true } +light-sdk-types = { workspace = true } +tabled = { workspace = true } +chrono = "0.4" diff --git a/sdk-libs/program-test/src/indexer/test_indexer.rs b/sdk-libs/program-test/src/indexer/test_indexer.rs index 13803284b3..3ceaf1fdb0 100644 --- a/sdk-libs/program-test/src/indexer/test_indexer.rs +++ b/sdk-libs/program-test/src/indexer/test_indexer.rs @@ -1588,67 +1588,7 @@ impl TestIndexer { compressed_accounts: &mut Vec, ) { let mut input_addresses = vec![]; - if event.input_compressed_account_hashes.len() > i { - let tx_hash: [u8; 32] = create_tx_hash( - &event.input_compressed_account_hashes, - &event.output_compressed_account_hashes, - slot, - ) - .unwrap(); - let hash = event.input_compressed_account_hashes[i]; - let index = self - .compressed_accounts - .iter() - .position(|x| x.hash().unwrap() == hash); - let (leaf_index, merkle_tree_pubkey) = if let Some(index) = index { - self.nullified_compressed_accounts - .push(self.compressed_accounts[index].clone()); - let leaf_index = self.compressed_accounts[index].merkle_context.leaf_index; - let merkle_tree_pubkey = self.compressed_accounts[index] - .merkle_context - .merkle_tree_pubkey; - if let Some(address) = self.compressed_accounts[index].compressed_account.address { - input_addresses.push(address); - } - self.compressed_accounts.remove(index); - (leaf_index, merkle_tree_pubkey) - } else { - let index = self - .token_compressed_accounts - .iter() - .position(|x| x.compressed_account.hash().unwrap() == hash) - .expect("input compressed account not found"); - self.token_nullified_compressed_accounts - .push(self.token_compressed_accounts[index].clone()); - let leaf_index = self.token_compressed_accounts[index] - .compressed_account - .merkle_context - .leaf_index; - let merkle_tree_pubkey = self.token_compressed_accounts[index] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - self.token_compressed_accounts.remove(index); - (leaf_index, merkle_tree_pubkey) - }; - let bundle = - &mut ::get_state_merkle_trees_mut(self) - .iter_mut() - .find(|x| { - x.accounts.merkle_tree - == solana_pubkey::Pubkey::from(merkle_tree_pubkey.to_bytes()) - }) - .unwrap(); - // Store leaf indices of input accounts for batched trees - if bundle.tree_type == TreeType::StateV2 { - let leaf_hash = event.input_compressed_account_hashes[i]; - bundle.input_leaf_indices.push(LeafIndexInfo { - leaf_index, - leaf: leaf_hash, - tx_hash, - }); - } - } + let mut new_addresses = vec![]; if event.output_compressed_accounts.len() > i { let compressed_account = &event.output_compressed_accounts[i]; @@ -1689,8 +1629,13 @@ impl TestIndexer { // new accounts are inserted in front so that the newest accounts are found first match compressed_account.compressed_account.data.as_ref() { Some(data) => { - if compressed_account.compressed_account.owner == light_compressed_token::ID.to_bytes() - && data.discriminator == light_compressed_token::constants::TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR + // Check for both V1 and V2 token account discriminators + let is_v1_token = data.discriminator == light_compressed_token::constants::TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR; // [2, 0, 0, 0, 0, 0, 0, 0] + let is_v2_token = data.discriminator == [0, 0, 0, 0, 0, 0, 0, 3]; // V2 discriminator + + if compressed_account.compressed_account.owner + == light_compressed_token::ID.to_bytes() + && (is_v1_token || is_v2_token) { if let Ok(token_data) = TokenData::deserialize(&mut data.data.as_slice()) { let token_account = TokenDataWithMerkleContext { @@ -1704,7 +1649,7 @@ impl TestIndexer { merkle_tree_pubkey: merkle_tree_pubkey.into(), queue_pubkey: nullifier_queue_pubkey.into(), prove_by_index: false, - tree_type:merkle_tree.tree_type, + tree_type: merkle_tree.tree_type, }, }, }; @@ -1719,7 +1664,7 @@ impl TestIndexer { merkle_tree_pubkey: merkle_tree_pubkey.into(), queue_pubkey: nullifier_queue_pubkey.into(), prove_by_index: false, - tree_type: merkle_tree.tree_type + tree_type: merkle_tree.tree_type, }, }; compressed_accounts.push(compressed_account.clone()); @@ -1785,6 +1730,73 @@ impl TestIndexer { )); } } + if event.input_compressed_account_hashes.len() > i { + let tx_hash: [u8; 32] = create_tx_hash( + &event.input_compressed_account_hashes, + &event.output_compressed_account_hashes, + slot, + ) + .unwrap(); + let hash = event.input_compressed_account_hashes[i]; + let index = self + .compressed_accounts + .iter() + .position(|x| x.hash().unwrap() == hash); + let (leaf_index, merkle_tree_pubkey) = if let Some(index) = index { + self.nullified_compressed_accounts + .push(self.compressed_accounts[index].clone()); + let leaf_index = self.compressed_accounts[index].merkle_context.leaf_index; + let merkle_tree_pubkey = self.compressed_accounts[index] + .merkle_context + .merkle_tree_pubkey; + if let Some(address) = self.compressed_accounts[index].compressed_account.address { + input_addresses.push(address); + } + self.compressed_accounts.remove(index); + (Some(leaf_index), Some(merkle_tree_pubkey)) + } else if let Some(index) = self + .token_compressed_accounts + .iter() + .position(|x| x.compressed_account.hash().unwrap() == hash) + { + self.token_nullified_compressed_accounts + .push(self.token_compressed_accounts[index].clone()); + let leaf_index = self.token_compressed_accounts[index] + .compressed_account + .merkle_context + .leaf_index; + let merkle_tree_pubkey = self.token_compressed_accounts[index] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + self.token_compressed_accounts.remove(index); + (Some(leaf_index), Some(merkle_tree_pubkey)) + } else { + (None, None) + }; + if let Some(leaf_index) = leaf_index { + let merkle_tree_pubkey = merkle_tree_pubkey.unwrap(); + let bundle = + &mut ::get_state_merkle_trees_mut(self) + .iter_mut() + .find(|x| { + x.accounts.merkle_tree + == solana_pubkey::Pubkey::from(merkle_tree_pubkey.to_bytes()) + }) + .unwrap(); + // Store leaf indices of input accounts for batched trees + if bundle.tree_type == TreeType::StateV2 { + let leaf_hash = event.input_compressed_account_hashes[i]; + bundle.input_leaf_indices.push(LeafIndexInfo { + leaf_index, + leaf: leaf_hash, + tx_hash, + }); + } + } else { + println!("Test indexer didn't find input compressed accounts to nullify"); + } + } // checks whether there are addresses in outputs which don't exist in inputs. // if so check pubkey_array for the first address Merkle tree and append to the bundles queue elements. // Note: diff --git a/sdk-libs/program-test/src/lib.rs b/sdk-libs/program-test/src/lib.rs index e1825673de..c36f8a52d3 100644 --- a/sdk-libs/program-test/src/lib.rs +++ b/sdk-libs/program-test/src/lib.rs @@ -114,6 +114,7 @@ pub mod accounts; pub mod indexer; +pub mod logging; pub mod program_test; pub mod utils; diff --git a/sdk-libs/program-test/src/logging/config.rs b/sdk-libs/program-test/src/logging/config.rs new file mode 100644 index 0000000000..3538cf3dbb --- /dev/null +++ b/sdk-libs/program-test/src/logging/config.rs @@ -0,0 +1,109 @@ +//! Configuration types for enhanced logging + +use serde::{Deserialize, Serialize}; + +/// Configuration for enhanced transaction logging +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EnhancedLoggingConfig { + /// Whether enhanced logging is enabled + pub enabled: bool, + /// Whether to log events to console (file logging is always enabled when enhanced_logging.enabled = true) + pub log_events: bool, + /// Level of detail in logs + pub verbosity: LogVerbosity, + /// Show account changes before/after transaction + pub show_account_changes: bool, + /// Decode Light Protocol specific instructions + pub decode_light_instructions: bool, + /// Show compute units consumed per instruction + pub show_compute_units: bool, + /// Use ANSI colors in output + pub use_colors: bool, + /// Maximum number of inner instruction levels to display + pub max_inner_instruction_depth: usize, + /// Show instruction data for account compression program + pub show_compression_instruction_data: bool, +} + +impl Default for EnhancedLoggingConfig { + fn default() -> Self { + Self { + enabled: true, // Always enabled for processing + log_events: false, // Don't log by default + verbosity: LogVerbosity::Standard, + show_account_changes: true, + decode_light_instructions: true, + show_compute_units: true, + use_colors: true, + max_inner_instruction_depth: 60, + show_compression_instruction_data: false, + } + } +} + +/// Verbosity levels for transaction logging +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum LogVerbosity { + /// Only instruction hierarchy and status + Brief, + /// + account changes and basic instruction info + Standard, + /// + parsed instruction data when available + Detailed, + /// + raw instruction data and internal debugging info + Full, +} + +impl EnhancedLoggingConfig { + /// Create config optimized for debugging + pub fn debug() -> Self { + Self { + enabled: true, + log_events: true, // Enable logging for debug mode + verbosity: LogVerbosity::Full, + show_account_changes: true, + decode_light_instructions: true, + show_compute_units: true, + use_colors: true, + max_inner_instruction_depth: 60, + show_compression_instruction_data: false, + } + } + + /// Create config optimized for CI/production + pub fn minimal() -> Self { + Self { + enabled: true, + log_events: false, // Don't log for minimal config + verbosity: LogVerbosity::Brief, + show_account_changes: false, + decode_light_instructions: false, + show_compute_units: false, + use_colors: false, + max_inner_instruction_depth: 60, + show_compression_instruction_data: false, + } + } + + /// Create config based on environment - always enabled, debug level when RUST_BACKTRACE is set + pub fn from_env() -> Self { + if std::env::var("RUST_BACKTRACE").is_ok() { + Self::debug() + } else { + // Always enabled but with standard verbosity when backtrace is not set + Self::default() + } + } + + /// Enable event logging with current settings + pub fn with_logging(mut self) -> Self { + self.log_events = true; + self + } + + /// Disable event logging + pub fn without_logging(mut self) -> Self { + self.log_events = false; + self + } +} diff --git a/sdk-libs/program-test/src/logging/decoder.rs b/sdk-libs/program-test/src/logging/decoder.rs new file mode 100644 index 0000000000..c7c98a29bc --- /dev/null +++ b/sdk-libs/program-test/src/logging/decoder.rs @@ -0,0 +1,979 @@ +//! Instruction decoder for Light Protocol and common Solana programs + +use borsh::BorshDeserialize; +use light_compressed_account::instruction_data::{ + data::InstructionDataInvoke, invoke_cpi::InstructionDataInvokeCpi, + with_account_info::InstructionDataInvokeCpiWithAccountInfo, + with_readonly::InstructionDataInvokeCpiWithReadOnly, +}; +use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, system_program}; + +use super::types::ParsedInstructionData; + +/// Helper to resolve merkle tree and queue pubkeys from instruction accounts +/// For InvokeCpi instructions, tree accounts start 2 positions after the system program +fn resolve_tree_and_queue_pubkeys( + accounts: &[AccountMeta], + merkle_tree_index: Option, + nullifier_queue_index: Option, +) -> (Option, Option) { + let mut tree_pubkey = None; + let mut queue_pubkey = None; + + // Find the system program account position + let mut system_program_pos = None; + for (i, account) in accounts.iter().enumerate() { + if account.pubkey == system_program::ID { + system_program_pos = Some(i); + break; + } + } + + if let Some(system_pos) = system_program_pos { + // Tree accounts start 2 positions after system program + let tree_accounts_start = system_pos + 2; + + if let Some(tree_idx) = merkle_tree_index { + let tree_account_pos = tree_accounts_start + tree_idx as usize; + if tree_account_pos < accounts.len() { + tree_pubkey = Some(accounts[tree_account_pos].pubkey); + } + } + + if let Some(queue_idx) = nullifier_queue_index { + let queue_account_pos = tree_accounts_start + queue_idx as usize; + if queue_account_pos < accounts.len() { + queue_pubkey = Some(accounts[queue_account_pos].pubkey); + } + } + } + + (tree_pubkey, queue_pubkey) +} + +/// Decode instruction data for known programs +pub fn decode_instruction( + program_id: &Pubkey, + data: &[u8], + accounts: &[AccountMeta], +) -> Option { + match program_id.to_string().as_str() { + // Light System Program + "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" => { + decode_light_system_instruction(data, accounts, program_id) + } + + // Compute Budget Program + "ComputeBudget111111111111111111111111111111" => decode_compute_budget_instruction(data), + + // System Program + id if id == system_program::ID.to_string() => decode_system_instruction(data), + + // Account Compression Program + "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq" => decode_compression_instruction(data), + + // Compressed Token Program + "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m" => decode_compressed_token_instruction(data), + + _ => Some(ParsedInstructionData::Unknown { + program_name: get_program_name(program_id), + data_preview: bs58::encode(&data[..data.len().min(16)]).into_string(), + }), + } +} + +/// Decode Light System Program instructions +fn decode_light_system_instruction( + data: &[u8], + accounts: &[AccountMeta], + program_id: &Pubkey, +) -> Option { + if data.is_empty() { + return None; + } + + // Light System Program uses 8-byte discriminators + if data.len() < 8 { + return Some(ParsedInstructionData::LightSystemProgram { + instruction_type: "Invalid".to_string(), + compressed_accounts: None, + proof_info: None, + address_params: None, + fee_info: None, + input_account_data: None, + output_account_data: None, + }); + } + + // Extract the 8-byte discriminator + let discriminator: [u8; 8] = data[0..8].try_into().unwrap(); + + // Light Protocol discriminators from compressed-account/src/discriminators.rs + let ( + instruction_type, + compressed_accounts, + proof_info, + address_params, + fee_info, + input_account_data, + output_account_data, + ) = match discriminator { + [26, 16, 169, 7, 21, 202, 242, 25] => { + // DISCRIMINATOR_INVOKE + match parse_invoke_instruction(&data[8..], accounts) { + Ok(parsed) => parsed, + Err(_) => ( + "Invoke (parse error)".to_string(), + None, + None, + None, + None, + None, + None, + ), + } + } + [49, 212, 191, 129, 39, 194, 43, 196] => { + // DISCRIMINATOR_INVOKE_CPI + match parse_invoke_cpi_instruction(&data[8..], accounts) { + Ok(parsed) => parsed, + Err(_) => ( + "InvokeCpi (parse error)".to_string(), + None, + None, + None, + None, + None, + None, + ), + } + } + [86, 47, 163, 166, 21, 223, 92, 8] => { + // DISCRIMINATOR_INVOKE_CPI_WITH_READ_ONLY + match parse_invoke_cpi_readonly_instruction(&data[8..], accounts) { + Ok(parsed) => parsed, + Err(_) => ( + "InvokeCpiWithReadOnly (parse error)".to_string(), + None, + None, + None, + None, + None, + None, + ), + } + } + [228, 34, 128, 84, 47, 139, 86, 240] => { + // INVOKE_CPI_WITH_ACCOUNT_INFO_INSTRUCTION + match parse_invoke_cpi_account_info_instruction(&data[8..], accounts, program_id) { + Ok(parsed) => parsed, + Err(_) => ( + "InvokeCpiWithAccountInfo (parse error)".to_string(), + None, + None, + None, + None, + None, + None, + ), + } + } + _ => { + // Unknown discriminator - show the discriminator bytes for debugging + let discriminator_str = format!("{:?}", discriminator); + ( + format!("Unknown({})", discriminator_str), + None, + None, + None, + None, + None, + None, + ) + } + }; + + Some(ParsedInstructionData::LightSystemProgram { + instruction_type, + compressed_accounts, + proof_info, + address_params, + fee_info, + input_account_data, + output_account_data, + }) +} + +type InstructionParseResult = Result< + ( + String, + Option, + Option, + Option>, + Option, + Option>, + Option>, + ), + Box, +>; + +/// Parse Invoke instruction data - display data hashes directly +fn parse_invoke_instruction(data: &[u8], accounts: &[AccountMeta]) -> InstructionParseResult { + // Skip the 4-byte vec length prefix that Anchor adds + if data.len() < 4 { + return Err("Instruction data too short for Anchor prefix".into()); + } + let instruction_data = InstructionDataInvoke::try_from_slice(&data[4..])?; + + let compressed_accounts = Some(super::types::CompressedAccountSummary { + input_accounts: instruction_data + .input_compressed_accounts_with_merkle_context + .len(), + output_accounts: instruction_data.output_compressed_accounts.len(), + lamports_change: instruction_data + .compress_or_decompress_lamports + .map(|l| l as i64), + }); + + let proof_info = instruction_data + .proof + .as_ref() + .map(|_| super::types::ProofSummary { + proof_type: "Validity".to_string(), + has_validity_proof: true, + }); + + // Extract actual address parameters with values + let address_params = if !instruction_data.new_address_params.is_empty() { + Some( + instruction_data + .new_address_params + .iter() + .map(|param| super::types::AddressParam { + seed: param.seed, + address_queue_index: Some(param.address_queue_account_index), + merkle_tree_index: Some(param.address_merkle_tree_account_index), + root_index: Some(param.address_merkle_tree_root_index), + derived_address: None, + }) + .collect(), + ) + } else { + None + }; + + // Extract input account data + let input_account_data = if !instruction_data + .input_compressed_accounts_with_merkle_context + .is_empty() + { + Some( + instruction_data + .input_compressed_accounts_with_merkle_context + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_context.merkle_tree_pubkey_index); + let queue_idx = Some(acc.merkle_context.queue_pubkey_index); + let (tree_pubkey, queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, queue_idx); + + super::types::InputAccountData { + lamports: acc.compressed_account.lamports, + owner: Some(acc.compressed_account.owner.into()), + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: queue_idx, + queue_pubkey, + address: acc.compressed_account.address, + data_hash: if let Some(ref data) = acc.compressed_account.data { + data.data_hash.to_vec() + } else { + vec![] + }, + discriminator: if let Some(ref data) = acc.compressed_account.data { + data.discriminator.to_vec() + } else { + vec![] + }, + } + }) + .collect(), + ) + } else { + None + }; + + // Extract output account data + let output_account_data = if !instruction_data.output_compressed_accounts.is_empty() { + Some( + instruction_data + .output_compressed_accounts + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_tree_index); + let (tree_pubkey, _queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, None); + + super::types::OutputAccountData { + lamports: acc.compressed_account.lamports, + data: acc.compressed_account.data.as_ref().map(|d| d.data.clone()), + owner: Some(acc.compressed_account.owner.into()), + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: None, + queue_pubkey: None, + address: acc.compressed_account.address, + data_hash: if let Some(ref data) = acc.compressed_account.data { + data.data_hash.to_vec() + } else { + vec![] + }, + discriminator: if let Some(ref data) = acc.compressed_account.data { + data.discriminator.to_vec() + } else { + vec![] + }, + } + }) + .collect(), + ) + } else { + None + }; + + let fee_info = instruction_data + .relay_fee + .map(|fee| super::types::FeeSummary { + relay_fee: Some(fee), + compression_fee: None, + }); + + Ok(( + "Invoke".to_string(), + compressed_accounts, + proof_info, + address_params, + fee_info, + input_account_data, + output_account_data, + )) +} + +/// Parse InvokeCpi instruction data - display data hashes directly +fn parse_invoke_cpi_instruction(data: &[u8], accounts: &[AccountMeta]) -> InstructionParseResult { + // Skip the 4-byte vec length prefix that Anchor adds + if data.len() < 4 { + return Err("Instruction data too short for Anchor prefix".into()); + } + let instruction_data = InstructionDataInvokeCpi::try_from_slice(&data[4..])?; + + let compressed_accounts = Some(super::types::CompressedAccountSummary { + input_accounts: instruction_data + .input_compressed_accounts_with_merkle_context + .len(), + output_accounts: instruction_data.output_compressed_accounts.len(), + lamports_change: instruction_data + .compress_or_decompress_lamports + .map(|l| l as i64), + }); + + let proof_info = instruction_data + .proof + .as_ref() + .map(|_| super::types::ProofSummary { + proof_type: "Validity".to_string(), + has_validity_proof: true, + }); + + // Extract actual address parameters with values + let address_params = if !instruction_data.new_address_params.is_empty() { + Some( + instruction_data + .new_address_params + .iter() + .map(|param| super::types::AddressParam { + seed: param.seed, + address_queue_index: Some(param.address_queue_account_index), + merkle_tree_index: Some(param.address_merkle_tree_account_index), + root_index: Some(param.address_merkle_tree_root_index), + derived_address: None, + }) + .collect(), + ) + } else { + None + }; + + // Extract input account data + let input_account_data = if !instruction_data + .input_compressed_accounts_with_merkle_context + .is_empty() + { + Some( + instruction_data + .input_compressed_accounts_with_merkle_context + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_context.merkle_tree_pubkey_index); + let queue_idx = Some(acc.merkle_context.queue_pubkey_index); + let (tree_pubkey, queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, queue_idx); + + super::types::InputAccountData { + lamports: acc.compressed_account.lamports, + owner: Some(acc.compressed_account.owner.into()), + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: queue_idx, + queue_pubkey, + address: acc.compressed_account.address, + data_hash: if let Some(ref data) = acc.compressed_account.data { + data.data_hash.to_vec() + } else { + vec![] + }, + discriminator: if let Some(ref data) = acc.compressed_account.data { + data.discriminator.to_vec() + } else { + vec![] + }, + } + }) + .collect(), + ) + } else { + None + }; + + // Extract output account data + let output_account_data = if !instruction_data.output_compressed_accounts.is_empty() { + Some( + instruction_data + .output_compressed_accounts + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_tree_index); + let (tree_pubkey, _queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, None); + + super::types::OutputAccountData { + lamports: acc.compressed_account.lamports, + data: acc.compressed_account.data.as_ref().map(|d| d.data.clone()), + owner: Some(acc.compressed_account.owner.into()), + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: None, + queue_pubkey: None, + address: acc.compressed_account.address, + data_hash: if let Some(ref data) = acc.compressed_account.data { + data.data_hash.to_vec() + } else { + vec![] + }, + discriminator: if let Some(ref data) = acc.compressed_account.data { + data.discriminator.to_vec() + } else { + vec![] + }, + } + }) + .collect(), + ) + } else { + None + }; + + let fee_info = instruction_data + .relay_fee + .map(|fee| super::types::FeeSummary { + relay_fee: Some(fee), + compression_fee: None, + }); + + Ok(( + "InvokeCpi".to_string(), + compressed_accounts, + proof_info, + address_params, + fee_info, + input_account_data, + output_account_data, + )) +} + +/// Parse InvokeCpiWithReadOnly instruction data - display data hashes directly +fn parse_invoke_cpi_readonly_instruction( + data: &[u8], + accounts: &[AccountMeta], +) -> InstructionParseResult { + let instruction_data = InstructionDataInvokeCpiWithReadOnly::try_from_slice(data)?; + + let compressed_accounts = Some(super::types::CompressedAccountSummary { + input_accounts: instruction_data.input_compressed_accounts.len(), + output_accounts: instruction_data.output_compressed_accounts.len(), + lamports_change: if instruction_data.compress_or_decompress_lamports > 0 { + Some(instruction_data.compress_or_decompress_lamports as i64) + } else { + None + }, + }); + + let proof_info = Some(super::types::ProofSummary { + proof_type: "Validity".to_string(), + has_validity_proof: true, + }); + + // Extract actual address parameters with values + let mut address_params = Vec::new(); + + // Add new address parameters with actual values + for param in &instruction_data.new_address_params { + address_params.push(super::types::AddressParam { + seed: param.seed, + address_queue_index: Some(param.address_queue_account_index), + merkle_tree_index: Some(param.address_merkle_tree_account_index), + root_index: Some(param.address_merkle_tree_root_index), + derived_address: None, + }); + } + + // Add readonly address parameters + for readonly_addr in &instruction_data.read_only_addresses { + address_params.push(super::types::AddressParam { + seed: [0; 32], // ReadOnly addresses don't have seeds in the same way + address_queue_index: None, + merkle_tree_index: Some(readonly_addr.address_merkle_tree_account_index), + root_index: Some(readonly_addr.address_merkle_tree_root_index), + derived_address: Some(readonly_addr.address), + }); + } + + let address_params = if !address_params.is_empty() { + Some(address_params) + } else { + None + }; + + // Extract input account data - use data_hash from InAccount + let input_account_data = if !instruction_data.input_compressed_accounts.is_empty() { + Some( + instruction_data + .input_compressed_accounts + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_context.merkle_tree_pubkey_index); + let queue_idx = Some(acc.merkle_context.queue_pubkey_index); + let (tree_pubkey, queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, queue_idx); + + super::types::InputAccountData { + lamports: acc.lamports, + owner: Some(instruction_data.invoking_program_id.into()), // Use invoking program as owner + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: queue_idx, + queue_pubkey, + address: acc.address, + data_hash: acc.data_hash.to_vec(), + discriminator: acc.discriminator.to_vec(), + } + }) + .collect(), + ) + } else { + None + }; + + // Extract output account data + let output_account_data = if !instruction_data.output_compressed_accounts.is_empty() { + Some( + instruction_data + .output_compressed_accounts + .iter() + .map(|acc| { + let tree_idx = Some(acc.merkle_tree_index); + let (tree_pubkey, _queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, None); + + super::types::OutputAccountData { + lamports: acc.compressed_account.lamports, + data: acc.compressed_account.data.as_ref().map(|d| d.data.clone()), + owner: Some(instruction_data.invoking_program_id.into()), // Use invoking program as owner for consistency + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: None, + queue_pubkey: None, + address: acc.compressed_account.address, + data_hash: if let Some(ref data) = acc.compressed_account.data { + data.data_hash.to_vec() + } else { + vec![] + }, + discriminator: if let Some(ref data) = acc.compressed_account.data { + data.discriminator.to_vec() + } else { + vec![] + }, + } + }) + .collect(), + ) + } else { + None + }; + + Ok(( + "InvokeCpiWithReadOnly".to_string(), + compressed_accounts, + proof_info, + address_params, + None, + input_account_data, + output_account_data, + )) +} + +/// Parse InvokeCpiWithAccountInfo instruction data - display data hashes directly +fn parse_invoke_cpi_account_info_instruction( + data: &[u8], + accounts: &[AccountMeta], + program_id: &Pubkey, +) -> InstructionParseResult { + let instruction_data = InstructionDataInvokeCpiWithAccountInfo::try_from_slice(data)?; + + let input_accounts = instruction_data + .account_infos + .iter() + .filter(|a| a.input.is_some()) + .count(); + let output_accounts = instruction_data + .account_infos + .iter() + .filter(|a| a.output.is_some()) + .count(); + + let compressed_accounts = Some(super::types::CompressedAccountSummary { + input_accounts, + output_accounts, + lamports_change: if instruction_data.compress_or_decompress_lamports > 0 { + Some(instruction_data.compress_or_decompress_lamports as i64) + } else { + None + }, + }); + + let proof_info = Some(super::types::ProofSummary { + proof_type: "Validity".to_string(), + has_validity_proof: true, + }); + + // Extract actual address parameters with values + let mut address_params = Vec::new(); + + // Add new address parameters with actual values + for param in &instruction_data.new_address_params { + address_params.push(super::types::AddressParam { + seed: param.seed, + address_queue_index: Some(param.address_queue_account_index), + merkle_tree_index: Some(param.address_merkle_tree_account_index), + root_index: Some(param.address_merkle_tree_root_index), + derived_address: None, + }); + } + + // Add readonly address parameters + for readonly_addr in &instruction_data.read_only_addresses { + address_params.push(super::types::AddressParam { + seed: [0; 32], // ReadOnly addresses don't have seeds in the same way + address_queue_index: None, + merkle_tree_index: Some(readonly_addr.address_merkle_tree_account_index), + root_index: Some(readonly_addr.address_merkle_tree_root_index), + derived_address: Some(readonly_addr.address), + }); + } + + let address_params = if !address_params.is_empty() { + Some(address_params) + } else { + None + }; + + // Extract input account data from account_infos + let input_account_data = { + let mut input_data = Vec::new(); + for account_info in &instruction_data.account_infos { + if let Some(ref input) = account_info.input { + input_data.push(super::types::InputAccountData { + lamports: input.lamports, + owner: Some(*program_id), // Use invoking program as owner + merkle_tree_index: None, // Note: merkle tree context not available in CompressedAccountInfo + merkle_tree_pubkey: None, + queue_index: None, + queue_pubkey: None, + address: account_info.address, // Use address from CompressedAccountInfo + data_hash: input.data_hash.to_vec(), + discriminator: input.discriminator.to_vec(), + }); + } + } + if !input_data.is_empty() { + Some(input_data) + } else { + None + } + }; + + // Extract output account data from account_infos + let output_account_data = { + let mut output_data = Vec::new(); + for account_info in &instruction_data.account_infos { + if let Some(ref output) = account_info.output { + let tree_idx = Some(output.output_merkle_tree_index); + let (tree_pubkey, _queue_pubkey) = + resolve_tree_and_queue_pubkeys(accounts, tree_idx, None); + + output_data.push(super::types::OutputAccountData { + lamports: output.lamports, + data: if !output.data.is_empty() { + Some(output.data.clone()) + } else { + None + }, + owner: Some(*program_id), // Use invoking program as owner + merkle_tree_index: tree_idx, + merkle_tree_pubkey: tree_pubkey, + queue_index: None, + queue_pubkey: None, + address: account_info.address, // Use address from CompressedAccountInfo + data_hash: output.data_hash.to_vec(), + discriminator: output.discriminator.to_vec(), + }); + } + } + if !output_data.is_empty() { + Some(output_data) + } else { + None + } + }; + + Ok(( + "InvokeCpiWithAccountInfo".to_string(), + compressed_accounts, + proof_info, + address_params, + None, + input_account_data, + output_account_data, + )) +} + +/// Decode Compute Budget Program instructions +fn decode_compute_budget_instruction(data: &[u8]) -> Option { + if data.len() < 4 { + return None; + } + + let instruction_discriminator = u32::from_le_bytes([data[0], data[1], data[2], data[3]]); + + match instruction_discriminator { + 0 => { + // RequestUnitsDeprecated + if data.len() >= 12 { + let units = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as u64; + let _additional_fee = + u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as u64; + Some(ParsedInstructionData::ComputeBudget { + instruction_type: "RequestUnitsDeprecated".to_string(), + value: Some(units), + }) + } else { + None + } + } + 1 => { + // RequestHeapFrame + if data.len() >= 8 { + let bytes = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as u64; + Some(ParsedInstructionData::ComputeBudget { + instruction_type: "RequestHeapFrame".to_string(), + value: Some(bytes), + }) + } else { + None + } + } + 2 => { + // SetComputeUnitLimit + if data.len() >= 8 { + let units = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as u64; + Some(ParsedInstructionData::ComputeBudget { + instruction_type: "SetComputeUnitLimit".to_string(), + value: Some(units), + }) + } else { + None + } + } + 3 => { + // SetComputeUnitPrice + if data.len() >= 12 { + let price = u64::from_le_bytes([ + data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], + ]); + Some(ParsedInstructionData::ComputeBudget { + instruction_type: "SetComputeUnitPrice".to_string(), + value: Some(price), + }) + } else { + None + } + } + _ => Some(ParsedInstructionData::ComputeBudget { + instruction_type: "Unknown".to_string(), + value: None, + }), + } +} + +/// Decode System Program instructions +fn decode_system_instruction(data: &[u8]) -> Option { + if data.len() < 4 { + return None; + } + + let instruction_type = u32::from_le_bytes([data[0], data[1], data[2], data[3]]); + + match instruction_type { + 0 => { + // CreateAccount + if data.len() >= 52 { + let lamports = u64::from_le_bytes([ + data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], + ]); + let space = u64::from_le_bytes([ + data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19], + ]); + + Some(ParsedInstructionData::System { + instruction_type: "CreateAccount".to_string(), + lamports: Some(lamports), + space: Some(space), + new_account: None, + }) + } else { + None + } + } + 2 => { + // Transfer + if data.len() >= 12 { + let lamports = u64::from_le_bytes([ + data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], + ]); + + Some(ParsedInstructionData::System { + instruction_type: "Transfer".to_string(), + lamports: Some(lamports), + space: None, + new_account: None, + }) + } else { + None + } + } + 8 => { + // Allocate + if data.len() >= 12 { + let space = u64::from_le_bytes([ + data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], + ]); + + Some(ParsedInstructionData::System { + instruction_type: "Allocate".to_string(), + lamports: None, + space: Some(space), + new_account: None, + }) + } else { + None + } + } + _ => Some(ParsedInstructionData::System { + instruction_type: "Unknown".to_string(), + lamports: None, + space: None, + new_account: None, + }), + } +} + +/// Decode Account Compression Program instructions +fn decode_compression_instruction(data: &[u8]) -> Option { + // Return basic instruction info for account compression + let instruction_name = if data.len() >= 8 { + // Common account compression operations + "InsertIntoQueues" + } else { + "Unknown" + }; + + Some(ParsedInstructionData::Unknown { + program_name: "Account Compression".to_string(), + data_preview: format!("{}({}bytes)", instruction_name, data.len()), + }) +} + +/// Decode Compressed Token Program instructions +fn decode_compressed_token_instruction(data: &[u8]) -> Option { + // Return basic instruction info for compressed token operations + let instruction_name = if data.len() >= 8 { + // Common compressed token operations + "TokenOperation" + } else { + "Unknown" + }; + + Some(ParsedInstructionData::Unknown { + program_name: "Compressed Token".to_string(), + data_preview: format!("{}({}bytes)", instruction_name, data.len()), + }) +} + +/// Get human-readable program name +fn get_program_name(program_id: &Pubkey) -> String { + match program_id.to_string().as_str() { + id if id == system_program::ID.to_string() => "System Program".to_string(), + "ComputeBudget111111111111111111111111111111" => "Compute Budget".to_string(), + "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" => "Light System Program".to_string(), + "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq" => "Account Compression".to_string(), + "FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy" => "Test Program".to_string(), + _ => { + let pubkey_str = program_id.to_string(); + format!("Program {}", &pubkey_str[..8]) + } + } +} + +/// Extract Light Protocol events from transaction logs and metadata +pub fn extract_light_events( + logs: &[String], + _events: &Option>, // Light Protocol events for future enhancement +) -> Vec { + let mut light_events = Vec::new(); + + // Parse events from logs + for log in logs { + if log.contains("PublicTransactionEvent") || log.contains("BatchPublicTransactionEvent") { + // Parse Light Protocol events from logs + light_events.push(super::types::LightProtocolEvent { + event_type: "PublicTransactionEvent".to_string(), + compressed_accounts: Vec::new(), + merkle_tree_changes: Vec::new(), + nullifiers: Vec::new(), + }); + } + } + + light_events +} diff --git a/sdk-libs/program-test/src/logging/decoder.rs:70:15 b/sdk-libs/program-test/src/logging/decoder.rs:70:15 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk-libs/program-test/src/logging/decoder.rs:946:15 b/sdk-libs/program-test/src/logging/decoder.rs:946:15 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk-libs/program-test/src/logging/formatter.rs b/sdk-libs/program-test/src/logging/formatter.rs new file mode 100644 index 0000000000..966c2aae35 --- /dev/null +++ b/sdk-libs/program-test/src/logging/formatter.rs @@ -0,0 +1,1235 @@ +//! Transaction formatting utilities for explorer-style output + +use std::fmt::{self, Write}; + +use solana_sdk::system_program; +use tabled::{Table, Tabled}; + +use super::{ + config::{EnhancedLoggingConfig, LogVerbosity}, + types::{ + AccountAccess, AccountChange, EnhancedInstructionLog, EnhancedTransactionLog, + TransactionStatus, + }, +}; + +/// Row for account table display +#[derive(Tabled)] +struct AccountRow { + #[tabled(rename = "Access")] + symbol: String, + #[tabled(rename = "Account")] + pubkey: String, + #[tabled(rename = "Type")] + access: String, + #[tabled(rename = "Name")] + name: String, +} + +/// Colors for terminal output +#[derive(Debug, Clone)] +pub struct Colors { + pub bold: String, + pub reset: String, + pub green: String, + pub red: String, + pub yellow: String, + pub blue: String, + pub cyan: String, + pub gray: String, +} + +impl Colors { + pub fn new(use_colors: bool) -> Self { + if use_colors { + Self { + bold: "\x1b[1m".to_string(), + reset: "\x1b[0m".to_string(), + green: "\x1b[32m".to_string(), + red: "\x1b[31m".to_string(), + yellow: "\x1b[33m".to_string(), + blue: "\x1b[34m".to_string(), + cyan: "\x1b[36m".to_string(), + gray: "\x1b[90m".to_string(), + } + } else { + Self { + bold: String::new(), + reset: String::new(), + green: String::new(), + red: String::new(), + yellow: String::new(), + blue: String::new(), + cyan: String::new(), + gray: String::new(), + } + } + } +} + +/// Transaction formatter with configurable output +pub struct TransactionFormatter { + config: EnhancedLoggingConfig, + colors: Colors, +} + +impl TransactionFormatter { + pub fn new(config: &EnhancedLoggingConfig) -> Self { + Self { + config: config.clone(), + colors: Colors::new(config.use_colors), + } + } + + /// Apply line breaks to long values in the complete output + fn apply_line_breaks(&self, text: &str) -> String { + let mut result = String::new(); + + for line in text.lines() { + // Look for patterns that need line breaking + if let Some(formatted_line) = self.format_line_if_needed(line) { + result.push_str(&formatted_line); + } else { + result.push_str(line); + } + result.push('\n'); + } + + result + } + + /// Format a line if it contains long values that need breaking + fn format_line_if_needed(&self, line: &str) -> Option { + // Extract leading whitespace/indentation and table characters + let leading_chars = line + .chars() + .take_while(|&c| c.is_whitespace() || "│├└┌┬┴┐┤─".contains(c)) + .collect::(); + + // Match patterns like "address: [0, 1, 2, 3, ...]" or "Raw instruction data (N bytes): [...]" + if line.contains(": [") && line.contains("]") { + // Handle byte arrays + if let Some(start) = line.find(": [") { + if let Some(end_pos) = line[start..].find(']') { + let end = start + end_pos; + let prefix = &line[..start + 2]; // Include ": " + let array_part = &line[start + 2..end + 1]; // The "[...]" part + let suffix = &line[end + 1..]; + + // For raw instruction data, use a shorter line length to better fit in terminal + let max_width = if line.contains("Raw instruction data") { + 80 // Wider for raw instruction data to fit more numbers per line + } else { + 50 // Keep existing width for other arrays + }; + + // Always format if it's raw instruction data or if it exceeds max_width + if line.contains("Raw instruction data") || array_part.len() > max_width { + let formatted_array = self.format_long_value_with_indent( + array_part, + max_width, + &leading_chars, + ); + return Some(format!("{}{}{}", prefix, formatted_array, suffix)); + } + } + } + } + + // Handle long base58 strings (44+ characters) in table cells + if line.contains('|') && !line.trim_start().starts_with('|') { + // This is a table content line, not a border + let mut new_line = String::new(); + let mut modified = false; + + // Split by table separators while preserving them + let parts: Vec<&str> = line.split('|').collect(); + for (i, part) in parts.iter().enumerate() { + if i > 0 { + new_line.push('|'); + } + + // Check if this cell contains a long value + for word in part.split_whitespace() { + if word.len() > 44 && word.chars().all(|c| c.is_alphanumeric()) { + let indent = " ".repeat(leading_chars.len() + 2); // Extra space for table formatting + let formatted_word = self.format_long_value_with_indent(word, 44, &indent); + new_line.push_str(&part.replace(word, &formatted_word)); + modified = true; + break; + } + } + + if !modified { + new_line.push_str(part); + } + } + + if modified { + return Some(new_line); + } + } + + None + } + + /// Format long value with proper indentation for continuation lines + fn format_long_value_with_indent(&self, value: &str, max_width: usize, indent: &str) -> String { + if value.len() <= max_width { + return value.to_string(); + } + + let mut result = String::new(); + + // Handle byte arrays specially by breaking at natural comma boundaries when possible + if value.starts_with('[') && value.ends_with(']') { + // This is a byte array - try to break at comma boundaries for better readability + let inner = &value[1..value.len() - 1]; // Remove [ and ] + let parts: Vec<&str> = inner.split(", ").collect(); + + result.push('['); + let mut current_line = String::new(); + let mut first_line = true; + + for (i, part) in parts.iter().enumerate() { + let addition = if i == 0 { + part.to_string() + } else { + format!(", {}", part) + }; + + // Check if adding this part would exceed the line width + if current_line.len() + addition.len() > max_width && !current_line.is_empty() { + // Add current line to result and start new line + if first_line { + result.push_str(¤t_line); + first_line = false; + } else { + result.push_str(&format!("\n{}{}", indent, current_line)); + } + current_line = part.to_string(); + } else { + current_line.push_str(&addition); + } + } + + // Add the last line + if !current_line.is_empty() { + if first_line { + result.push_str(¤t_line); + } else { + result.push_str(&format!("\n{}{}", indent, current_line)); + } + } + + result.push(']'); + } else { + // Fall back to character-based breaking for non-array values + let chars = value.chars().collect::>(); + let mut pos = 0; + + while pos < chars.len() { + let end = (pos + max_width).min(chars.len()); + let chunk: String = chars[pos..end].iter().collect(); + + if pos == 0 { + result.push_str(&chunk); + } else { + result.push_str(&format!("\n{}{}", indent, chunk)); + } + + pos = end; + } + } + + result + } + + /// Format complete transaction log + pub fn format(&self, log: &EnhancedTransactionLog, tx_number: usize) -> String { + let mut output = String::new(); + + // Transaction box header with number + writeln!(output, "{}┌───────────────────────────────────────── Transaction #{} ─────────────────────────────────────────────┐{}", self.colors.gray, tx_number, self.colors.reset).expect("Failed to write box header"); + + // Transaction header + self.write_transaction_header(&mut output, log) + .expect("Failed to write header"); + + // Instructions section + if !log.instructions.is_empty() { + self.write_instructions_section(&mut output, log) + .expect("Failed to write instructions"); + } + + // Account changes section + if self.config.show_account_changes && !log.account_changes.is_empty() { + self.write_account_changes_section(&mut output, log) + .expect("Failed to write account changes"); + } + + // Light Protocol events section + if !log.light_events.is_empty() { + self.write_light_events_section(&mut output, log) + .expect("Failed to write Light Protocol events"); + } + + // Program logs section (LiteSVM pretty logs) + if !log.program_logs_pretty.trim().is_empty() { + self.write_program_logs_section(&mut output, log) + .expect("Failed to write program logs"); + } + + // Transaction box footer + writeln!(output, "{}└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘{}", self.colors.gray, self.colors.reset).expect("Failed to write box footer"); + + // Apply line breaks for long values in the complete output + self.apply_line_breaks(&output) + } + + /// Write transaction header with status, fee, and compute units + fn write_transaction_header( + &self, + output: &mut String, + log: &EnhancedTransactionLog, + ) -> fmt::Result { + writeln!( + output, + "{}│{} {}Transaction: {}{} | Slot: {} | Status: {} {}{}", + self.colors.gray, + self.colors.reset, + self.colors.bold, + self.colors.cyan, + log.signature, + log.slot, + log.status.symbol(), + self.status_color(&log.status), + log.status.text(), + )?; + + writeln!( + output, + "{}│{} Fee: {}{:.6} SOL | Compute Used: {}{}/{} CU{}", + self.colors.gray, + self.colors.reset, + self.colors.yellow, + log.fee as f64 / 1_000_000_000.0, + self.colors.blue, + log.compute_used, + log.compute_total, + self.colors.reset + )?; + + writeln!(output, "{}│{}", self.colors.gray, self.colors.reset)?; + Ok(()) + } + + /// Write instructions hierarchy + fn write_instructions_section( + &self, + output: &mut String, + log: &EnhancedTransactionLog, + ) -> fmt::Result { + writeln!( + output, + "{}│{} {}Instructions ({}):{}", + self.colors.gray, + self.colors.reset, + self.colors.bold, + log.instructions.len(), + self.colors.reset + )?; + writeln!(output, "{}│{}", self.colors.gray, self.colors.reset)?; + + for (i, instruction) in log.instructions.iter().enumerate() { + self.write_instruction(output, instruction, 0, i + 1)?; + } + + Ok(()) + } + + /// Write single instruction with proper indentation and hierarchy + fn write_instruction( + &self, + output: &mut String, + instruction: &EnhancedInstructionLog, + depth: usize, + number: usize, + ) -> fmt::Result { + let indent = self.get_tree_indent(depth); + let prefix = if depth == 0 { "├─" } else { "└─" }; + + // Instruction header + let inner_count = if instruction.inner_instructions.is_empty() { + String::new() + } else { + format!(".{}", instruction.inner_instructions.len()) + }; + + write!( + output, + "{}{} {}#{}{} {}{} ({}{}{})", + indent, + prefix, + self.colors.bold, + number, + inner_count, + self.colors.blue, + instruction.program_id, + self.colors.cyan, + instruction.program_name, + self.colors.reset + )?; + + // Add instruction name if parsed + if let Some(ref name) = instruction.instruction_name { + write!( + output, + " - {}{}{}", + self.colors.yellow, name, self.colors.reset + )?; + } + + // Add compute units if available and requested + if self.config.show_compute_units { + if let Some(compute) = instruction.compute_consumed { + write!( + output, + " {}({}{}CU{})", + self.colors.gray, self.colors.blue, compute, self.colors.gray + )?; + } + } + + writeln!(output, "{}", self.colors.reset)?; + + // Show instruction details based on verbosity + match self.config.verbosity { + LogVerbosity::Detailed | LogVerbosity::Full => { + if let Some(ref parsed) = instruction.parsed_data { + self.write_parsed_instruction_data( + output, + parsed, + &instruction.data, + depth + 1, + )?; + } else if !instruction.data.is_empty() { + // Show raw instruction data for unparseable instructions with chunking + // Skip instruction data for account compression program unless explicitly configured + let should_show_data = if instruction.program_name == "Account Compression" { + self.config.show_compression_instruction_data + } else { + true + }; + + if should_show_data { + let indent = self.get_tree_indent(depth + 1); + writeln!( + output, + "{}{}Raw instruction data ({} bytes): {}[", + indent, + self.colors.gray, + instruction.data.len(), + self.colors.cyan + )?; + + // Chunk the data into 32-byte groups for better readability + for (i, chunk) in instruction.data.chunks(32).enumerate() { + write!(output, "{} ", indent)?; + for (j, byte) in chunk.iter().enumerate() { + if j > 0 { + write!(output, ", ")?; + } + write!(output, "{}", byte)?; + } + if i < instruction.data.chunks(32).len() - 1 { + writeln!(output, ",")?; + } else { + writeln!(output, "]{}", self.colors.reset)?; + } + } + } + } + } + _ => {} + } + + // Show accounts if verbose + if self.config.verbosity == LogVerbosity::Full && !instruction.accounts.is_empty() { + let accounts_indent = self.get_tree_indent(depth + 1); + writeln!( + output, + "{}{}Accounts ({}):{}", + accounts_indent, + self.colors.gray, + instruction.accounts.len(), + self.colors.reset + )?; + + // Create a table for better account formatting + let mut account_rows: Vec = Vec::new(); + + for account in instruction.accounts.iter() { + let access = if account.is_signer && account.is_writable { + AccountAccess::SignerWritable + } else if account.is_signer { + AccountAccess::Signer + } else if account.is_writable { + AccountAccess::Writable + } else { + AccountAccess::Readonly + }; + + let account_name = self.get_account_name(&account.pubkey); + account_rows.push(AccountRow { + symbol: access.symbol().to_string(), + pubkey: account.pubkey.to_string(), + access: access.text().to_string(), + name: account_name, + }); + } + + if !account_rows.is_empty() { + let table = Table::new(account_rows) + .to_string() + .lines() + .map(|line| format!("{}{}", accounts_indent, line)) + .collect::>() + .join("\n"); + writeln!(output, "{}", table)?; + } + } + + // Write inner instructions recursively + for (i, inner) in instruction.inner_instructions.iter().enumerate() { + if depth < self.config.max_inner_instruction_depth { + self.write_instruction(output, inner, depth + 1, i + 1)?; + } + } + + Ok(()) + } + + /// Write parsed instruction data + fn write_parsed_instruction_data( + &self, + output: &mut String, + parsed: &super::types::ParsedInstructionData, + instruction_data: &[u8], + depth: usize, + ) -> fmt::Result { + let indent = self.get_tree_indent(depth); + + match parsed { + super::types::ParsedInstructionData::LightSystemProgram { + instruction_type, + compressed_accounts, + proof_info, + address_params, + fee_info, + input_account_data, + output_account_data, + } => { + writeln!( + output, + "{}{}Light System: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + instruction_type, + self.colors.reset + )?; + + if let Some(compressed_accounts) = compressed_accounts { + writeln!( + output, + "{}{}Accounts: {}in: {}, out: {}{}", + indent, + self.colors.gray, + self.colors.cyan, + compressed_accounts.input_accounts, + compressed_accounts.output_accounts, + self.colors.reset + )?; + } + + if let Some(proof_info) = proof_info { + if proof_info.has_validity_proof { + writeln!( + output, + "{}{}Proof: {}{} proof{}", + indent, + self.colors.gray, + self.colors.cyan, + proof_info.proof_type, + self.colors.reset + )?; + } + } + + // Display input account data + if let Some(ref input_accounts) = input_account_data { + writeln!( + output, + "{}{}Input Accounts ({}):{}", + indent, + self.colors.gray, + input_accounts.len(), + self.colors.reset + )?; + for (i, acc_data) in input_accounts.iter().enumerate() { + writeln!( + output, + "{} {}[{}]{}", + indent, self.colors.gray, i, self.colors.reset + )?; + writeln!( + output, + "{} {}owner: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + acc_data + .owner + .map(|o| o.to_string()) + .unwrap_or("None".to_string()), + self.colors.reset + )?; + if let Some(ref address) = acc_data.address { + writeln!( + output, + "{} {}address: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + address, + self.colors.reset + )?; + } + writeln!( + output, + "{} {}lamports: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.lamports, + self.colors.reset + )?; + if !acc_data.data_hash.is_empty() { + writeln!( + output, + "{} {}data_hash: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.data_hash, + self.colors.reset + )?; + } + if !acc_data.discriminator.is_empty() { + writeln!( + output, + "{} {}discriminator: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.discriminator, + self.colors.reset + )?; + } + if let Some(tree_idx) = acc_data.merkle_tree_index { + if let Some(tree_pubkey) = acc_data.merkle_tree_pubkey { + writeln!( + output, + "{} {}merkle_tree_pubkey (index {}{}{}): {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + tree_idx, + self.colors.gray, + self.colors.yellow, + tree_pubkey, + self.colors.reset + )?; + } else { + writeln!( + output, + "{} {}merkle_tree_index: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + tree_idx, + self.colors.reset + )?; + } + } else if let Some(tree_pubkey) = acc_data.merkle_tree_pubkey { + writeln!( + output, + "{} {}merkle_tree_pubkey: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + tree_pubkey, + self.colors.reset + )?; + } + if let Some(queue_idx) = acc_data.queue_index { + if let Some(queue_pubkey) = acc_data.queue_pubkey { + writeln!( + output, + "{} {}queue_pubkey (index {}{}{}): {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + queue_idx, + self.colors.gray, + self.colors.yellow, + queue_pubkey, + self.colors.reset + )?; + } else { + writeln!( + output, + "{} {}queue_index: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + queue_idx, + self.colors.reset + )?; + } + } else if let Some(queue_pubkey) = acc_data.queue_pubkey { + writeln!( + output, + "{} {}queue_pubkey: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + queue_pubkey, + self.colors.reset + )?; + } + } + } + + // Display output account data + if let Some(ref output_data) = output_account_data { + writeln!( + output, + "{}{}Output Accounts ({}):{}", + indent, + self.colors.gray, + output_data.len(), + self.colors.reset + )?; + for (i, acc_data) in output_data.iter().enumerate() { + writeln!( + output, + "{} {}[{}]{}", + indent, self.colors.gray, i, self.colors.reset + )?; + writeln!( + output, + "{} {}owner: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + acc_data + .owner + .map(|o| o.to_string()) + .unwrap_or("None".to_string()), + self.colors.reset + )?; + if let Some(ref address) = acc_data.address { + writeln!( + output, + "{} {}address: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + address, + self.colors.reset + )?; + } + writeln!( + output, + "{} {}lamports: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.lamports, + self.colors.reset + )?; + if !acc_data.data_hash.is_empty() { + writeln!( + output, + "{} {}data_hash: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.data_hash, + self.colors.reset + )?; + } + if !acc_data.discriminator.is_empty() { + writeln!( + output, + "{} {}discriminator: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + acc_data.discriminator, + self.colors.reset + )?; + } + if let Some(ref data) = acc_data.data { + writeln!( + output, + "{} {}data ({} bytes): {}{:?}{}", + indent, + self.colors.gray, + data.len(), + self.colors.cyan, + data, + self.colors.reset + )?; + } + if let Some(tree_idx) = acc_data.merkle_tree_index { + if let Some(tree_pubkey) = acc_data.merkle_tree_pubkey { + writeln!( + output, + "{} {}merkle_tree_pubkey (index {}{}{}): {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + tree_idx, + self.colors.gray, + self.colors.yellow, + tree_pubkey, + self.colors.reset + )?; + } else { + writeln!( + output, + "{} {}merkle_tree_index: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + tree_idx, + self.colors.reset + )?; + } + } else if let Some(tree_pubkey) = acc_data.merkle_tree_pubkey { + writeln!( + output, + "{} {}merkle_tree_pubkey: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + tree_pubkey, + self.colors.reset + )?; + } + } + } + + // Display address parameters with actual values + if let Some(address_params) = address_params { + writeln!( + output, + "{}{}New Addresses ({}):{}", + indent, + self.colors.gray, + address_params.len(), + self.colors.reset + )?; + for (i, addr_param) in address_params.iter().enumerate() { + writeln!( + output, + "{} {}[{}] {}seed: {}{:?}{}", + indent, + self.colors.gray, + i, + self.colors.gray, + self.colors.cyan, + addr_param.seed, + self.colors.reset + )?; + if let Some(queue_idx) = addr_param.address_queue_index { + writeln!( + output, + "{} {}queue_idx: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + queue_idx, + self.colors.reset + )?; + } + if let Some(tree_idx) = addr_param.merkle_tree_index { + writeln!( + output, + "{} {}tree_idx: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + tree_idx, + self.colors.reset + )?; + } + if let Some(root_idx) = addr_param.root_index { + writeln!( + output, + "{} {}root_idx: {}{}{}", + indent, + self.colors.gray, + self.colors.cyan, + root_idx, + self.colors.reset + )?; + } + if let Some(ref derived_addr) = addr_param.derived_address { + writeln!( + output, + "{} {}address: {}{:?}{}", + indent, + self.colors.gray, + self.colors.cyan, + derived_addr, + self.colors.reset + )?; + } + } + } + + if let Some(fee_info) = fee_info { + if let Some(relay_fee) = fee_info.relay_fee { + writeln!( + output, + "{}{}Relay Fee: {}{} lamports{}", + indent, + self.colors.gray, + self.colors.yellow, + relay_fee, + self.colors.reset + )?; + } + if let Some(compression_fee) = fee_info.compression_fee { + writeln!( + output, + "{}{}Compression Fee: {}{} lamports{}", + indent, + self.colors.gray, + self.colors.yellow, + compression_fee, + self.colors.reset + )?; + } + } + } + super::types::ParsedInstructionData::ComputeBudget { + instruction_type, + value, + } => { + write!( + output, + "{}{}Compute Budget: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + instruction_type, + self.colors.reset + )?; + + if let Some(val) = value { + writeln!(output, " ({})", val)?; + } else { + writeln!(output)?; + } + } + super::types::ParsedInstructionData::System { + instruction_type, + lamports, + space: _, + new_account: _, + } => { + write!( + output, + "{}{}System: {}{}{}", + indent, + self.colors.gray, + self.colors.yellow, + instruction_type, + self.colors.reset + )?; + + if let Some(amount) = lamports { + writeln!(output, " ({} lamports)", amount)?; + } else { + writeln!(output)?; + } + } + super::types::ParsedInstructionData::Unknown { + program_name, + data_preview: _, + } => { + writeln!( + output, + "{}{}Program: {}{}{}", + indent, self.colors.gray, self.colors.yellow, program_name, self.colors.reset + )?; + + // Show raw instruction data for unknown programs with chunking + // Skip instruction data for account compression program unless explicitly configured + let should_show_data = if program_name == "Account Compression" { + self.config.show_compression_instruction_data + } else { + true + }; + + if !instruction_data.is_empty() && should_show_data { + writeln!( + output, + "{}{}Raw instruction data ({} bytes): {}[", + indent, + self.colors.gray, + instruction_data.len(), + self.colors.cyan + )?; + + // Chunk the data into 32-byte groups for better readability + for (i, chunk) in instruction_data.chunks(32).enumerate() { + write!(output, "{} ", indent)?; + for (j, byte) in chunk.iter().enumerate() { + if j > 0 { + write!(output, ", ")?; + } + write!(output, "{}", byte)?; + } + if i < instruction_data.chunks(32).len() - 1 { + writeln!(output, ",")?; + } else { + writeln!(output, "]{}", self.colors.reset)?; + } + } + } + } + } + + Ok(()) + } + + /// Write account changes section + fn write_account_changes_section( + &self, + output: &mut String, + log: &EnhancedTransactionLog, + ) -> fmt::Result { + writeln!(output)?; + writeln!( + output, + "{}Account Changes ({}):{}\n", + self.colors.bold, + log.account_changes.len(), + self.colors.reset + )?; + + for change in &log.account_changes { + self.write_account_change(output, change)?; + } + + Ok(()) + } + + /// Write single account change + fn write_account_change(&self, output: &mut String, change: &AccountChange) -> fmt::Result { + writeln!( + output, + "│ {}{} {} ({}) - {}{}{}", + change.access.symbol(), + self.colors.cyan, + change.pubkey, + change.access.text(), + self.colors.yellow, + change.account_type, + self.colors.reset + )?; + + if change.lamports_before != change.lamports_after { + writeln!( + output, + "│ {}Lamports: {} → {}{}", + self.colors.gray, change.lamports_before, change.lamports_after, self.colors.reset + )?; + } + + Ok(()) + } + + /// Write Light Protocol events section + fn write_light_events_section( + &self, + output: &mut String, + log: &EnhancedTransactionLog, + ) -> fmt::Result { + writeln!(output)?; + writeln!( + output, + "{}Light Protocol Events ({}):{}\n", + self.colors.bold, + log.light_events.len(), + self.colors.reset + )?; + + for event in &log.light_events { + writeln!( + output, + "│ {}Event: {}{}{}", + self.colors.blue, self.colors.yellow, event.event_type, self.colors.reset + )?; + + if !event.compressed_accounts.is_empty() { + writeln!( + output, + "│ {}Compressed Accounts: {}{}", + self.colors.gray, + event.compressed_accounts.len(), + self.colors.reset + )?; + } + + if !event.merkle_tree_changes.is_empty() { + writeln!( + output, + "│ {}Merkle Tree Changes: {}{}", + self.colors.gray, + event.merkle_tree_changes.len(), + self.colors.reset + )?; + } + } + + Ok(()) + } + + /// Write program logs section using LiteSVM's pretty logs + fn write_program_logs_section( + &self, + output: &mut String, + log: &EnhancedTransactionLog, + ) -> fmt::Result { + writeln!(output)?; + writeln!( + output, + "{}│{} {}Program Logs:{}", + self.colors.gray, self.colors.reset, self.colors.bold, self.colors.reset + )?; + writeln!(output, "{}│{}", self.colors.gray, self.colors.reset)?; + + // Display LiteSVM's pretty formatted logs with proper indentation + for line in log.program_logs_pretty.lines() { + if !line.trim().is_empty() { + writeln!( + output, + "{}│{} {}", + self.colors.gray, self.colors.reset, line + )?; + } + } + + Ok(()) + } + + /// Get tree-style indentation for given depth + fn get_tree_indent(&self, depth: usize) -> String { + let border = format!("{}│{} ", self.colors.gray, self.colors.reset); + if depth == 0 { + border + } else { + format!("{}{}", border, "│ ".repeat(depth)) + } + } + + /// Get color for transaction status + fn status_color(&self, status: &TransactionStatus) -> &str { + match status { + TransactionStatus::Success => &self.colors.green, + TransactionStatus::Failed(_) => &self.colors.red, + TransactionStatus::Unknown => &self.colors.yellow, + } + } + + /// Get human-readable name for known accounts using constants and test accounts + fn get_account_name(&self, pubkey: &solana_sdk::pubkey::Pubkey) -> String { + let pubkey_bytes = pubkey.to_bytes(); + + // Light Protocol Programs and Accounts from constants + if pubkey_bytes == light_sdk_types::constants::LIGHT_SYSTEM_PROGRAM_ID { + return "light system program".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::ACCOUNT_COMPRESSION_PROGRAM_ID { + return "account compression program".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::REGISTERED_PROGRAM_PDA { + return "registered program pda".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::ACCOUNT_COMPRESSION_AUTHORITY_PDA { + return "account compression authority".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::NOOP_PROGRAM_ID { + return "noop program".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::C_TOKEN_PROGRAM_ID { + return "compressed token program".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::ADDRESS_TREE_V1 { + return "address tree v1".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::ADDRESS_QUEUE_V1 { + return "address queue v1".to_string(); + } + if pubkey_bytes == light_sdk_types::constants::SOL_POOL_PDA { + return "sol pool pda".to_string(); + } + + // String-based matches for test accounts and other addresses + match pubkey.to_string().as_str() { + "FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy" => "test program".to_string(), + + // Test accounts from test_accounts.rs - Local Test Validator + "smt1NamzXdq4AMqS2fS2F1i5KTYPZRhoHgWx38d8WsT" => "v1 state merkle tree".to_string(), + "nfq1NvQDJ2GEgnS8zt9prAe8rjjpAW1zFkrvZoBR148" => "v1 nullifier queue".to_string(), + "cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4" => "v1 cpi context".to_string(), + "amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2" => "v1 address merkle tree".to_string(), + "aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F" => "v1 address queue".to_string(), + + // V2 State Trees and Queues (test accounts) + "6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU" => "v2 state output queue".to_string(), + "HLKs5NJ8FXkJg8BrzJt56adFYYuwg5etzDtBbQYTsixu" => "v2 state merkle tree".to_string(), + "7Hp52chxaew8bW1ApR4fck2bh6Y8qA1pu3qwH6N9zaLj" => "v2 cpi context".to_string(), + + // V2 Address Trees (test accounts) + "EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK" => "v2 address merkle tree".to_string(), + + // CPI Authority (commonly used in tests) + "HZH7qSLcpAeDqCopVU4e5XkhT9j3JFsQiq8CmruY3aru" => "cpi authority pda".to_string(), + + // Solana Native Programs + id if id == system_program::ID.to_string() => "system program".to_string(), + "ComputeBudget111111111111111111111111111111" => "compute budget program".to_string(), + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" => "token program".to_string(), + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" => { + "associated token program".to_string() + } + + _ => { + // Check if it's a PDA or regular account + if pubkey.is_on_curve() { + "user account".to_string() + } else { + "pda account".to_string() + } + } + } + } +} diff --git a/sdk-libs/program-test/src/logging/formatter.rs:1218:19 b/sdk-libs/program-test/src/logging/formatter.rs:1218:19 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk-libs/program-test/src/logging/mod.rs b/sdk-libs/program-test/src/logging/mod.rs new file mode 100644 index 0000000000..a1bd94b8bb --- /dev/null +++ b/sdk-libs/program-test/src/logging/mod.rs @@ -0,0 +1,201 @@ +//! Enhanced logging system for light-program-test +//! +//! This module provides Solana Explorer-like transaction logging with: +//! - Hierarchical instruction display with inner instructions +//! - Account changes tracking +//! - Light Protocol specific parsing and formatting +//! - Configurable verbosity levels +//! - Color-coded output +//! +//! Logging behavior: +//! - File logging: Always enabled when `enhanced_logging.enabled = true` (default) +//! - Log file: Written to `target/light_program_test.log` +//! - Console output: Only when `RUST_BACKTRACE` is set AND `log_events = true` +//! - Log file is overwritten at session start, then appended for each transaction + +pub mod config; +pub mod decoder; +pub mod formatter; +pub mod types; + +use std::{ + fs::OpenOptions, + io::Write, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, +}; + +use chrono; +pub use config::{EnhancedLoggingConfig, LogVerbosity}; +pub use formatter::TransactionFormatter; +use litesvm::types::TransactionResult; +use solana_sdk::{signature::Signature, transaction::Transaction}; +pub use types::{ + AccountChange, EnhancedInstructionLog, EnhancedTransactionLog, ParsedInstructionData, + TransactionStatus, +}; + +use crate::program_test::config::ProgramTestConfig; + +static SESSION_STARTED: std::sync::Once = std::sync::Once::new(); + +/// Get the log file path in target directory +fn get_log_file_path() -> PathBuf { + // Always use cargo workspace target directory + use std::process::Command; + if let Ok(output) = Command::new("cargo") + .arg("metadata") + .arg("--format-version=1") + .arg("--no-deps") + .output() + { + if output.status.success() { + if let Ok(metadata) = String::from_utf8(output.stdout) { + if let Ok(json) = serde_json::from_str::(&metadata) { + if let Some(target_directory) = json["target_directory"].as_str() { + let mut path = PathBuf::from(target_directory); + path.push("light_program_test.log"); + return path; + } + } + } + } + } + + // Fallback to current directory's target + let mut path = PathBuf::from("target"); + path.push("light_program_test.log"); + path +} + +/// Initialize log file with session header (called only once per session) +fn initialize_log_file() { + SESSION_STARTED.call_once(|| { + let log_path = get_log_file_path(); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + // Create new log file with session header + if let Ok(mut file) = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&log_path) + { + // Format timestamp as readable date + let datetime = + chrono::DateTime::from_timestamp(timestamp as i64, 0).unwrap_or(chrono::Utc::now()); + let formatted_date = datetime.format("%Y-%m-%d %H:%M:%S UTC"); + + let _ = writeln!( + file, + "=== Light Program Test Session Started at {} ===\n", + formatted_date + ); + } + }); +} + +/// Strip ANSI escape codes from string for plain text log files +fn strip_ansi_codes(text: &str) -> String { + // Simple regex-free approach to remove ANSI escape sequences + let mut result = String::with_capacity(text.len()); + let mut chars = text.chars(); + + while let Some(ch) = chars.next() { + if ch == '\x1b' { + // Found escape character, skip until we find 'm' (end of color code) + for next_ch in chars.by_ref() { + if next_ch == 'm' { + break; + } + } + } else { + result.push(ch); + } + } + + result +} + +/// Write log entry to file (append to existing session log) +fn write_to_log_file(content: &str) { + // Ensure session is initialized + initialize_log_file(); + + let log_path = get_log_file_path(); + + // Ensure parent directory exists + if let Some(parent) = log_path.parent() { + let _ = std::fs::create_dir_all(parent); + } + + // Strip ANSI color codes for file output + let clean_content = strip_ansi_codes(content); + + // Append transaction log to existing file + if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(&log_path) { + let _ = writeln!(file, "{}", clean_content); + } +} + +/// Main entry point for enhanced transaction logging +pub fn log_transaction_enhanced( + config: &ProgramTestConfig, + transaction: &Transaction, + result: &TransactionResult, + signature: &Signature, + slot: u64, + transaction_counter: usize, +) { + log_transaction_enhanced_with_console( + config, + transaction, + result, + signature, + slot, + transaction_counter, + false, + ) +} + +/// Enhanced transaction logging with console output control +pub fn log_transaction_enhanced_with_console( + config: &ProgramTestConfig, + transaction: &Transaction, + result: &TransactionResult, + signature: &Signature, + slot: u64, + transaction_counter: usize, + print_to_console: bool, +) { + if !config.enhanced_logging.enabled { + return; + } + + let enhanced_log = EnhancedTransactionLog::from_transaction_result( + transaction, + result, + signature, + slot, + &config.enhanced_logging, + ); + + let formatter = TransactionFormatter::new(&config.enhanced_logging); + let formatted_log = formatter.format(&enhanced_log, transaction_counter); + + // Always write to log file when enhanced logging is enabled + write_to_log_file(&formatted_log); + + // Print to console if requested + if print_to_console { + println!("{}", formatted_log); + } +} + +/// Check if enhanced logging should be used instead of basic logging +pub fn should_use_enhanced_logging(config: &ProgramTestConfig) -> bool { + config.enhanced_logging.enabled && !config.no_logs +} diff --git a/sdk-libs/program-test/src/logging/types.rs b/sdk-libs/program-test/src/logging/types.rs new file mode 100644 index 0000000000..0f4490cbfb --- /dev/null +++ b/sdk-libs/program-test/src/logging/types.rs @@ -0,0 +1,480 @@ +//! Type definitions for enhanced logging + +use solana_sdk::{ + inner_instruction::InnerInstruction, instruction::AccountMeta, pubkey::Pubkey, + signature::Signature, system_program, +}; + +use super::config::EnhancedLoggingConfig; + +/// Enhanced transaction log containing all formatting information +#[derive(Debug, Clone)] +pub struct EnhancedTransactionLog { + pub signature: Signature, + pub slot: u64, + pub status: TransactionStatus, + pub fee: u64, + pub compute_used: u64, + pub compute_total: u64, + pub instructions: Vec, + pub account_changes: Vec, + pub program_logs_pretty: String, + pub light_events: Vec, +} + +/// Transaction execution status +#[derive(Debug, Clone)] +pub enum TransactionStatus { + Success, + Failed(String), + Unknown, +} + +impl TransactionStatus { + pub fn symbol(&self) -> &'static str { + match self { + TransactionStatus::Success => "✅", + TransactionStatus::Failed(_) => "❌", + TransactionStatus::Unknown => "⚠️", + } + } + + pub fn text(&self) -> String { + match self { + TransactionStatus::Success => "Success".to_string(), + TransactionStatus::Failed(err) => format!("Failed: {}", err), + TransactionStatus::Unknown => "Unknown".to_string(), + } + } +} + +/// Enhanced instruction log with hierarchy and parsing +#[derive(Debug, Clone)] +pub struct EnhancedInstructionLog { + pub index: usize, + pub program_id: Pubkey, + pub program_name: String, + pub instruction_name: Option, + pub accounts: Vec, + pub data: Vec, + pub parsed_data: Option, + pub inner_instructions: Vec, + pub compute_consumed: Option, + pub success: bool, + pub depth: usize, +} + +/// Parsed instruction data for known programs +#[derive(Debug, Clone)] +pub enum ParsedInstructionData { + LightSystemProgram { + instruction_type: String, + compressed_accounts: Option, + proof_info: Option, + address_params: Option>, + fee_info: Option, + input_account_data: Option>, + output_account_data: Option>, + }, + ComputeBudget { + instruction_type: String, + value: Option, + }, + System { + instruction_type: String, + lamports: Option, + space: Option, + new_account: Option, + }, + Unknown { + program_name: String, + data_preview: String, + }, +} + +/// Summary of compressed accounts in a Light Protocol instruction +#[derive(Debug, Clone)] +pub struct CompressedAccountSummary { + pub input_accounts: usize, + pub output_accounts: usize, + pub lamports_change: Option, +} + +/// Summary of proof information +#[derive(Debug, Clone)] +pub struct ProofSummary { + pub proof_type: String, + pub has_validity_proof: bool, +} + +/// Summary of fee information +#[derive(Debug, Clone)] +pub struct FeeSummary { + pub relay_fee: Option, + pub compression_fee: Option, +} + +/// Address parameter information +#[derive(Debug, Clone)] +pub struct AddressParam { + pub seed: [u8; 32], + pub address_queue_index: Option, + pub merkle_tree_index: Option, + pub root_index: Option, + pub derived_address: Option<[u8; 32]>, +} + +/// Input account data +#[derive(Debug, Clone)] +pub struct InputAccountData { + pub lamports: u64, + pub owner: Option, + pub merkle_tree_index: Option, + pub merkle_tree_pubkey: Option, + pub queue_index: Option, + pub queue_pubkey: Option, + pub address: Option<[u8; 32]>, + pub data_hash: Vec, + pub discriminator: Vec, +} + +/// Output account data +#[derive(Debug, Clone)] +pub struct OutputAccountData { + pub lamports: u64, + pub data: Option>, + pub owner: Option, + pub merkle_tree_index: Option, + pub merkle_tree_pubkey: Option, + pub queue_index: Option, + pub queue_pubkey: Option, + pub address: Option<[u8; 32]>, + pub data_hash: Vec, + pub discriminator: Vec, +} + +/// Account state changes during transaction +#[derive(Debug, Clone)] +pub struct AccountChange { + pub pubkey: Pubkey, + pub account_type: String, + pub access: AccountAccess, + pub lamports_before: u64, + pub lamports_after: u64, + pub data_len_before: usize, + pub data_len_after: usize, + pub owner: Pubkey, + pub executable: bool, + pub rent_epoch: u64, +} + +/// Account access pattern during transaction +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AccountAccess { + Readonly, + Writable, + Signer, + SignerWritable, +} + +impl AccountAccess { + pub fn symbol(&self) -> &'static str { + match self { + AccountAccess::Readonly => "👁️", + AccountAccess::Writable => "✏️", + AccountAccess::Signer => "🔑", + AccountAccess::SignerWritable => "🔐", + } + } + + pub fn text(&self) -> &'static str { + match self { + AccountAccess::Readonly => "readonly", + AccountAccess::Writable => "writable", + AccountAccess::Signer => "signer", + AccountAccess::SignerWritable => "signer+writable", + } + } +} + +/// Light Protocol specific events +#[derive(Debug, Clone)] +pub struct LightProtocolEvent { + pub event_type: String, + pub compressed_accounts: Vec, + pub merkle_tree_changes: Vec, + pub nullifiers: Vec, +} + +/// Compressed account information +#[derive(Debug, Clone)] +pub struct CompressedAccountInfo { + pub hash: String, + pub owner: Pubkey, + pub lamports: u64, + pub data: Option>, + pub address: Option, +} + +/// Merkle tree state change +#[derive(Debug, Clone)] +pub struct MerkleTreeChange { + pub tree_pubkey: Pubkey, + pub tree_type: String, + pub sequence_number: u64, + pub leaf_index: u64, +} + +impl EnhancedTransactionLog { + /// Use LiteSVM's pretty logs instead of parsing raw logs + fn get_pretty_logs_string(result: &litesvm::types::TransactionResult) -> String { + match result { + Ok(meta) => meta.pretty_logs(), + Err(failed) => failed.meta.pretty_logs(), + } + } + + /// Create from LiteSVM transaction result + pub fn from_transaction_result( + transaction: &solana_sdk::transaction::Transaction, + result: &litesvm::types::TransactionResult, + signature: &Signature, + slot: u64, + config: &EnhancedLoggingConfig, + ) -> Self { + let (status, compute_consumed) = match result { + Ok(meta) => (TransactionStatus::Success, meta.compute_units_consumed), + Err(failed) => ( + TransactionStatus::Failed(format!("{:?}", failed.err)), + failed.meta.compute_units_consumed, + ), + }; + + // Calculate estimated fee (basic calculation: signatures * lamports_per_signature) + // Default Solana fee is 5000 lamports per signature + let estimated_fee = (transaction.signatures.len() as u64) * 5000; + + // Parse instructions + let instructions: Vec = transaction + .message + .instructions + .iter() + .enumerate() + .map(|(index, ix)| EnhancedInstructionLog { + index, + program_id: transaction.message.account_keys[ix.program_id_index as usize], + program_name: get_program_name( + &transaction.message.account_keys[ix.program_id_index as usize], + ), + instruction_name: None, // Will be filled by decoder + accounts: ix + .accounts + .iter() + .map(|&idx| AccountMeta { + pubkey: transaction.message.account_keys[idx as usize], + is_signer: transaction.message.is_signer(idx as usize), + is_writable: transaction.message.is_maybe_writable(idx as usize, None), + }) + .collect(), + data: ix.data.clone(), + parsed_data: None, // Will be filled by decoder + inner_instructions: Vec::new(), // Will be filled from meta + compute_consumed: None, + success: true, + depth: 0, + }) + .collect(); + + // Extract inner instructions from LiteSVM metadata + let inner_instructions_list = match result { + Ok(meta) => &meta.inner_instructions, + Err(failed) => &failed.meta.inner_instructions, + }; + + // Apply decoder to instructions if enabled and populate inner instructions + let mut instructions = instructions; + if config.decode_light_instructions { + // First, decode all top-level instructions + for instruction in instructions.iter_mut() { + instruction.parsed_data = super::decoder::decode_instruction( + &instruction.program_id, + &instruction.data, + &instruction.accounts, + ); + if let Some(ref parsed) = instruction.parsed_data { + instruction.instruction_name = match parsed { + ParsedInstructionData::LightSystemProgram { + instruction_type, .. + } => Some(instruction_type.clone()), + ParsedInstructionData::ComputeBudget { + instruction_type, .. + } => Some(instruction_type.clone()), + ParsedInstructionData::System { + instruction_type, .. + } => Some(instruction_type.clone()), + _ => None, + }; + } + } + + // Now populate inner instructions for each top-level instruction + for (instruction_index, inner_list) in inner_instructions_list.iter().enumerate() { + if let Some(instruction) = instructions.get_mut(instruction_index) { + instruction.inner_instructions = Self::parse_inner_instructions( + inner_list, // inner_list is already Vec + &transaction.message.account_keys, + &transaction.message, // Pass the full message for account access info + 1, // Start at depth 1 for inner instructions + config, + ); + } + } + } + + // Get LiteSVM's pretty formatted logs + let pretty_logs_string = Self::get_pretty_logs_string(result); + + Self { + signature: *signature, + slot, + status, + fee: estimated_fee, + compute_used: compute_consumed, + compute_total: 1_400_000, // Default compute limit + instructions, + account_changes: Vec::new(), // Will be filled if requested + program_logs_pretty: pretty_logs_string, + light_events: Vec::new(), + } + } + + /// Parse inner instructions from Solana's InnerInstruction format with proper nesting + fn parse_inner_instructions( + inner_instructions: &[InnerInstruction], + account_keys: &[Pubkey], + message: &solana_sdk::message::Message, + base_depth: usize, + config: &EnhancedLoggingConfig, + ) -> Vec { + let mut result = Vec::new(); + + for (index, inner_ix) in inner_instructions.iter().enumerate() { + let program_id = account_keys[inner_ix.instruction.program_id_index as usize]; + let program_name = get_program_name(&program_id); + + let accounts: Vec = inner_ix + .instruction + .accounts + .iter() + .map(|&idx| { + let account_index = idx as usize; + let pubkey = account_keys[account_index]; + + // Get the correct signer and writable information from the original transaction message + let is_signer = message.is_signer(account_index); + let is_writable = message.is_maybe_writable(account_index, None); + + AccountMeta { + pubkey, + is_signer, + is_writable, + } + }) + .collect(); + + let parsed_data = if config.decode_light_instructions { + super::decoder::decode_instruction( + &program_id, + &inner_ix.instruction.data, + &accounts, + ) + } else { + None + }; + + let instruction_name = parsed_data.as_ref().and_then(|parsed| match parsed { + ParsedInstructionData::LightSystemProgram { + instruction_type, .. + } => Some(instruction_type.clone()), + ParsedInstructionData::ComputeBudget { + instruction_type, .. + } => Some(instruction_type.clone()), + ParsedInstructionData::System { + instruction_type, .. + } => Some(instruction_type.clone()), + _ => None, + }); + + // Calculate the actual depth based on stack_height + // stack_height 2 = first level CPI (depth = base_depth + 1) + // stack_height 3 = second level CPI (depth = base_depth + 2), etc. + let instruction_depth = base_depth + (inner_ix.stack_height as usize).saturating_sub(1); + + let instruction_log = EnhancedInstructionLog { + index, + program_id, + program_name, + instruction_name, + accounts, + data: inner_ix.instruction.data.clone(), + parsed_data, + inner_instructions: Vec::new(), + compute_consumed: None, + success: true, // We assume inner instructions succeeded if we're parsing them + depth: instruction_depth, + }; + + // Find the correct parent for this instruction based on stack height + // Stack height 2 = direct CPI, should be at top level + // Stack height 3+ = nested CPI, should be child of previous instruction with stack_height - 1 + if inner_ix.stack_height <= 2 { + // Top-level CPI - add directly to result + result.push(instruction_log); + } else { + // Nested CPI - find the appropriate parent + // We need to traverse the result structure to find the right parent + let target_parent_depth = instruction_depth - 1; + if let Some(parent) = + Self::find_parent_for_instruction(&mut result, target_parent_depth) + { + parent.inner_instructions.push(instruction_log); + } else { + // Fallback: add to top level if we can't find appropriate parent + result.push(instruction_log); + } + } + } + + result + } + + /// Helper function to find the appropriate parent for nested instructions + fn find_parent_for_instruction( + instructions: &mut [EnhancedInstructionLog], + target_depth: usize, + ) -> Option<&mut EnhancedInstructionLog> { + for instruction in instructions.iter_mut().rev() { + if instruction.depth == target_depth { + return Some(instruction); + } + // Recursively search in inner instructions + if let Some(parent) = + Self::find_parent_for_instruction(&mut instruction.inner_instructions, target_depth) + { + return Some(parent); + } + } + None + } +} +/// Get human-readable program name from pubkey +fn get_program_name(program_id: &Pubkey) -> String { + match program_id.to_string().as_str() { + id if id == system_program::ID.to_string() => "System Program".to_string(), + "ComputeBudget111111111111111111111111111111" => "Compute Budget".to_string(), + "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" => "Light System Program".to_string(), + "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq" => "Account Compression".to_string(), + "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m" => "Compressed Token Program".to_string(), + _ => format!("Unknown Program ({})", program_id), + } +} diff --git a/sdk-libs/program-test/src/logging/types.rs:473:15 b/sdk-libs/program-test/src/logging/types.rs:473:15 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk-libs/program-test/src/program_test/config.rs b/sdk-libs/program-test/src/program_test/config.rs index 33d8262b50..aec0faf78c 100644 --- a/sdk-libs/program-test/src/program_test/config.rs +++ b/sdk-libs/program-test/src/program_test/config.rs @@ -9,6 +9,9 @@ use light_prover_client::prover::ProverConfig; use light_registry::protocol_config::state::ProtocolConfig; use solana_sdk::pubkey::Pubkey; +use crate::logging::EnhancedLoggingConfig; + +/// Configuration for Light Program Test #[derive(Debug, Clone)] pub struct ProgramTestConfig { pub additional_programs: Option>, @@ -25,9 +28,16 @@ pub struct ProgramTestConfig { pub v2_state_tree_config: Option, pub v2_address_tree_config: Option, pub skip_protocol_init: bool, + /// Log failed transactions pub log_failed_tx: bool, + /// Disable all logging pub no_logs: bool, + /// Skip startup logs pub skip_startup_logs: bool, + /// Log Light Protocol events (BatchPublicTransactionEvent, etc.) + pub log_light_protocol_events: bool, + /// Enhanced transaction logging configuration + pub enhanced_logging: EnhancedLoggingConfig, } impl ProgramTestConfig { @@ -77,19 +87,17 @@ impl ProgramTestConfig { } } - // TODO: uncomment once batched trees are on devnet. - // #[cfg(not(feature = "devenv"))] - // pub fn default_with_batched_trees() -> Self { - // Self { - // additional_programs: None, - // with_prover: false, - // v2_state_tree_config: Some(InitStateTreeAccountsInstructionData::default()), - // v2_address_tree_config: Some( - // InitAddressTreeAccountsInstructionData::default(), - // ), - // ..Default::default() - // } - // } + /// Enable Light Protocol event logging + pub fn with_light_protocol_events(mut self) -> Self { + self.log_light_protocol_events = true; + self + } + + /// Disable Light Protocol event logging + pub fn without_light_protocol_events(mut self) -> Self { + self.log_light_protocol_events = false; + self + } } impl Default for ProgramTestConfig { @@ -119,6 +127,8 @@ impl Default for ProgramTestConfig { log_failed_tx: true, no_logs: false, skip_startup_logs: true, + log_light_protocol_events: false, // Disabled by default + enhanced_logging: EnhancedLoggingConfig::from_env(), } } } 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 938d071573..cdb19b3e7d 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 @@ -26,6 +26,7 @@ pub struct LightProgramTest { pub indexer: Option, pub test_accounts: TestAccounts, pub payer: Keypair, + pub transaction_counter: usize, } impl LightProgramTest { @@ -58,6 +59,7 @@ impl LightProgramTest { test_accounts: TestAccounts::get_program_test_test_accounts(), payer, config: config.clone(), + transaction_counter: 0, }; let keypairs = TestKeypairs::program_test_default(); @@ -112,6 +114,8 @@ impl LightProgramTest { context.set_account(address_queue_pubkey, account); } } + // reset tx counter after program setup. + context.transaction_counter = 0; // Will always start a prover server. #[cfg(feature = "devenv")] let prover_config = if config.prover_config.is_none() { @@ -152,11 +156,6 @@ impl LightProgramTest { self.test_accounts.v1_address_trees[0] } - #[cfg(feature = "v2")] - pub fn get_address_merkle_tree_v2(&self) -> solana_sdk::pubkey::Pubkey { - self.test_accounts.v2_address_trees[0] - } - pub async fn add_indexer( &mut self, test_accounts: &TestAccounts, diff --git a/sdk-libs/program-test/src/program_test/rpc.rs b/sdk-libs/program-test/src/program_test/rpc.rs index 01821323da..cc0095eb2e 100644 --- a/sdk-libs/program-test/src/program_test/rpc.rs +++ b/sdk-libs/program-test/src/program_test/rpc.rs @@ -138,9 +138,12 @@ impl Rpc for LightProgramTest { ) -> Result { let sig = *transaction.signatures.first().unwrap(); if self.indexer.is_some() { + // Delegate to _send_transaction_with_batched_event which handles counter and logging self._send_transaction_with_batched_event(transaction) .await?; } else { + // Handle transaction directly without logging (logging should be done elsewhere) + self.transaction_counter += 1; let _res = self.context.send_transaction(transaction).map_err(|x| { if self.config.log_failed_tx { println!("{}", x.meta.pretty_logs()); @@ -148,6 +151,7 @@ impl Rpc for LightProgramTest { RpcError::TransactionError(x.err) })?; + self.maybe_print_logs(_res.pretty_logs()); } Ok(sig) @@ -158,15 +162,17 @@ impl Rpc for LightProgramTest { transaction: Transaction, ) -> Result<(Signature, Slot), RpcError> { let sig = *transaction.signatures.first().unwrap(); + self.transaction_counter += 1; let _res = self.context.send_transaction(transaction).map_err(|x| { if self.config.log_failed_tx { println!("{}", x.meta.pretty_logs()); } RpcError::TransactionError(x.err) })?; - self.maybe_print_logs(_res.pretty_logs()); let slot = self.context.get_sysvar::().slot; + self.maybe_print_logs(_res.pretty_logs()); + Ok((sig, slot)) } @@ -287,17 +293,8 @@ impl Rpc for LightProgramTest { tree_type: TreeType::AddressV1, } } -} -impl LightProgramTest { - fn maybe_print_logs(&self, logs: impl std::fmt::Display) { - if !self.config.no_logs && cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_ok() - { - println!("{}", logs); - } - } - #[cfg(feature = "v2")] - pub fn get_address_tree_v2(&self) -> TreeInfo { + fn get_address_tree_v2(&self) -> TreeInfo { TreeInfo { tree: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), queue: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"), @@ -306,6 +303,22 @@ impl LightProgramTest { tree_type: TreeType::AddressV2, } } +} + +impl LightProgramTest { + fn maybe_print_logs(&self, logs: impl std::fmt::Display) { + // Use enhanced logging if enabled and RUST_BACKTRACE is set + if crate::logging::should_use_enhanced_logging(&self.config) { + // Enhanced logging will be handled in the transaction processing methods + return; + } + + // Fallback to basic logging + if !self.config.no_logs && cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_ok() + { + println!("{}", logs); + } + } async fn _send_transaction_with_batched_event( &mut self, @@ -314,20 +327,72 @@ impl LightProgramTest { let mut vec = Vec::new(); let signature = transaction.signatures[0]; - // Simulate the transaction. Currently, in banks-client/server, only - // simulations are able to track CPIs. Therefore, simulating is the - // only way to retrieve the event. - let simulation_result = self - .context - .simulate_transaction(transaction.clone()) - .map_err(|x| { - if self.config.log_failed_tx { - println!("{}", x.meta.pretty_logs()); - } + let transaction_for_logging = transaction.clone(); // Clone for logging + // Simulate the transaction. Currently, in banks-client/server, only + // simulations are able to track CPIs. Therefore, simulating is the + // only way to retrieve the event. + let simulation_result = self.context.simulate_transaction(transaction.clone()); - RpcError::TransactionError(x.err) - })?; + // Transaction was successful, execute it. + self.transaction_counter += 1; + let transaction_result = self.context.send_transaction(transaction.clone()); + let slot = self.context.get_sysvar::().slot; + + // Always try enhanced logging for file output (both success and failure) + if crate::logging::should_use_enhanced_logging(&self.config) { + crate::logging::log_transaction_enhanced( + &self.config, + &transaction_for_logging, + &transaction_result, + &signature, + slot, + self.transaction_counter, + ); + } + + // Handle transaction result after logging + let _res = transaction_result.as_ref().map_err(|x| { + // Prevent duplicate prints for failing tx. + if self.config.log_failed_tx { + crate::logging::log_transaction_enhanced_with_console( + &self.config, + &transaction_for_logging, + &transaction_result, + &signature, + slot, + self.transaction_counter, + true, // Enable console output + ); + } + RpcError::TransactionError(x.err.clone()) + })?; + + // Console logging - if RUST_BACKTRACE is set, print to console too + if !self.config.no_logs && std::env::var("RUST_BACKTRACE").is_ok() { + if crate::logging::should_use_enhanced_logging(&self.config) { + // Print enhanced logs to console + crate::logging::log_transaction_enhanced_with_console( + &self.config, + &transaction_for_logging, + &transaction_result, + &signature, + slot, + self.transaction_counter, + true, // Enable console output + ); + + // if self.config.log_light_protocol_events { + // if let Some(ref event_data) = event { + // println!("event:\n {:?}", event_data); + // } + // } + } else { + // Fallback to basic log printing + self.maybe_print_logs(_res.pretty_logs()); + } + } + let simulation_result = simulation_result.unwrap(); // Try old event deserialization. let event = simulation_result .meta @@ -392,29 +457,9 @@ impl LightProgramTest { ParseIndexerEventError, >(None))? }; - - // Transaction was successful, execute it. - let _res = self.context.send_transaction(transaction).map_err(|x| { - // Prevent duplicate prints for failing tx. - - if self.config.log_failed_tx { - println!("{}", x.meta.pretty_logs()); - } - - RpcError::TransactionError(x.err) - })?; - if !self.config.no_logs { - #[cfg(debug_assertions)] - { - if std::env::var("RUST_BACKTRACE").is_ok() { - // Print all tx logs and events. - println!("{}", _res.pretty_logs()); - println!("event:\n {:?}", event); - } - } + if self.config.log_light_protocol_events { + println!("event:\n {:?}", event); } - - let slot = self.context.get_sysvar::().slot; let event = event.map(|e| (e, signature, slot)); if let Some(indexer) = self.indexer.as_mut() { @@ -466,6 +511,7 @@ impl LightProgramTest { T::try_from_slice(&inner_instruction.instruction.data).ok() }); // If transaction was successful, execute it. + self.transaction_counter += 1; let _res = self.context.send_transaction(transaction).map_err(|x| { if self.config.log_failed_tx { println!("{}", x.meta.pretty_logs()); 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 dabf1315e4..f054e58e30 100644 --- a/sdk-libs/program-test/src/utils/setup_light_programs.rs +++ b/sdk-libs/program-test/src/utils/setup_light_programs.rs @@ -26,7 +26,7 @@ use crate::{ pub fn setup_light_programs( additional_programs: Option>, ) -> Result { - let program_test = LiteSVM::new(); + let program_test = LiteSVM::new().with_log_bytes_limit(Some(100_000)); let program_test = program_test.with_compute_budget(ComputeBudget { compute_unit_limit: 1_400_000, ..Default::default() diff --git a/sdk-tests/client-test/Cargo.toml b/sdk-tests/client-test/Cargo.toml index 8436feda1b..59e3dc76c1 100644 --- a/sdk-tests/client-test/Cargo.toml +++ b/sdk-tests/client-test/Cargo.toml @@ -7,12 +7,9 @@ license = "Apache-2.0" edition = "2021" [lib] -crate-type = ["cdylib", "lib"] +crate-type = ["lib"] name = "client_test" -[features] -test-sbf = [] - [dev-dependencies] light-client = { workspace = true, features = ["devenv"] } light-program-test = { workspace = true, features = ["devenv"] } diff --git a/sdk-tests/client-test/tests/light_program_test.rs b/sdk-tests/client-test/tests/light_program_test.rs index 087674193a..52c4e1a8a3 100644 --- a/sdk-tests/client-test/tests/light_program_test.rs +++ b/sdk-tests/client-test/tests/light_program_test.rs @@ -1,4 +1,3 @@ -#![cfg(feature = "test-sbf")] use light_client::{ indexer::{ AddressWithTree, GetCompressedTokenAccountsByOwnerOrDelegateOptions, Hash, Indexer, diff --git a/sdk-tests/sdk-anchor-test/package.json b/sdk-tests/sdk-anchor-test/package.json index a248fd9fff..f69a01a509 100644 --- a/sdk-tests/sdk-anchor-test/package.json +++ b/sdk-tests/sdk-anchor-test/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "cargo test-sbf -p sdk-test" + "test": "cargo test-sbf -p sdk-native-test" }, "dependencies": { "@coral-xyz/anchor": "^0.29.0" diff --git a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs index 96949c26e5..9f8662e393 100644 --- a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs +++ b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs @@ -18,7 +18,7 @@ use solana_sdk::{ }; #[tokio::test] -async fn test_sdk_test() { +async fn test_anchor_sdk_test() { let config = ProgramTestConfig::new_v2(true, Some(vec![("sdk_anchor_test", sdk_anchor_test::ID)])); let mut rpc = LightProgramTest::new(config).await.unwrap(); @@ -92,7 +92,7 @@ async fn create_compressed_account( ) -> Result { let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); let mut remaining_accounts = PackedAccounts::default(); - remaining_accounts.add_system_accounts(config); + remaining_accounts.add_system_accounts(config).unwrap(); let address_merkle_tree_info = rpc.get_address_tree_v1(); @@ -149,7 +149,7 @@ async fn update_compressed_account( let mut remaining_accounts = PackedAccounts::default(); let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); - remaining_accounts.add_system_accounts(config); + remaining_accounts.add_system_accounts(config).unwrap(); let hash = compressed_account.hash; let rpc_result = rpc diff --git a/sdk-tests/sdk-test/Cargo.toml b/sdk-tests/sdk-native-test/Cargo.toml similarity index 95% rename from sdk-tests/sdk-test/Cargo.toml rename to sdk-tests/sdk-native-test/Cargo.toml index 8ca0da7072..54b5978963 100644 --- a/sdk-tests/sdk-test/Cargo.toml +++ b/sdk-tests/sdk-native-test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sdk-test" +name = "sdk-native-test" version = "1.0.0" description = "Test program using generalized account compression" repository = "https://github.com/Lightprotocol/light-protocol" @@ -8,7 +8,7 @@ edition = "2021" [lib] crate-type = ["cdylib", "lib"] -name = "sdk_test" +name = "sdk_native_test" [features] no-entrypoint = [] diff --git a/sdk-tests/sdk-test/Xargo.toml b/sdk-tests/sdk-native-test/Xargo.toml similarity index 100% rename from sdk-tests/sdk-test/Xargo.toml rename to sdk-tests/sdk-native-test/Xargo.toml diff --git a/sdk-tests/sdk-test/src/create_pda.rs b/sdk-tests/sdk-native-test/src/create_pda.rs similarity index 100% rename from sdk-tests/sdk-test/src/create_pda.rs rename to sdk-tests/sdk-native-test/src/create_pda.rs diff --git a/sdk-tests/sdk-test/src/lib.rs b/sdk-tests/sdk-native-test/src/lib.rs similarity index 100% rename from sdk-tests/sdk-test/src/lib.rs rename to sdk-tests/sdk-native-test/src/lib.rs diff --git a/sdk-tests/sdk-test/src/update_pda.rs b/sdk-tests/sdk-native-test/src/update_pda.rs similarity index 100% rename from sdk-tests/sdk-test/src/update_pda.rs rename to sdk-tests/sdk-native-test/src/update_pda.rs diff --git a/sdk-tests/sdk-test/tests/test.rs b/sdk-tests/sdk-native-test/tests/test.rs similarity index 88% rename from sdk-tests/sdk-test/tests/test.rs rename to sdk-tests/sdk-native-test/tests/test.rs index 0615866a06..90d8fd78bf 100644 --- a/sdk-tests/sdk-test/tests/test.rs +++ b/sdk-tests/sdk-native-test/tests/test.rs @@ -1,4 +1,4 @@ -//#![cfg(feature = "test-sbf")] +#![cfg(feature = "test-sbf")] use borsh::BorshSerialize; use light_compressed_account::{ @@ -11,7 +11,7 @@ use light_program_test::{ use light_sdk::instruction::{ account_meta::CompressedAccountMeta, PackedAccounts, SystemAccountMetaConfig, }; -use sdk_test::{ +use sdk_native_test::{ create_pda::CreatePdaInstructionData, update_pda::{UpdateMyCompressedAccount, UpdatePdaInstructionData}, ARRAY_LEN, @@ -23,26 +23,27 @@ use solana_sdk::{ }; #[tokio::test] -async fn test_sdk_test() { - let config = ProgramTestConfig::new_v2(true, Some(vec![("sdk_test", sdk_test::ID)])); +async fn test_sdk_native_test() { + let config = + ProgramTestConfig::new_v2(true, Some(vec![("sdk_native_test", sdk_native_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 address_tree_pubkey = rpc.get_address_tree_v2().tree; let account_data = [1u8; ARRAY_LEN]; // // V1 trees // let (address, _) = light_sdk::address::derive_address( // &[b"compressed", &account_data], // &address_tree_info, - // &sdk_test::ID, + // &sdk_native_test::ID, // ); // Batched trees let address_seed = hashv_to_bn254_field_size_be(&[b"compressed", account_data.as_slice()]); let address = derive_address( &address_seed, &address_tree_pubkey.to_bytes(), - &sdk_test::ID.to_bytes(), + &sdk_native_test::ID.to_bytes(), ); let ouput_queue = rpc.get_random_state_tree_info().unwrap().queue; create_pda( @@ -79,10 +80,12 @@ pub async fn create_pda( address_tree_pubkey: Pubkey, address: [u8; 32], ) -> Result<(), RpcError> { - let system_account_meta_config = SystemAccountMetaConfig::new(sdk_test::ID); + let system_account_meta_config = SystemAccountMetaConfig::new(sdk_native_test::ID); let mut accounts = PackedAccounts::default(); accounts.add_pre_accounts_signer(payer.pubkey()); - accounts.add_system_accounts(system_account_meta_config); + accounts + .add_system_accounts(system_account_meta_config) + .unwrap(); let rpc_result = rpc .get_validity_proof( @@ -111,7 +114,7 @@ pub async fn create_pda( let inputs = instruction_data.try_to_vec().unwrap(); let instruction = Instruction { - program_id: sdk_test::ID, + program_id: sdk_native_test::ID, accounts, data: [&[0u8][..], &inputs[..]].concat(), }; @@ -127,10 +130,12 @@ pub async fn update_pda( new_account_data: [u8; ARRAY_LEN], compressed_account: CompressedAccountWithMerkleContext, ) -> Result<(), RpcError> { - let system_account_meta_config = SystemAccountMetaConfig::new(sdk_test::ID); + let system_account_meta_config = SystemAccountMetaConfig::new(sdk_native_test::ID); let mut accounts = PackedAccounts::default(); accounts.add_pre_accounts_signer(payer.pubkey()); - accounts.add_system_accounts(system_account_meta_config); + accounts + .add_system_accounts(system_account_meta_config) + .unwrap(); let rpc_result = rpc .get_validity_proof(vec![compressed_account.hash().unwrap()], vec![], None) @@ -167,7 +172,7 @@ pub async fn update_pda( let inputs = instruction_data.try_to_vec().unwrap(); let instruction = Instruction { - program_id: sdk_test::ID, + program_id: sdk_native_test::ID, accounts, data: [&[1u8][..], &inputs[..]].concat(), }; diff --git a/sdk-tests/sdk-pinocchio-test/tests/test.rs b/sdk-tests/sdk-pinocchio-test/tests/test.rs index a4a62e7eab..861ac8b679 100644 --- a/sdk-tests/sdk-pinocchio-test/tests/test.rs +++ b/sdk-tests/sdk-pinocchio-test/tests/test.rs @@ -21,7 +21,7 @@ use solana_sdk::{ }; #[tokio::test] -async fn test_sdk_test() { +async fn test_pinocchio_sdk_test() { let config = ProgramTestConfig::new_v2( false, Some(vec![( @@ -32,7 +32,7 @@ async fn test_sdk_test() { 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 address_tree_pubkey = rpc.get_address_tree_v2(); let account_data = [1u8; 31]; // // V1 trees @@ -45,7 +45,7 @@ async fn test_sdk_test() { let address_seed = hashv_to_bn254_field_size_be(&[b"compressed", account_data.as_slice()]); let address = derive_address( &address_seed, - &address_tree_pubkey.to_bytes(), + &address_tree_pubkey.tree.to_bytes(), &sdk_pinocchio_test::ID, ); @@ -56,7 +56,7 @@ async fn test_sdk_test() { &mut rpc, &output_queue, account_data, - address_tree_pubkey, + address_tree_pubkey.tree, address, ) .await @@ -94,7 +94,9 @@ pub async fn create_pda( 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); + accounts + .add_system_accounts(system_account_meta_config) + .unwrap(); let rpc_result = rpc .get_validity_proof( @@ -142,7 +144,9 @@ pub async fn update_pda( 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); + accounts + .add_system_accounts(system_account_meta_config) + .unwrap(); let rpc_result = rpc .get_validity_proof(vec![compressed_account.hash().unwrap()], vec![], None) From b771b9e5191e623816b3076789b23d7871365795 Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 1 Sep 2025 13:54:41 +0100 Subject: [PATCH 2/6] stash --- Cargo.lock | 46 +++++++++---------- anchor-programs/system/src/lib.rs | 1 + .../create-address-test-program/src/lib.rs | 1 + program-tests/registry-test/tests/tests.rs | 1 + program-tests/system-cpi-test/src/lib.rs | 1 + .../resize_registered_program_account.rs | 2 +- programs/account-compression/src/lib.rs | 1 + programs/compressed-token/src/lib.rs | 1 + programs/registry/src/lib.rs | 1 + sdk-libs/client/src/indexer/types.rs | 8 ++++ sdk-libs/client/src/rpc/client.rs | 10 ++++ sdk-libs/client/src/rpc/rpc_trait.rs | 9 ++-- .../programs/sdk-anchor-test/src/lib.rs | 1 + .../programs/sdk-anchor-test/tests/test.rs | 4 +- sdk-tests/sdk-native-test/tests/test.rs | 8 +--- sdk-tests/sdk-pinocchio-test/tests/test.rs | 8 +--- 16 files changed, 61 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1ca7eac59..63a1c141f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5964,7 +5964,7 @@ checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" dependencies = [ "num-bigint 0.4.6", "num-traits", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5985,7 +5985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" dependencies = [ "blake3", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-hash", "solana-sanitize", ] @@ -6001,7 +6001,7 @@ dependencies = [ "ark-ff 0.4.2", "ark-serialize 0.4.2", "bytemuck", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.16", ] @@ -6406,7 +6406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" dependencies = [ "solana-account-info", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-instruction", "solana-program-error", "solana-pubkey", @@ -6422,7 +6422,7 @@ dependencies = [ "bytemuck", "bytemuck_derive", "curve25519-dalek 4.1.3", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "subtle", "thiserror 2.0.16", ] @@ -6442,6 +6442,11 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf784bb2cb3e02cac9801813c30187344228d2ae952534902108f6150573a33d" +[[package]] +name = "solana-define-syscall" +version = "2.2.1" +source = "git+https://github.com/anza-xyz/solana-sdk?rev=1c1d667f161666f12f5a43ebef8eda9470a8c6ee#1c1d667f161666f12f5a43ebef8eda9470a8c6ee" + [[package]] name = "solana-derivation-path" version = "2.2.1" @@ -6696,7 +6701,7 @@ dependencies = [ "num-traits", "serde", "serde_derive", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-pubkey", "wasm-bindgen", ] @@ -6725,7 +6730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" dependencies = [ "sha3", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-hash", "solana-sanitize", ] @@ -6907,7 +6912,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" dependencies = [ - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -7053,7 +7058,7 @@ checksum = "230110ef64d44bc5f261249e45dde64cc176598c5c05728a118b8979e0c865ff" dependencies = [ "ark-bn254 0.4.0", "light-poseidon 0.2.0", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.16", ] @@ -7130,7 +7135,7 @@ dependencies = [ "solana-clock", "solana-cpi", "solana-decode-error", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-epoch-rewards", "solana-epoch-schedule", "solana-example-mocks", @@ -7205,11 +7210,11 @@ dependencies = [ [[package]] name = "solana-program-memory" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +version = "2.2.1" +source = "git+https://github.com/anza-xyz/solana-sdk?rev=1c1d667f161666f12f5a43ebef8eda9470a8c6ee#1c1d667f161666f12f5a43ebef8eda9470a8c6ee" dependencies = [ - "solana-define-syscall", + "num-traits", + "solana-define-syscall 2.2.1 (git+https://github.com/anza-xyz/solana-sdk?rev=1c1d667f161666f12f5a43ebef8eda9470a8c6ee)", ] [[package]] @@ -7288,7 +7293,7 @@ dependencies = [ "serde_derive", "solana-atomic-u64", "solana-decode-error", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-sanitize", "solana-sha256-hasher", "wasm-bindgen", @@ -7684,7 +7689,7 @@ checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" dependencies = [ "borsh 1.5.7", "libsecp256k1", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.16", ] @@ -7764,7 +7769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0037386961c0d633421f53560ad7c80675c0447cba4d1bb66d60974dd486c7ea" dependencies = [ "sha2 0.10.9", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-hash", ] @@ -8033,7 +8038,7 @@ dependencies = [ "serde_derive", "solana-account-info", "solana-clock", - "solana-define-syscall", + "solana-define-syscall 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-epoch-rewards", "solana-epoch-schedule", "solana-fee-calculator", @@ -10760,8 +10765,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "solana-program-memory" -version = "2.2.1" -source = "git+https://github.com/anza-xyz/solana-sdk?rev=1c1d667f161666f12f5a43ebef8eda9470a8c6ee#1c1d667f161666f12f5a43ebef8eda9470a8c6ee" diff --git a/anchor-programs/system/src/lib.rs b/anchor-programs/system/src/lib.rs index 36a92162f5..decdd915f1 100644 --- a/anchor-programs/system/src/lib.rs +++ b/anchor-programs/system/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use anchor_lang::prelude::*; pub mod account_traits; diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index 90c6d100d3..b0ad818d44 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::too_many_arguments)] #![allow(unexpected_cfgs)] +#![allow(deprecated)] use account_compression::utils::constants::{CPI_AUTHORITY_PDA_SEED, NOOP_PUBKEY}; use anchor_lang::{ diff --git a/program-tests/registry-test/tests/tests.rs b/program-tests/registry-test/tests/tests.rs index 4b4765ec1e..f363c5c85e 100644 --- a/program-tests/registry-test/tests/tests.rs +++ b/program-tests/registry-test/tests/tests.rs @@ -193,6 +193,7 @@ async fn test_initialize_protocol_config() { test_accounts: TestAccounts::get_program_test_test_accounts(), payer, config: ProgramTestConfig::default(), + transaction_counter: 0, }; let payer = rpc.get_payer().insecure_clone(); diff --git a/program-tests/system-cpi-test/src/lib.rs b/program-tests/system-cpi-test/src/lib.rs index 325f84452d..32126b8fac 100644 --- a/program-tests/system-cpi-test/src/lib.rs +++ b/program-tests/system-cpi-test/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::too_many_arguments)] +#![allow(deprecated)] use account_compression::{program::AccountCompression, utils::constants::CPI_AUTHORITY_PDA_SEED}; use anchor_lang::{prelude::*, solana_program::pubkey::Pubkey}; pub mod create_pda; diff --git a/programs/account-compression/src/instructions/resize_registered_program_account.rs b/programs/account-compression/src/instructions/resize_registered_program_account.rs index d7c2da4b8f..bb95b73ee2 100644 --- a/programs/account-compression/src/instructions/resize_registered_program_account.rs +++ b/programs/account-compression/src/instructions/resize_registered_program_account.rs @@ -67,7 +67,7 @@ pub fn process_resize_registered_program_pda<'info>( ], )?; - account_info.realloc(RegisteredProgram::LEN, false)?; + account_info.resize(RegisteredProgram::LEN)?; } // Initialize registered_program_signer_pda with derived signer pda. diff --git a/programs/account-compression/src/lib.rs b/programs/account-compression/src/lib.rs index fdbc67d428..f19d7c8245 100644 --- a/programs/account-compression/src/lib.rs +++ b/programs/account-compression/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::too_many_arguments)] #![allow(unexpected_cfgs)] +#![allow(deprecated)] pub mod errors; pub mod instructions; pub use instructions::*; diff --git a/programs/compressed-token/src/lib.rs b/programs/compressed-token/src/lib.rs index 08aee63691..7e3cbbedd0 100644 --- a/programs/compressed-token/src/lib.rs +++ b/programs/compressed-token/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use anchor_lang::prelude::*; pub mod constants; diff --git a/programs/registry/src/lib.rs b/programs/registry/src/lib.rs index eda4e62106..2cc392431f 100644 --- a/programs/registry/src/lib.rs +++ b/programs/registry/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::too_many_arguments)] +#![allow(deprecated)] use account_compression::{ utils::constants::CPI_AUTHORITY_PDA_SEED, AddressMerkleTreeConfig, AddressQueueConfig, NullifierQueueConfig, StateMerkleTreeConfig, diff --git a/sdk-libs/client/src/indexer/types.rs b/sdk-libs/client/src/indexer/types.rs index 51d804e40b..8a408dc77e 100644 --- a/sdk-libs/client/src/indexer/types.rs +++ b/sdk-libs/client/src/indexer/types.rs @@ -458,6 +458,14 @@ impl TreeInfo { } } + pub fn get_output_pubkey(&self) -> Result { + match self.tree_type { + TreeType::StateV1 => Ok(self.tree), + TreeType::StateV2 => Ok(self.queue), + _ => Err(IndexerError::InvalidPackTreeType), + } + } + pub fn from_api_model( value: &photon_api::models::MerkleContextV2, ) -> Result { diff --git a/sdk-libs/client/src/rpc/client.rs b/sdk-libs/client/src/rpc/client.rs index 05419e8a2b..af3fcb1641 100644 --- a/sdk-libs/client/src/rpc/client.rs +++ b/sdk-libs/client/src/rpc/client.rs @@ -750,6 +750,16 @@ impl Rpc for LightClient { tree_type: TreeType::AddressV1, } } + + 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, + } + } } impl MerkleTreeExt for LightClient {} diff --git a/sdk-libs/client/src/rpc/rpc_trait.rs b/sdk-libs/client/src/rpc/rpc_trait.rs index 0cb349e368..09b664d239 100644 --- a/sdk-libs/client/src/rpc/rpc_trait.rs +++ b/sdk-libs/client/src/rpc/rpc_trait.rs @@ -169,8 +169,10 @@ pub trait Rpc: Send + Sync + Debug + 'static { signers: &'a [&'a Keypair], ) -> Result { let blockhash = self.get_latest_blockhash().await?.0; - let transaction = - Transaction::new_signed_with_payer(instructions, Some(payer), signers, blockhash); + let mut transaction = Transaction::new_with_payer(instructions, Some(payer)); + transaction + .try_sign(signers, blockhash) + .map_err(|e| RpcError::CustomError(e.to_string()))?; self.process_transaction(transaction).await } @@ -204,6 +206,5 @@ pub trait Rpc: Send + Sync + Debug + 'static { fn get_address_tree_v1(&self) -> TreeInfo; - // TODO: add with v2 release - // fn get_address_tree_v2(&self) -> Result, RpcError>; + fn get_address_tree_v2(&self) -> TreeInfo; } diff --git a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs index 9f5f0367d5..ba0242cc30 100644 --- a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs +++ b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs @@ -1,4 +1,5 @@ #![allow(unexpected_cfgs)] +#![allow(deprecated)] use anchor_lang::{prelude::*, Discriminator}; use light_sdk::{ diff --git a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs index 9f8662e393..92e268446e 100644 --- a/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs +++ b/sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs @@ -92,7 +92,7 @@ async fn create_compressed_account( ) -> Result { let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); let mut remaining_accounts = PackedAccounts::default(); - remaining_accounts.add_system_accounts(config).unwrap(); + remaining_accounts.add_system_accounts(config); let address_merkle_tree_info = rpc.get_address_tree_v1(); @@ -149,7 +149,7 @@ async fn update_compressed_account( let mut remaining_accounts = PackedAccounts::default(); let config = SystemAccountMetaConfig::new(sdk_anchor_test::ID); - remaining_accounts.add_system_accounts(config).unwrap(); + remaining_accounts.add_system_accounts(config); let hash = compressed_account.hash; let rpc_result = rpc diff --git a/sdk-tests/sdk-native-test/tests/test.rs b/sdk-tests/sdk-native-test/tests/test.rs index 90d8fd78bf..ea1e690657 100644 --- a/sdk-tests/sdk-native-test/tests/test.rs +++ b/sdk-tests/sdk-native-test/tests/test.rs @@ -83,9 +83,7 @@ pub async fn create_pda( let system_account_meta_config = SystemAccountMetaConfig::new(sdk_native_test::ID); let mut accounts = PackedAccounts::default(); accounts.add_pre_accounts_signer(payer.pubkey()); - accounts - .add_system_accounts(system_account_meta_config) - .unwrap(); + accounts.add_system_accounts(system_account_meta_config); let rpc_result = rpc .get_validity_proof( @@ -133,9 +131,7 @@ pub async fn update_pda( let system_account_meta_config = SystemAccountMetaConfig::new(sdk_native_test::ID); let mut accounts = PackedAccounts::default(); accounts.add_pre_accounts_signer(payer.pubkey()); - accounts - .add_system_accounts(system_account_meta_config) - .unwrap(); + accounts.add_system_accounts(system_account_meta_config); let rpc_result = rpc .get_validity_proof(vec![compressed_account.hash().unwrap()], vec![], None) diff --git a/sdk-tests/sdk-pinocchio-test/tests/test.rs b/sdk-tests/sdk-pinocchio-test/tests/test.rs index 861ac8b679..b0dd79a5f3 100644 --- a/sdk-tests/sdk-pinocchio-test/tests/test.rs +++ b/sdk-tests/sdk-pinocchio-test/tests/test.rs @@ -94,9 +94,7 @@ pub async fn create_pda( 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) - .unwrap(); + accounts.add_system_accounts(system_account_meta_config); let rpc_result = rpc .get_validity_proof( @@ -144,9 +142,7 @@ pub async fn update_pda( 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) - .unwrap(); + accounts.add_system_accounts(system_account_meta_config); let rpc_result = rpc .get_validity_proof(vec![compressed_account.hash().unwrap()], vec![], None) From 70eee3ad972518106c82c43438735bdec1109df5 Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 1 Sep 2025 14:17:56 +0100 Subject: [PATCH 3/6] refactor: github ci --- .../workflows/light-system-programs-tests.yml | 2 +- .github/workflows/rust.yml | 13 ++----------- ...light-examples-tests.yml => sdk-tests.yml} | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 15 deletions(-) rename .github/workflows/{light-examples-tests.yml => sdk-tests.yml} (72%) diff --git a/.github/workflows/light-system-programs-tests.yml b/.github/workflows/light-system-programs-tests.yml index a106c91af9..5cb979b492 100644 --- a/.github/workflows/light-system-programs-tests.yml +++ b/.github/workflows/light-system-programs-tests.yml @@ -15,7 +15,7 @@ on: paths: - "programs/**" - "program-tests/**" - - "program-libs/verifier/**" + - "program-libs/**" - "merkle-tree/**" - ".github/workflows/light-system-programs-tests.yml" - "test-utils/**" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 737037a766..c894ef9bcf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,7 +4,7 @@ on: push: branches: [main] paths: - - "**/*.rs" + - "program-libs/**" - "**/Cargo.*" - ".cargo/**" - "prover/client/**" @@ -12,6 +12,7 @@ on: pull_request: branches: ["*"] paths: + - "program-libs/**" - "**/*.rs" - "**/Cargo.*" - "prover/client/**" @@ -65,16 +66,6 @@ jobs: cargo test -p light-bloom-filter --all-features cargo test -p light-indexed-merkle-tree --all-features cargo test -p light-batched-merkle-tree --all-features -- --test test_e2e - - name: sdk-libs - packages: light-macros light-sdk light-program-test light-client light-batched-merkle-tree - test_cmd: | - cargo test -p light-macros - cargo test -p light-sdk - cargo test -p light-program-test - cargo test -p light-client - cargo test -p client-test - cargo test -p light-sparse-merkle-tree - cargo test -p light-batched-merkle-tree --features test-only -- --skip test_simulate_transactions --skip test_e2e name: Test ${{ matrix.group.name }} diff --git a/.github/workflows/light-examples-tests.yml b/.github/workflows/sdk-tests.yml similarity index 72% rename from .github/workflows/light-examples-tests.yml rename to .github/workflows/sdk-tests.yml index fada8d6fca..f8cf063f65 100644 --- a/.github/workflows/light-examples-tests.yml +++ b/.github/workflows/sdk-tests.yml @@ -3,13 +3,17 @@ on: branches: - main paths: - - "examples/**" + - "program-libs/**" + - "programs/**" + - "sdk-tests/**" - "sdk-libs/**" pull_request: branches: - "*" paths: - - "examples/**" + - "programs/**" + - "program-libs/**" + - "sdk-tests/**" - "sdk-libs/**" types: - opened @@ -51,7 +55,16 @@ jobs: sub-tests: '["cargo-test-sbf -p sdk-native-test"]' - program: sdk-anchor-test-program sub-tests: '["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk-pinocchio-test"]' - + - program: sdk-libs + packages: light-macros light-sdk light-program-test light-client light-batched-merkle-tree + test_cmd: | + cargo test -p light-macros + cargo test -p light-sdk + cargo test -p light-program-test + cargo test -p light-client + cargo test -p client-test + cargo test -p light-sparse-merkle-tree + cargo test -p light-batched-merkle-tree --features test-only -- --skip test_simulate_transactions --skip test_e2e steps: - name: Checkout sources uses: actions/checkout@v4 From e01c23a975bc43043837d14db0a106ca67baf9ee Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 1 Sep 2025 14:30:04 +0100 Subject: [PATCH 4/6] replace program test logging emojis with numbers --- .github/workflows/rust.yml | 1 - sdk-libs/program-test/src/logging/formatter.rs | 8 ++++---- sdk-libs/program-test/src/logging/types.rs | 10 +++------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c894ef9bcf..1099ac1ce4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,7 +13,6 @@ on: branches: ["*"] paths: - "program-libs/**" - - "**/*.rs" - "**/Cargo.*" - "prover/client/**" - ".github/workflows/rust.yml" diff --git a/sdk-libs/program-test/src/logging/formatter.rs b/sdk-libs/program-test/src/logging/formatter.rs index 966c2aae35..6fcf26d844 100644 --- a/sdk-libs/program-test/src/logging/formatter.rs +++ b/sdk-libs/program-test/src/logging/formatter.rs @@ -16,7 +16,7 @@ use super::{ /// Row for account table display #[derive(Tabled)] struct AccountRow { - #[tabled(rename = "Access")] + #[tabled(rename = "#")] symbol: String, #[tabled(rename = "Account")] pubkey: String, @@ -469,7 +469,7 @@ impl TransactionFormatter { // Create a table for better account formatting let mut account_rows: Vec = Vec::new(); - for account in instruction.accounts.iter() { + for (idx, account) in instruction.accounts.iter().enumerate() { let access = if account.is_signer && account.is_writable { AccountAccess::SignerWritable } else if account.is_signer { @@ -482,7 +482,7 @@ impl TransactionFormatter { let account_name = self.get_account_name(&account.pubkey); account_rows.push(AccountRow { - symbol: access.symbol().to_string(), + symbol: access.symbol(idx + 1), pubkey: account.pubkey.to_string(), access: access.text().to_string(), name: account_name, @@ -1046,7 +1046,7 @@ impl TransactionFormatter { writeln!( output, "│ {}{} {} ({}) - {}{}{}", - change.access.symbol(), + change.access.symbol(change.account_index), self.colors.cyan, change.pubkey, change.access.text(), diff --git a/sdk-libs/program-test/src/logging/types.rs b/sdk-libs/program-test/src/logging/types.rs index 0f4490cbfb..bda5d221c2 100644 --- a/sdk-libs/program-test/src/logging/types.rs +++ b/sdk-libs/program-test/src/logging/types.rs @@ -159,6 +159,7 @@ pub struct AccountChange { pub pubkey: Pubkey, pub account_type: String, pub access: AccountAccess, + pub account_index: usize, pub lamports_before: u64, pub lamports_after: u64, pub data_len_before: usize, @@ -178,13 +179,8 @@ pub enum AccountAccess { } impl AccountAccess { - pub fn symbol(&self) -> &'static str { - match self { - AccountAccess::Readonly => "👁️", - AccountAccess::Writable => "✏️", - AccountAccess::Signer => "🔑", - AccountAccess::SignerWritable => "🔐", - } + pub fn symbol(&self, index: usize) -> String { + format!("#{}", index) } pub fn text(&self) -> &'static str { From 4ccfb5197a1598c7866378b9884c0779f4273f35 Mon Sep 17 00:00:00 2001 From: ananas Date: Tue, 2 Sep 2025 16:55:40 +0100 Subject: [PATCH 5/6] fix: ci --- .github/workflows/sdk-tests.yml | 12 ++++++++++-- programs/package.json | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sdk-tests.yml b/.github/workflows/sdk-tests.yml index f8cf063f65..3785ea6811 100644 --- a/.github/workflows/sdk-tests.yml +++ b/.github/workflows/sdk-tests.yml @@ -79,14 +79,22 @@ jobs: source ./scripts/devenv.sh npx nx build @lightprotocol/programs - - name: ${{ matrix.program }} + - name: Run sub-tests for ${{ matrix.program }} + if: matrix.sub-tests != null run: | source ./scripts/devenv.sh npx nx build @lightprotocol/zk-compression-cli - IFS=',' read -r -a sub_tests <<< "${{ join(fromJSON(matrix['sub-tests']), ', ') }}" + IFS=',' read -r -a sub_tests <<< "${{ join(fromJSON(matrix.sub-tests), ', ') }}" for subtest in "${sub_tests[@]}" do echo "$subtest" eval "RUSTFLAGS=\"-D warnings\" $subtest" done + + - name: Run tests for ${{ matrix.program }} + if: matrix.test_cmd != null + run: | + source ./scripts/devenv.sh + npx nx build @lightprotocol/zk-compression-cli + ${{ matrix.test_cmd }} diff --git a/programs/package.json b/programs/package.json index c505a6e42c..f19b06e82b 100644 --- a/programs/package.json +++ b/programs/package.json @@ -12,7 +12,7 @@ "test-compressed-token": "cargo test-sbf -p compressed-token-test", "e2e-test": "cargo-test-sbf -p e2e-test", "test-registry": "cargo-test-sbf -p registry-test", - "sdk-test-program": "cargo test-sbf -p sdk-test", + "sdk-test-program": "cargo test-sbf -p sdk-native-test", "test-system": "cargo test-sbf -p system-test", "test-system-cpi": "cargo test-sbf -p system-cpi-test", "ignored-program-owned-account-test": "cargo-test-sbf -p program-owned-account-test" From d9c3d961883a466ee565dc0405613eaba7c097e8 Mon Sep 17 00:00:00 2001 From: ananas Date: Tue, 2 Sep 2025 17:02:37 +0100 Subject: [PATCH 6/6] add signer error --- sdk-libs/client/src/rpc/errors.rs | 4 ++++ sdk-libs/client/src/rpc/rpc_trait.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sdk-libs/client/src/rpc/errors.rs b/sdk-libs/client/src/rpc/errors.rs index 2875b0ecb2..717a8fd7eb 100644 --- a/sdk-libs/client/src/rpc/errors.rs +++ b/sdk-libs/client/src/rpc/errors.rs @@ -33,6 +33,9 @@ pub enum RpcError { #[error("Error: `{0}`")] CustomError(String), + #[error("Signing error: {0}")] + SigningError(String), + #[error("Assert Rpc Error: {0}")] AssertRpcError(String), @@ -73,6 +76,7 @@ impl Clone for RpcError { RpcError::ClientError(_) => RpcError::CustomError("ClientError".to_string()), RpcError::IoError(e) => RpcError::IoError(e.kind().into()), RpcError::CustomError(e) => RpcError::CustomError(e.clone()), + RpcError::SigningError(e) => RpcError::SigningError(e.clone()), RpcError::AssertRpcError(e) => RpcError::AssertRpcError(e.clone()), RpcError::InvalidWarpSlot => RpcError::InvalidWarpSlot, RpcError::AccountDoesNotExist(e) => RpcError::AccountDoesNotExist(e.clone()), diff --git a/sdk-libs/client/src/rpc/rpc_trait.rs b/sdk-libs/client/src/rpc/rpc_trait.rs index 09b664d239..900ed08bab 100644 --- a/sdk-libs/client/src/rpc/rpc_trait.rs +++ b/sdk-libs/client/src/rpc/rpc_trait.rs @@ -83,6 +83,8 @@ pub trait Rpc: Send + Sync + Debug + 'static { match error { // Do not retry transaction errors. RpcError::ClientError(error) => error.kind.get_transaction_error().is_none(), + // Do not retry signing errors. + RpcError::SigningError(_) => false, _ => true, } } @@ -172,7 +174,7 @@ pub trait Rpc: Send + Sync + Debug + 'static { let mut transaction = Transaction::new_with_payer(instructions, Some(payer)); transaction .try_sign(signers, blockhash) - .map_err(|e| RpcError::CustomError(e.to_string()))?; + .map_err(|e| RpcError::SigningError(e.to_string()))?; self.process_transaction(transaction).await }