diff --git a/Cargo.lock b/Cargo.lock index 0604cd86a..e40d2419e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2710,7 +2710,7 @@ dependencies = [ [[package]] name = "solana-account" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "qualifier_attr", @@ -2731,18 +2731,18 @@ dependencies = [ [[package]] name = "solana-account-info" -version = "2.3.0" +version = "3.0.0" dependencies = [ "bincode", "serde", - "solana-program-error 3.0.0", + "solana-program-error", "solana-program-memory", "solana-pubkey", ] [[package]] name = "solana-address" -version = "0.1.0" +version = "1.0.0" dependencies = [ "anyhow", "arbitrary", @@ -2752,7 +2752,6 @@ dependencies = [ "curve25519-dalek", "five8", "five8_const", - "num-traits", "rand", "serde", "serde_derive", @@ -2760,13 +2759,13 @@ dependencies = [ "solana-address", "solana-atomic-u64", "solana-cpi", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-example-mocks", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-hash", "solana-instruction", - "solana-program-error 3.0.0", + "solana-program-error", "solana-sanitize", "solana-sha256-hasher", "solana-system-interface", @@ -2776,7 +2775,7 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-interface" -version = "2.2.2" +version = "3.0.0" dependencies = [ "bincode", "bytemuck", @@ -2796,14 +2795,14 @@ dependencies = [ [[package]] name = "solana-atomic-u64" -version = "2.2.1" +version = "3.0.0" dependencies = [ "parking_lot", ] [[package]] name = "solana-big-mod-exp" -version = "2.2.1" +version = "3.0.0" dependencies = [ "array-bytes", "criterion", @@ -2812,12 +2811,12 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "solana-define-syscall 2.3.0", + "solana-define-syscall", ] [[package]] name = "solana-bincode" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "serde", @@ -2827,11 +2826,11 @@ dependencies = [ [[package]] name = "solana-blake3-hasher" -version = "2.2.1" +version = "3.0.0" dependencies = [ "blake3", "solana-blake3-hasher", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-hash", ] @@ -2867,7 +2866,7 @@ dependencies = [ [[package]] name = "solana-bn254" -version = "2.2.2" +version = "3.0.0" dependencies = [ "ark-bn254", "ark-ec", @@ -2879,13 +2878,13 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "thiserror 2.0.12", ] [[package]] name = "solana-borsh" -version = "2.2.1" +version = "3.0.0" dependencies = [ "borsh", ] @@ -2911,7 +2910,7 @@ dependencies = [ [[package]] name = "solana-clock" -version = "2.2.2" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -2924,7 +2923,7 @@ dependencies = [ [[package]] name = "solana-cluster-type" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -2935,7 +2934,7 @@ dependencies = [ [[package]] name = "solana-commitment-config" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -2943,7 +2942,7 @@ dependencies = [ [[package]] name = "solana-compute-budget-interface" -version = "2.2.2" +version = "3.0.0" dependencies = [ "borsh", "serde", @@ -2956,13 +2955,13 @@ dependencies = [ [[package]] name = "solana-cpi" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-account-info", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-instruction", "solana-program-entrypoint", - "solana-program-error 3.0.0", + "solana-program-error", "solana-pubkey", "solana-sdk-ids", "solana-stable-layout", @@ -2970,28 +2969,13 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "solana-decode-error" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c781686a18db2f942e70913f7ca15dc120ec38dcab42ff7557db2c70c625a35" -dependencies = [ - "num-traits", -] - [[package]] name = "solana-define-syscall" -version = "2.3.0" - -[[package]] -name = "solana-define-syscall" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" +version = "3.0.0" [[package]] name = "solana-derivation-path" -version = "2.2.1" +version = "3.0.0" dependencies = [ "assert_matches", "derivation-path", @@ -3001,7 +2985,7 @@ dependencies = [ [[package]] name = "solana-ed25519-program" -version = "2.2.3" +version = "3.0.0" dependencies = [ "bytemuck", "bytemuck_derive", @@ -3011,7 +2995,7 @@ dependencies = [ [[package]] name = "solana-epoch-info" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3019,7 +3003,7 @@ dependencies = [ [[package]] name = "solana-epoch-rewards" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3034,7 +3018,7 @@ dependencies = [ [[package]] name = "solana-epoch-rewards-hasher" -version = "2.2.1" +version = "3.0.0" dependencies = [ "siphasher", "solana-hash", @@ -3043,7 +3027,7 @@ dependencies = [ [[package]] name = "solana-epoch-schedule" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3059,9 +3043,9 @@ dependencies = [ [[package]] name = "solana-epoch-stake" -version = "2.2.1" +version = "3.0.0" dependencies = [ - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-pubkey", ] @@ -3095,16 +3079,18 @@ dependencies = [ "solana-account-info", "solana-feature-gate-interface", "solana-instruction", - "solana-program-error 3.0.0", + "solana-program-error", "solana-pubkey", "solana-rent", "solana-sdk-ids", "solana-system-interface", + "strum", + "strum_macros", ] [[package]] name = "solana-fee-calculator" -version = "2.2.1" +version = "3.0.0" dependencies = [ "log", "serde", @@ -3118,7 +3104,7 @@ dependencies = [ [[package]] name = "solana-fee-structure" -version = "2.3.0" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3127,7 +3113,7 @@ dependencies = [ [[package]] name = "solana-file-download" -version = "2.2.2" +version = "3.0.0" dependencies = [ "console", "indicatif", @@ -3137,7 +3123,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "2.3.0" +version = "3.0.0" dependencies = [ "bitflags 2.8.0", "boxcar", @@ -3160,7 +3146,7 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "2.2.1" +version = "3.0.0" dependencies = [ "proc-macro2", "quote", @@ -3200,7 +3186,7 @@ dependencies = [ [[package]] name = "solana-hard-forks" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3210,7 +3196,7 @@ dependencies = [ [[package]] name = "solana-hash" -version = "2.3.0" +version = "3.0.0" dependencies = [ "borsh", "bs58", @@ -3227,7 +3213,7 @@ dependencies = [ [[package]] name = "solana-inflation" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3237,13 +3223,13 @@ dependencies = [ [[package]] name = "solana-instruction" -version = "2.3.0" +version = "3.0.0" dependencies = [ "bincode", "borsh", "serde", "serde_derive", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-instruction", @@ -3253,26 +3239,26 @@ dependencies = [ [[package]] name = "solana-instruction-error" -version = "1.0.0" +version = "2.0.0" dependencies = [ "num-traits", "serde", "serde_derive", "solana-frozen-abi", "solana-frozen-abi-macro", - "solana-program-error 3.0.0", + "solana-program-error", ] [[package]] name = "solana-instructions-sysvar" -version = "2.2.2" +version = "3.0.0" dependencies = [ "bitflags 2.8.0", "qualifier_attr", "solana-account-info", "solana-instruction", "solana-instruction-error", - "solana-program-error 3.0.0", + "solana-program-error", "solana-pubkey", "solana-sanitize", "solana-sdk-ids", @@ -3282,16 +3268,16 @@ dependencies = [ [[package]] name = "solana-keccak-hasher" -version = "2.2.1" +version = "3.0.0" dependencies = [ "sha3", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-hash", ] [[package]] name = "solana-keypair" -version = "2.2.3" +version = "3.0.0" dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", @@ -3310,7 +3296,7 @@ dependencies = [ [[package]] name = "solana-last-restart-slot" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3321,7 +3307,7 @@ dependencies = [ [[package]] name = "solana-loader-v2-interface" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_bytes", @@ -3333,7 +3319,7 @@ dependencies = [ [[package]] name = "solana-loader-v3-interface" -version = "5.0.0" +version = "6.0.0" dependencies = [ "bincode", "serde", @@ -3350,7 +3336,7 @@ dependencies = [ [[package]] name = "solana-loader-v4-interface" -version = "2.2.1" +version = "3.0.0" dependencies = [ "memoffset", "serde", @@ -3367,7 +3353,7 @@ dependencies = [ [[package]] name = "solana-logger" -version = "2.3.1" +version = "3.0.0" dependencies = [ "env_logger", "lazy_static", @@ -3411,27 +3397,18 @@ dependencies = [ [[package]] name = "solana-msg" -version = "2.2.1" -dependencies = [ - "solana-define-syscall 2.3.0", -] - -[[package]] -name = "solana-msg" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +version = "3.0.0" dependencies = [ - "solana-define-syscall 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-define-syscall", ] [[package]] name = "solana-native-token" -version = "2.3.0" +version = "3.0.0" [[package]] name = "solana-nonce" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "serde", @@ -3457,7 +3434,7 @@ dependencies = [ [[package]] name = "solana-offchain-message" -version = "2.2.1" +version = "3.0.0" dependencies = [ "num_enum", "solana-hash", @@ -3474,7 +3451,7 @@ dependencies = [ [[package]] name = "solana-package-metadata" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-package-metadata-macro", "solana-pubkey", @@ -3482,7 +3459,7 @@ dependencies = [ [[package]] name = "solana-package-metadata-macro" -version = "2.2.1" +version = "3.0.0" dependencies = [ "proc-macro2", "quote", @@ -3492,7 +3469,7 @@ dependencies = [ [[package]] name = "solana-packet" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "bitflags 2.8.0", @@ -3508,7 +3485,7 @@ dependencies = [ [[package]] name = "solana-poh-config" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3520,14 +3497,14 @@ dependencies = [ [[package]] name = "solana-precompile-error" -version = "2.2.2" +version = "3.0.0" dependencies = [ "num-traits", ] [[package]] name = "solana-presigner" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-keypair", "solana-pubkey", @@ -3546,7 +3523,7 @@ dependencies = [ "solana-borsh", "solana-clock", "solana-cpi", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-epoch-rewards", "solana-epoch-schedule", "solana-epoch-stake", @@ -3560,10 +3537,10 @@ dependencies = [ "solana-instructions-sysvar", "solana-keccak-hasher", "solana-last-restart-slot", - "solana-msg 2.2.1", + "solana-msg", "solana-native-token", "solana-program-entrypoint", - "solana-program-error 3.0.0", + "solana-program-error", "solana-program-memory", "solana-program-option", "solana-program-pack", @@ -3585,25 +3562,12 @@ dependencies = [ [[package]] name = "solana-program-entrypoint" -version = "2.3.0" +version = "3.0.0" dependencies = [ "solana-account-info", - "solana-define-syscall 2.3.0", - "solana-msg 2.2.1", - "solana-program-error 3.0.0", - "solana-pubkey", -] - -[[package]] -name = "solana-program-error" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" -dependencies = [ - "num-traits", - "solana-decode-error", - "solana-instruction", - "solana-msg 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-define-syscall", + "solana-msg", + "solana-program-error", "solana-pubkey", ] @@ -3619,25 +3583,25 @@ dependencies = [ [[package]] name = "solana-program-memory" -version = "2.3.1" +version = "3.0.0" dependencies = [ - "solana-define-syscall 2.3.0", + "solana-define-syscall", ] [[package]] name = "solana-program-option" -version = "2.2.1" +version = "3.0.0" [[package]] name = "solana-program-pack" -version = "2.2.1" +version = "3.0.0" dependencies = [ - "solana-program-error 3.0.0", + "solana-program-error", ] [[package]] name = "solana-pubkey" -version = "2.4.0" +version = "3.0.0" dependencies = [ "rand", "solana-address", @@ -3645,14 +3609,14 @@ dependencies = [ [[package]] name = "solana-quic-definitions" -version = "2.3.1" +version = "3.0.0" dependencies = [ "solana-keypair", ] [[package]] name = "solana-rent" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3686,7 +3650,7 @@ dependencies = [ [[package]] name = "solana-reward-info" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3696,7 +3660,7 @@ dependencies = [ [[package]] name = "solana-sanitize" -version = "2.2.1" +version = "3.0.0" [[package]] name = "solana-sdk" @@ -3738,14 +3702,14 @@ dependencies = [ [[package]] name = "solana-sdk-ids" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-pubkey", ] [[package]] name = "solana-sdk-macro" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bs58", "proc-macro2", @@ -3787,7 +3751,7 @@ dependencies = [ [[package]] name = "solana-secp256k1-program" -version = "2.2.3" +version = "3.0.0" dependencies = [ "anyhow", "bincode", @@ -3803,8 +3767,8 @@ dependencies = [ "solana-instruction", "solana-instructions-sysvar", "solana-keccak-hasher", - "solana-msg 2.2.1", - "solana-program-error 3.0.0", + "solana-msg", + "solana-program-error", "solana-sdk-ids", "solana-secp256k1-program", "solana-signature", @@ -3812,13 +3776,13 @@ dependencies = [ [[package]] name = "solana-secp256k1-recover" -version = "2.2.1" +version = "3.0.0" dependencies = [ "anyhow", "borsh", "k256", "rand", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-program", @@ -3827,7 +3791,7 @@ dependencies = [ [[package]] name = "solana-secp256r1-program" -version = "2.2.4" +version = "3.0.0" dependencies = [ "bytemuck", "openssl", @@ -3837,14 +3801,14 @@ dependencies = [ [[package]] name = "solana-seed-derivable" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-derivation-path", ] [[package]] name = "solana-seed-phrase" -version = "2.2.1" +version = "3.0.0" dependencies = [ "hmac", "pbkdf2 0.11.0", @@ -3853,7 +3817,7 @@ dependencies = [ [[package]] name = "solana-serde" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "serde", @@ -3862,7 +3826,7 @@ dependencies = [ [[package]] name = "solana-serde-varint" -version = "2.2.2" +version = "3.0.0" dependencies = [ "bincode", "rand", @@ -3873,7 +3837,7 @@ dependencies = [ [[package]] name = "solana-serialize-utils" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bincode", "borsh", @@ -3886,17 +3850,17 @@ dependencies = [ [[package]] name = "solana-sha256-hasher" -version = "2.2.1" +version = "3.0.0" dependencies = [ "sha2", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-hash", "solana-sha256-hasher", ] [[package]] name = "solana-short-vec" -version = "2.2.1" +version = "3.0.0" dependencies = [ "assert_matches", "bincode", @@ -3908,7 +3872,7 @@ dependencies = [ [[package]] name = "solana-shred-version" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-hard-forks", "solana-hash", @@ -3917,7 +3881,7 @@ dependencies = [ [[package]] name = "solana-signature" -version = "2.3.0" +version = "3.0.0" dependencies = [ "bincode", "bs58", @@ -3939,7 +3903,7 @@ dependencies = [ [[package]] name = "solana-signer" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-pubkey", "solana-signature", @@ -3959,7 +3923,7 @@ dependencies = [ [[package]] name = "solana-slot-hashes" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -3971,7 +3935,7 @@ dependencies = [ [[package]] name = "solana-slot-history" -version = "2.2.1" +version = "3.0.0" dependencies = [ "bv", "serde", @@ -3982,7 +3946,7 @@ dependencies = [ [[package]] name = "solana-stable-layout" -version = "2.2.1" +version = "3.0.0" dependencies = [ "memoffset", "solana-instruction", @@ -3990,40 +3954,32 @@ dependencies = [ ] [[package]] -name = "solana-stake-interface" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +name = "solana-system-interface" +version = "2.0.0" dependencies = [ + "anyhow", + "borsh", "num-traits", "serde", "serde_derive", - "solana-clock", + "solana-account-info", "solana-cpi", - "solana-decode-error", + "solana-example-mocks", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-instruction", - "solana-program-error 2.2.2", + "solana-logger", + "solana-msg", + "solana-nonce", + "solana-program-entrypoint", + "solana-program-error", "solana-pubkey", "solana-system-interface", + "solana-sysvar", "solana-sysvar-id", -] - -[[package]] -name = "solana-system-interface" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" -dependencies = [ - "js-sys", - "num-traits", - "serde", - "serde_derive", - "solana-decode-error", - "solana-instruction", - "solana-pubkey", - "wasm-bindgen", + "static_assertions", + "strum", + "strum_macros", ] [[package]] @@ -4041,7 +3997,7 @@ dependencies = [ [[package]] name = "solana-sysvar" -version = "2.2.2" +version = "3.0.0" dependencies = [ "anyhow", "base64 0.22.1", @@ -4054,7 +4010,7 @@ dependencies = [ "serial_test", "solana-account-info", "solana-clock", - "solana-define-syscall 2.3.0", + "solana-define-syscall", "solana-epoch-rewards", "solana-epoch-schedule", "solana-example-mocks", @@ -4064,9 +4020,9 @@ dependencies = [ "solana-hash", "solana-instruction", "solana-last-restart-slot", - "solana-msg 2.2.1", + "solana-msg", "solana-program-entrypoint", - "solana-program-error 3.0.0", + "solana-program-error", "solana-program-memory", "solana-pubkey", "solana-rent", @@ -4075,7 +4031,6 @@ dependencies = [ "solana-sha256-hasher", "solana-slot-hashes", "solana-slot-history", - "solana-stake-interface", "solana-sysvar", "solana-sysvar-id", "test-case", @@ -4083,7 +4038,7 @@ dependencies = [ [[package]] name = "solana-sysvar-id" -version = "2.2.1" +version = "3.0.0" dependencies = [ "solana-pubkey", "solana-sdk-ids", @@ -4091,7 +4046,7 @@ dependencies = [ [[package]] name = "solana-time-utils" -version = "2.2.1" +version = "3.0.0" [[package]] name = "solana-transaction" @@ -4129,7 +4084,7 @@ dependencies = [ [[package]] name = "solana-transaction-error" -version = "2.2.1" +version = "3.0.0" dependencies = [ "serde", "serde_derive", @@ -4141,7 +4096,7 @@ dependencies = [ [[package]] name = "solana-validator-exit" -version = "2.2.1" +version = "3.0.0" [[package]] name = "solana-vote-interface" @@ -4149,12 +4104,14 @@ version = "2.2.5" dependencies = [ "arbitrary", "bincode", + "cfg_eval", "itertools 0.12.1", "num-derive", "num-traits", "rand", "serde", "serde_derive", + "serde_with", "solana-clock", "solana-epoch-schedule", "solana-frozen-abi", diff --git a/Cargo.toml b/Cargo.toml index 55649531e..e8c0b2e52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,106 +206,105 @@ sha2 = "0.10.8" sha3 = "0.10.8" signal-hook = "0.3.17" siphasher = "0.3.11" -solana-account = { path = "account", version = "2.2.1" } -solana-account-info = { path = "account-info", version = "2.2.1" } -solana-address = { path = "address", version = "0.1.0" } -solana-address-lookup-table-interface = { path = "address-lookup-table-interface", version = "2.2.2" } -solana-atomic-u64 = { path = "atomic-u64", version = "2.2.1" } -solana-big-mod-exp = { path = "big-mod-exp", version = "2.2.1" } -solana-bincode = { path = "bincode", version = "2.2.1" } -solana-blake3-hasher = { path = "blake3-hasher", version = "2.2.1" } +solana-account = { path = "account", version = "3.0.0" } +solana-account-info = { path = "account-info", version = "3.0.0" } +solana-address = { path = "address", version = "1.0.0" } +solana-address-lookup-table-interface = { path = "address-lookup-table-interface", version = "3.0.0" } +solana-atomic-u64 = { path = "atomic-u64", version = "3.0.0" } +solana-big-mod-exp = { path = "big-mod-exp", version = "3.0.0" } +solana-bincode = { path = "bincode", version = "3.0.0" } +solana-blake3-hasher = { path = "blake3-hasher", version = "3.0.0" } solana-bls-signatures = { path = "bls-signatures", version = "0.1.1" } -solana-bn254 = { path = "bn254", version = "2.2.2" } -solana-borsh = { path = "borsh", version = "2.2.1" } +solana-bn254 = { path = "bn254", version = "3.0.0" } +solana-borsh = { path = "borsh", version = "3.0.0" } solana-client-traits = { path = "client-traits", version = "2.2.1" } -solana-clock = { path = "clock", version = "2.2.1" } -solana-cluster-type = { path = "cluster-type", version = "2.2.1" } -solana-commitment-config = { path = "commitment-config", version = "2.2.1" } -solana-compute-budget-interface = { path = "compute-budget-interface", version = "2.2.1" } -solana-cpi = { path = "cpi", version = "2.2.1" } -solana-define-syscall = { path = "define-syscall", version = "2.2.1" } -solana-derivation-path = { path = "derivation-path", version = "2.2.1" } -solana-ed25519-program = { path = "ed25519-program", version = "2.2.1" } -solana-epoch-info = { path = "epoch-info", version = "2.2.1" } -solana-epoch-rewards = { path = "epoch-rewards", version = "2.2.1" } -solana-epoch-rewards-hasher = { path = "epoch-rewards-hasher", version = "2.2.1" } -solana-epoch-schedule = { path = "epoch-schedule", version = "2.2.1" } -solana-epoch-stake = { path = "epoch-stake", version = "2.2.1" } +solana-clock = { path = "clock", version = "3.0.0" } +solana-cluster-type = { path = "cluster-type", version = "3.0.0" } +solana-commitment-config = { path = "commitment-config", version = "3.0.0" } +solana-compute-budget-interface = { path = "compute-budget-interface", version = "3.0.0" } +solana-cpi = { path = "cpi", version = "3.0.0" } +solana-define-syscall = { path = "define-syscall", version = "3.0.0" } +solana-derivation-path = { path = "derivation-path", version = "3.0.0" } +solana-ed25519-program = { path = "ed25519-program", version = "3.0.0" } +solana-epoch-info = { path = "epoch-info", version = "3.0.0" } +solana-epoch-rewards = { path = "epoch-rewards", version = "3.0.0" } +solana-epoch-rewards-hasher = { path = "epoch-rewards-hasher", version = "3.0.0" } +solana-epoch-schedule = { path = "epoch-schedule", version = "3.0.0" } +solana-epoch-stake = { path = "epoch-stake", version = "3.0.0" } solana-example-mocks = { path = "example-mocks", version = "2.2.1" } solana-feature-gate-interface = { path = "feature-gate-interface", version = "2.2.1" } -solana-fee-calculator = { path = "fee-calculator", version = "2.2.1" } -solana-fee-structure = { path = "fee-structure", version = "2.2.1" } -solana-file-download = { path = "file-download", version = "2.2.1" } -solana-frozen-abi = { path = "frozen-abi", version = "2.2.1" } -solana-frozen-abi-macro = { path = "frozen-abi-macro", version = "2.2.1" } +solana-fee-calculator = { path = "fee-calculator", version = "3.0.0" } +solana-fee-structure = { path = "fee-structure", version = "3.0.0" } +solana-file-download = { path = "file-download", version = "3.0.0" } +solana-frozen-abi = { path = "frozen-abi", version = "3.0.0" } +solana-frozen-abi-macro = { path = "frozen-abi-macro", version = "3.0.0" } solana-genesis-config = { path = "genesis-config", version = "2.2.1" } -solana-hard-forks = { path = "hard-forks", version = "2.2.1", default-features = false } -solana-hash = { path = "hash", version = "2.2.1", default-features = false } -solana-inflation = { path = "inflation", version = "2.2.1" } -solana-instruction = { path = "instruction", version = "2.3.0", default-features = false } -solana-instruction-error = { path = "instruction-error", version = "1.0.0" } -solana-instructions-sysvar = { path = "instructions-sysvar", version = "2.2.1" } -solana-keccak-hasher = { path = "keccak-hasher", version = "2.2.1" } -solana-keypair = { path = "keypair", version = "2.2.1" } -solana-last-restart-slot = { path = "last-restart-slot", version = "2.2.1" } -solana-loader-v2-interface = { path = "loader-v2-interface", version = "2.2.1" } -solana-loader-v3-interface = { path = "loader-v3-interface", version = "5.0.0" } -solana-loader-v4-interface = { path = "loader-v4-interface", version = "2.2.1" } -solana-logger = { path = "logger", version = "2.2.1" } +solana-hard-forks = { path = "hard-forks", version = "3.0.0", default-features = false } +solana-hash = { path = "hash", version = "3.0.0", default-features = false } +solana-inflation = { path = "inflation", version = "3.0.0" } +solana-instruction = { path = "instruction", version = "3.0.0", default-features = false } +solana-instruction-error = { path = "instruction-error", version = "2.0.0" } +solana-instructions-sysvar = { path = "instructions-sysvar", version = "3.0.0" } +solana-keccak-hasher = { path = "keccak-hasher", version = "3.0.0" } +solana-keypair = { path = "keypair", version = "3.0.0" } +solana-last-restart-slot = { path = "last-restart-slot", version = "3.0.0" } +solana-loader-v2-interface = { path = "loader-v2-interface", version = "3.0.0" } +solana-loader-v3-interface = { path = "loader-v3-interface", version = "6.0.0" } +solana-loader-v4-interface = { path = "loader-v4-interface", version = "3.0.0" } +solana-logger = { path = "logger", version = "3.0.0" } solana-message = { path = "message", version = "2.2.1" } -solana-msg = { path = "msg", version = "2.2.1" } -solana-native-token = { path = "native-token", version = "2.2.1" } -solana-nonce = { path = "nonce", version = "2.2.1" } +solana-msg = { path = "msg", version = "3.0.0" } +solana-native-token = { path = "native-token", version = "3.0.0" } +solana-nonce = { path = "nonce", version = "3.0.0" } solana-nonce-account = { path = "nonce-account", version = "2.2.1" } -solana-offchain-message = { path = "offchain-message", version = "2.2.1" } -solana-package-metadata = { path = "package-metadata", version = "2.2.1" } -solana-package-metadata-macro = { path = "package-metadata-macro", version = "2.2.1" } -solana-packet = { path = "packet", version = "2.2.1" } -solana-poh-config = { path = "poh-config", version = "2.2.1" } -solana-precompile-error = { path = "precompile-error", version = "2.2.1" } -solana-presigner = { path = "presigner", version = "2.2.1" } +solana-offchain-message = { path = "offchain-message", version = "3.0.0" } +solana-package-metadata = { path = "package-metadata", version = "3.0.0" } +solana-package-metadata-macro = { path = "package-metadata-macro", version = "3.0.0" } +solana-packet = { path = "packet", version = "3.0.0" } +solana-poh-config = { path = "poh-config", version = "3.0.0" } +solana-precompile-error = { path = "precompile-error", version = "3.0.0" } +solana-presigner = { path = "presigner", version = "3.0.0" } solana-program = { path = "program", version = "2.2.1", default-features = false } -solana-program-entrypoint = { path = "program-entrypoint", version = "2.2.1" } +solana-program-entrypoint = { path = "program-entrypoint", version = "3.0.0" } solana-program-error = { path = "program-error", version = "3.0.0" } -solana-program-memory = { path = "program-memory", version = "2.2.1" } -solana-program-option = { path = "program-option", version = "2.2.1" } -solana-program-pack = { path = "program-pack", version = "2.2.1" } -solana-pubkey = { path = "pubkey", version = "2.2.1", default-features = false } -solana-quic-definitions = { path = "quic-definitions", version = "2.2.1" } -solana-rent = { path = "rent", version = "2.2.1", default-features = false } +solana-program-memory = { path = "program-memory", version = "3.0.0" } +solana-program-option = { path = "program-option", version = "3.0.0" } +solana-program-pack = { path = "program-pack", version = "3.0.0" } +solana-pubkey = { path = "pubkey", version = "3.0.0", default-features = false } +solana-quic-definitions = { path = "quic-definitions", version = "3.0.0" } +solana-rent = { path = "rent", version = "3.0.0", default-features = false } solana-rent-collector = { path = "rent-collector", version = "2.2.1" } -solana-reward-info = { path = "reward-info", version = "2.2.1" } -solana-sanitize = { path = "sanitize", version = "2.2.1" } +solana-reward-info = { path = "reward-info", version = "3.0.0" } +solana-sanitize = { path = "sanitize", version = "3.0.0" } solana-sdk = { path = "sdk", version = "2.2.1" } -solana-sdk-ids = { path = "sdk-ids", version = "2.2.1" } -solana-sdk-macro = { path = "sdk-macro", version = "2.2.1" } +solana-sdk-ids = { path = "sdk-ids", version = "3.0.0" } +solana-sdk-macro = { path = "sdk-macro", version = "3.0.0" } solana-sdk-wasm-js = { path = "sdk-wasm-js", version = "1.0.0" } -solana-secp256k1-program = { path = "secp256k1-program", version = "2.2.1" } -solana-secp256k1-recover = { path = "secp256k1-recover", version = "2.2.1" } -solana-secp256r1-program = { path = "secp256r1-program", version = "2.2.1", default-features = false } -solana-seed-derivable = { path = "seed-derivable", version = "2.2.1" } -solana-seed-phrase = { path = "seed-phrase", version = "2.2.1" } -solana-serde = { path = "serde", version = "2.2.1" } -solana-serde-varint = { path = "serde-varint", version = "2.2.1" } -solana-serialize-utils = { path = "serialize-utils", version = "2.2.1" } -solana-sha256-hasher = { path = "sha256-hasher", version = "2.2.1" } -solana-short-vec = { path = "short-vec", version = "2.2.1" } -solana-shred-version = { path = "shred-version", version = "2.2.1" } -solana-signature = { path = "signature", version = "2.3.0", default-features = false } -solana-signer = { path = "signer", version = "2.2.1" } +solana-secp256k1-program = { path = "secp256k1-program", version = "3.0.0" } +solana-secp256k1-recover = { path = "secp256k1-recover", version = "3.0.0" } +solana-secp256r1-program = { path = "secp256r1-program", version = "3.0.0", default-features = false } +solana-seed-derivable = { path = "seed-derivable", version = "3.0.0" } +solana-seed-phrase = { path = "seed-phrase", version = "3.0.0" } +solana-serde = { path = "serde", version = "3.0.0" } +solana-serde-varint = { path = "serde-varint", version = "3.0.0" } +solana-serialize-utils = { path = "serialize-utils", version = "3.0.0" } +solana-sha256-hasher = { path = "sha256-hasher", version = "3.0.0" } +solana-short-vec = { path = "short-vec", version = "3.0.0" } +solana-shred-version = { path = "shred-version", version = "3.0.0" } +solana-signature = { path = "signature", version = "3.0.0", default-features = false } +solana-signer = { path = "signer", version = "3.0.0" } solana-signer-store = { path = "signer-store", version = "0.1.0" } -solana-slot-hashes = { path = "slot-hashes", version = "2.2.1" } -solana-slot-history = { path = "slot-history", version = "2.2.1" } -solana-stable-layout = { path = "stable-layout", version = "2.2.1" } -solana-stake-interface = { version = "1.2.1" } -solana-system-interface = "1.0" +solana-slot-hashes = { path = "slot-hashes", version = "3.0.0" } +solana-slot-history = { path = "slot-history", version = "3.0.0" } +solana-stable-layout = { path = "stable-layout", version = "3.0.0" } +solana-system-interface = { path = "system-interface", version = "2.0" } solana-system-transaction = { path = "system-transaction", version = "2.2.1" } -solana-sysvar = { path = "sysvar", version = "2.2.1" } -solana-sysvar-id = { path = "sysvar-id", version = "2.2.1" } -solana-time-utils = { path = "time-utils", version = "2.2.1" } +solana-sysvar = { path = "sysvar", version = "3.0.0" } +solana-sysvar-id = { path = "sysvar-id", version = "3.0.0" } +solana-time-utils = { path = "time-utils", version = "3.0.0" } solana-transaction = { path = "transaction", version = "2.2.1" } -solana-transaction-error = { path = "transaction-error", version = "2.2.1" } -solana-validator-exit = { path = "validator-exit", version = "2.2.1" } +solana-transaction-error = { path = "transaction-error", version = "3.0.0" } +solana-validator-exit = { path = "validator-exit", version = "3.0.0" } solana-vote-interface = { path = "vote-interface", version = "2.2.1" } static_assertions = "1.1.0" strum = "0.24" @@ -331,38 +330,3 @@ opt-level = 1 # Enable optimizations for procmacros for faster recompile [profile.dev.build-override] opt-level = 1 - -[patch.crates-io] -# We include the following crates as our dependencies above from crates.io: -# -# * solana-system-interface -# -# They, in turn, depend on a number of crates that we also include directly -# using `path` specifications. For example, `solana-system-interface` depends -# on `solana-instruction`. And we explicitly specify `solana-instruction` above -# as a local path dependency: -# -# solana-instruction = { path = "instruction", version = "2.2.1" } -# -# Unfortunately, Cargo will try to resolve the `solana-system-interface` -# `solana-instruction` dependency only using what is available on crates.io. -# Crates.io normally contains a previous version of these crates, and we end up -# with two versions of `solana-instruction` and all of their dependencies in our -# build tree. -# -# If you are developing downstream using non-crates-io solana-program (local or -# forked repo, or from github rev, eg), duplicate the following patch statements -# in your Cargo.toml. If you still hit duplicate-type errors with the patch -# statements in place, run `cargo update -p solana-program` to remove extraneous -# versions from your Cargo.lock file. -solana-account = { path = "account" } -solana-clock = { path = "clock" } -solana-cpi = { path = "cpi" } -solana-frozen-abi = { path = "frozen-abi" } -solana-frozen-abi-macro = { path = "frozen-abi-macro" } -solana-instruction = { path = "instruction" } -solana-program-error = { path = "program-error" } -solana-pubkey = { path = "pubkey" } -solana-rent = { path = "rent" } -solana-signature = { path = "signature" } -solana-sysvar-id = { path = "sysvar-id" } diff --git a/account-info/Cargo.toml b/account-info/Cargo.toml index 35c991255..0057ae776 100644 --- a/account-info/Cargo.toml +++ b/account-info/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-account-info" description = "Solana AccountInfo and related definitions." documentation = "https://docs.rs/solana-account-info" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/account/Cargo.toml b/account/Cargo.toml index 2e04dc48b..7120be53b 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-account" description = "Solana Account type" documentation = "https://docs.rs/solana-account" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/address-lookup-table-interface/Cargo.toml b/address-lookup-table-interface/Cargo.toml index 9a416b119..fd28470fa 100644 --- a/address-lookup-table-interface/Cargo.toml +++ b/address-lookup-table-interface/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-address-lookup-table-interface" description = "Solana address lookup table interface." documentation = "https://docs.rs/solana-address-lookup-table-interface" -version = "2.2.2" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/address/Cargo.toml b/address/Cargo.toml index 0e6f28f4b..6384007f3 100644 --- a/address/Cargo.toml +++ b/address/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-address" description = "Solana account addresses" documentation = "https://docs.rs/solana-address" -version = "0.1.0" +version = "1.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } @@ -17,7 +17,7 @@ bytemuck = ["dep:bytemuck", "dep:bytemuck_derive"] curve25519 = ["dep:curve25519-dalek", "error", "sha2"] decode = ["dep:five8", "dep:five8_const", "error"] dev-context-only-utils = ["dep:arbitrary", "rand"] -error = ["dep:num-traits", "dep:solana-program-error"] +error = ["dep:solana-program-error"] frozen-abi = [ "dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", @@ -38,7 +38,6 @@ bytemuck = { workspace = true, optional = true } bytemuck_derive = { workspace = true, optional = true } five8 = { workspace = true, optional = true } five8_const = { workspace = true, optional = true } -num-traits = { workspace = true, optional = true } rand = { workspace = true, optional = true } serde = { workspace = true, optional = true } serde_derive = { workspace = true, optional = true } @@ -75,7 +74,7 @@ solana-example-mocks = { path = "../example-mocks" } solana-hash = { workspace = true } solana-instruction = { path = "../instruction", features = ["borsh"] } solana-program-error = { workspace = true, features = ["borsh"] } -solana-system-interface = { workspace = true, features = ["bincode"] } +solana-system-interface = { path = "../system-interface", features = ["bincode"] } strum = { workspace = true } strum_macros = { workspace = true } diff --git a/address/src/error.rs b/address/src/error.rs index 840b265b6..6a047fd5b 100644 --- a/address/src/error.rs +++ b/address/src/error.rs @@ -1,14 +1,5 @@ -#[cfg(feature = "serde")] -use serde_derive::Serialize; -use { - core::{convert::Infallible, fmt}, - num_traits::{FromPrimitive, ToPrimitive}, - solana_program_error::ProgramError, -}; +use {core::fmt, solana_program_error::ProgramError}; -// Use strum when testing to ensure our FromPrimitive -// impl is exhaustive -#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] #[cfg_attr(feature = "serde", derive(serde_derive::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub enum AddressError { @@ -18,40 +9,6 @@ pub enum AddressError { IllegalOwner, } -impl ToPrimitive for AddressError { - #[inline] - fn to_i64(&self) -> Option { - Some(match *self { - AddressError::MaxSeedLengthExceeded => AddressError::MaxSeedLengthExceeded as i64, - AddressError::InvalidSeeds => AddressError::InvalidSeeds as i64, - AddressError::IllegalOwner => AddressError::IllegalOwner as i64, - }) - } - #[inline] - fn to_u64(&self) -> Option { - self.to_i64().map(|x| x as u64) - } -} - -impl FromPrimitive for AddressError { - #[inline] - fn from_i64(n: i64) -> Option { - if n == AddressError::MaxSeedLengthExceeded as i64 { - Some(AddressError::MaxSeedLengthExceeded) - } else if n == AddressError::InvalidSeeds as i64 { - Some(AddressError::InvalidSeeds) - } else if n == AddressError::IllegalOwner as i64 { - Some(AddressError::IllegalOwner) - } else { - None - } - } - #[inline] - fn from_u64(n: u64) -> Option { - Self::from_i64(n as i64) - } -} - impl core::error::Error for AddressError {} impl fmt::Display for AddressError { @@ -89,49 +46,18 @@ impl From for ProgramError { } } -// Use strum when testing to ensure our FromPrimitive -// impl is exhaustive -#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] -#[cfg_attr(feature = "serde", derive(Serialize))] +#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))] +#[cfg(feature = "decode")] #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParseAddressError { WrongSize, Invalid, } -impl ToPrimitive for ParseAddressError { - #[inline] - fn to_i64(&self) -> Option { - Some(match *self { - ParseAddressError::WrongSize => ParseAddressError::WrongSize as i64, - ParseAddressError::Invalid => ParseAddressError::Invalid as i64, - }) - } - #[inline] - fn to_u64(&self) -> Option { - self.to_i64().map(|x| x as u64) - } -} - -impl FromPrimitive for ParseAddressError { - #[inline] - fn from_i64(n: i64) -> Option { - if n == ParseAddressError::WrongSize as i64 { - Some(ParseAddressError::WrongSize) - } else if n == ParseAddressError::Invalid as i64 { - Some(ParseAddressError::Invalid) - } else { - None - } - } - #[inline] - fn from_u64(n: u64) -> Option { - Self::from_i64(n as i64) - } -} - +#[cfg(feature = "decode")] impl core::error::Error for ParseAddressError {} +#[cfg(feature = "decode")] impl fmt::Display for ParseAddressError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -141,8 +67,9 @@ impl fmt::Display for ParseAddressError { } } -impl From for ParseAddressError { - fn from(_: Infallible) -> Self { +#[cfg(feature = "decode")] +impl From for ParseAddressError { + fn from(_: core::convert::Infallible) -> Self { unreachable!("Infallible uninhabited"); } } diff --git a/address/src/lib.rs b/address/src/lib.rs index 903c6dafc..ac9e97d85 100644 --- a/address/src/lib.rs +++ b/address/src/lib.rs @@ -331,10 +331,7 @@ macro_rules! address { #[cfg(test)] mod tests { - use { - super::*, core::str::from_utf8, num_traits::FromPrimitive, std::string::String, - strum::IntoEnumIterator, - }; + use {super::*, core::str::from_utf8, std::string::String}; fn encode_address(address: &[u8; 32]) -> String { let mut buffer = [0u8; 44]; @@ -589,29 +586,6 @@ mod tests { assert!(address_from_seed_by_marker(&PDA_MARKER[1..]).is_ok()); } - #[test] - fn test_address_error_from_primitive_exhaustive() { - for variant in AddressError::iter() { - let variant_i64 = variant.clone() as i64; - assert_eq!( - AddressError::from_repr(variant_i64 as usize), - AddressError::from_i64(variant_i64) - ); - assert_eq!(AddressError::from(variant_i64 as u64), variant); - } - } - - #[test] - fn test_parse_address_error_from_primitive_exhaustive() { - for variant in ParseAddressError::iter() { - let variant_i64 = variant as i64; - assert_eq!( - ParseAddressError::from_repr(variant_i64 as usize), - ParseAddressError::from_i64(variant_i64) - ); - } - } - #[test] fn test_as_array() { let bytes = [1u8; 32]; diff --git a/atomic-u64/Cargo.toml b/atomic-u64/Cargo.toml index 548c5d486..e7591420d 100644 --- a/atomic-u64/Cargo.toml +++ b/atomic-u64/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-atomic-u64" description = "Solana atomic u64 implementation. For internal use only." documentation = "https://docs.rs/solana-atomic-u64" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/big-mod-exp/Cargo.toml b/big-mod-exp/Cargo.toml index 236dd469d..c92f89396 100644 --- a/big-mod-exp/Cargo.toml +++ b/big-mod-exp/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-big-mod-exp" description = "Solana big integer modular exponentiation" documentation = "https://docs.rs/solana-big-mod-exp" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/bincode/Cargo.toml b/bincode/Cargo.toml index 087a6bed6..fe4fd60c9 100644 --- a/bincode/Cargo.toml +++ b/bincode/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-bincode" description = "Solana bincode utilities" documentation = "https://docs.rs/solana-bincode" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/blake3-hasher/Cargo.toml b/blake3-hasher/Cargo.toml index 8d451945a..7adc0a32b 100644 --- a/blake3-hasher/Cargo.toml +++ b/blake3-hasher/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-blake3-hasher" description = "Solana BLAKE3 hashing" documentation = "https://docs.rs/solana-blake3-hasher" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/bn254/Cargo.toml b/bn254/Cargo.toml index 5ed445319..7eef2528d 100644 --- a/bn254/Cargo.toml +++ b/bn254/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-bn254" description = "Solana BN254" documentation = "https://docs.rs/solana-bn254" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index fafe760fc..eb360482b 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-borsh" description = "Solana Borsh utilities" documentation = "https://docs.rs/solana-borsh" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/clock/Cargo.toml b/clock/Cargo.toml index 3f7fde3c0..6cbc1f997 100644 --- a/clock/Cargo.toml +++ b/clock/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-clock" description = "Solana Clock and Time Definitions" documentation = "https://docs.rs/solana-clock" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/cluster-type/Cargo.toml b/cluster-type/Cargo.toml index ea14e6a4b..6f9654610 100644 --- a/cluster-type/Cargo.toml +++ b/cluster-type/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-cluster-type" description = "Solana ClusterType enum" documentation = "https://docs.rs/solana-cluster-type" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/commitment-config/Cargo.toml b/commitment-config/Cargo.toml index ffb35b3e0..e1e6d0941 100644 --- a/commitment-config/Cargo.toml +++ b/commitment-config/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-commitment-config" description = "Solana commitment config." documentation = "https://docs.rs/solana-commitment-config" -version = "2.2.1" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/compute-budget-interface/Cargo.toml b/compute-budget-interface/Cargo.toml index 9d3ca8f93..1710308a0 100644 --- a/compute-budget-interface/Cargo.toml +++ b/compute-budget-interface/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-compute-budget-interface" description = "Solana compute budget interface." documentation = "https://docs.rs/solana-compute-budget-interface" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/cpi/Cargo.toml b/cpi/Cargo.toml index 1e4679788..329eb976a 100644 --- a/cpi/Cargo.toml +++ b/cpi/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-cpi" description = "Solana Cross-program Invocation" documentation = "https://docs.rs/solana-cpi" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/define-syscall/Cargo.toml b/define-syscall/Cargo.toml index 53466082e..865d1f79e 100644 --- a/define-syscall/Cargo.toml +++ b/define-syscall/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-define-syscall" description = "Solana define_syscall macro and core syscall definitions." documentation = "https://docs.rs/solana-define-syscall" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/derivation-path/Cargo.toml b/derivation-path/Cargo.toml index d6af8ff19..0cddd7438 100644 --- a/derivation-path/Cargo.toml +++ b/derivation-path/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-derivation-path" description = "Solana BIP44 derivation paths." documentation = "https://docs.rs/solana-derivation-path" -version = "2.2.1" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/ed25519-program/Cargo.toml b/ed25519-program/Cargo.toml index 6053d068a..f959239e6 100644 --- a/ed25519-program/Cargo.toml +++ b/ed25519-program/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-ed25519-program" description = "Instructions for the Solana ed25519 native program" documentation = "https://docs.rs/solana-ed25519-program" -version = "2.2.3" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/epoch-info/Cargo.toml b/epoch-info/Cargo.toml index 48430f4b6..0dd7fc9be 100644 --- a/epoch-info/Cargo.toml +++ b/epoch-info/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-epoch-info" description = "Information about a Solana epoch." documentation = "https://docs.rs/solana-epoch-info" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/epoch-rewards-hasher/Cargo.toml b/epoch-rewards-hasher/Cargo.toml index 40356c274..e18eafc82 100644 --- a/epoch-rewards-hasher/Cargo.toml +++ b/epoch-rewards-hasher/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-epoch-rewards-hasher" description = "Solana epoch rewards hasher." documentation = "https://docs.rs/solana-epoch-rewards-hasher" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/epoch-rewards/Cargo.toml b/epoch-rewards/Cargo.toml index 6e9f2f42c..b932b0a1a 100644 --- a/epoch-rewards/Cargo.toml +++ b/epoch-rewards/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-epoch-rewards" description = "Solana epoch rewards sysvar." documentation = "https://docs.rs/solana-epoch-rewards" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/epoch-schedule/Cargo.toml b/epoch-schedule/Cargo.toml index ef833bcef..04568e269 100644 --- a/epoch-schedule/Cargo.toml +++ b/epoch-schedule/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-epoch-schedule" description = "Configuration for Solana epochs and slots." documentation = "https://docs.rs/solana-epoch-schedule" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/epoch-stake/Cargo.toml b/epoch-stake/Cargo.toml index 1648c2c82..1a10dec1f 100644 --- a/epoch-stake/Cargo.toml +++ b/epoch-stake/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-epoch-stake" description = "Solana epoch stake syscall API" documentation = "https://docs.rs/solana-epoch-stake" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/example-mocks/Cargo.toml b/example-mocks/Cargo.toml index 17ae55910..b3f99ba65 100644 --- a/example-mocks/Cargo.toml +++ b/example-mocks/Cargo.toml @@ -11,6 +11,11 @@ edition = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[features] +bincode = ["solana-system-interface/bincode"] [dependencies] serde = { workspace = true } diff --git a/example-mocks/src/lib.rs b/example-mocks/src/lib.rs index 3c10e83fb..95e35ef7f 100644 --- a/example-mocks/src/lib.rs +++ b/example-mocks/src/lib.rs @@ -12,6 +12,7 @@ #![doc(hidden)] #![allow(clippy::new_without_default)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod solana_rpc_client { pub mod rpc_client { diff --git a/feature-gate-interface/Cargo.toml b/feature-gate-interface/Cargo.toml index 9e6684c1c..2edea47e3 100644 --- a/feature-gate-interface/Cargo.toml +++ b/feature-gate-interface/Cargo.toml @@ -8,6 +8,7 @@ repository = { workspace = true } homepage = { workspace = true } license = { workspace = true } edition = { workspace = true } +rust-version = "1.81.0" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,7 +19,6 @@ bincode = [ "dep:solana-account", "dep:solana-account-info", "dep:solana-instruction", - "dep:solana-program-error", "dep:solana-rent", "dep:solana-system-interface", "serde", @@ -33,7 +33,7 @@ serde_derive = { workspace = true, optional = true } solana-account = { workspace = true, optional = true } solana-account-info = { workspace = true, optional = true } solana-instruction = { workspace = true, optional = true } -solana-program-error = { workspace = true, optional = true } +solana-program-error = { workspace = true } solana-pubkey = { workspace = true } solana-rent = { workspace = true, optional = true } solana-sdk-ids = { workspace = true } @@ -43,6 +43,8 @@ solana-system-interface = { workspace = true, optional = true, features = [ [dev-dependencies] solana-feature-gate-interface = { path = ".", features = ["dev-context-only-utils"] } +strum = { workspace = true } +strum_macros = { workspace = true } [lints] workspace = true diff --git a/feature-gate-interface/src/error.rs b/feature-gate-interface/src/error.rs new file mode 100644 index 000000000..88a962b18 --- /dev/null +++ b/feature-gate-interface/src/error.rs @@ -0,0 +1,62 @@ +//! Program error types. + +use solana_program_error::{ProgramError, ToStr}; + +/// Program error types. +#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Deserialize, serde_derive::Serialize) +)] +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FeatureGateError { + /// Feature already activated + FeatureAlreadyActivated, +} + +impl core::error::Error for FeatureGateError {} + +impl core::fmt::Display for FeatureGateError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str(self.to_str()) + } +} + +impl ToStr for FeatureGateError { + fn to_str(&self) -> &'static str { + match self { + FeatureGateError::FeatureAlreadyActivated => "Feature already activated", + } + } +} + +impl From for ProgramError { + fn from(e: FeatureGateError) -> Self { + ProgramError::Custom(e as u32) + } +} + +impl TryFrom for FeatureGateError { + type Error = ProgramError; + fn try_from(error: u32) -> Result { + match error { + 0 => Ok(FeatureGateError::FeatureAlreadyActivated), + _ => Err(ProgramError::InvalidArgument), + } + } +} + +#[cfg(test)] +mod tests { + use {super::FeatureGateError, strum::IntoEnumIterator}; + + #[test] + fn test_system_error_from_primitive_exhaustive() { + for variant in FeatureGateError::iter() { + let variant_u32 = variant.clone() as u32; + assert_eq!(FeatureGateError::from_repr(variant_u32).unwrap(), variant); + assert_eq!(FeatureGateError::try_from(variant_u32).unwrap(), variant); + } + } +} diff --git a/feature-gate-interface/src/instruction.rs b/feature-gate-interface/src/instruction.rs new file mode 100644 index 000000000..199f0639a --- /dev/null +++ b/feature-gate-interface/src/instruction.rs @@ -0,0 +1,48 @@ +#[cfg(feature = "bincode")] +use { + crate::state::Feature, + solana_instruction::{AccountMeta, Instruction}, + solana_pubkey::Pubkey, + solana_rent::Rent, + solana_sdk_ids::{feature::id, incinerator, system_program}, + solana_system_interface::instruction as system_instruction, +}; + +#[cfg(feature = "bincode")] +/// Activate a feature +pub fn activate(feature_id: &Pubkey, funding_address: &Pubkey, rent: &Rent) -> Vec { + activate_with_lamports( + feature_id, + funding_address, + rent.minimum_balance(Feature::size_of()), + ) +} + +#[cfg(feature = "bincode")] +pub fn activate_with_lamports( + feature_id: &Pubkey, + funding_address: &Pubkey, + lamports: u64, +) -> Vec { + vec![ + system_instruction::transfer(funding_address, feature_id, lamports), + system_instruction::allocate(feature_id, Feature::size_of() as u64), + system_instruction::assign(feature_id, &id()), + ] +} + +/// Creates a 'RevokePendingActivation' instruction. +#[cfg(feature = "bincode")] +pub fn revoke_pending_activation(feature_id: &Pubkey) -> Instruction { + let accounts = vec![ + AccountMeta::new(*feature_id, true), + AccountMeta::new(incinerator::id(), false), + AccountMeta::new_readonly(system_program::id(), false), + ]; + + Instruction { + program_id: crate::id(), + accounts, + data: vec![0], + } +} diff --git a/feature-gate-interface/src/lib.rs b/feature-gate-interface/src/lib.rs index b38fe4863..e236a4c95 100644 --- a/feature-gate-interface/src/lib.rs +++ b/feature-gate-interface/src/lib.rs @@ -11,190 +11,16 @@ //! 2. When the next epoch is entered the runtime will check for new activation requests and //! active them. When this occurs, the activation slot is recorded in the feature account -pub use solana_sdk_ids::feature::{check_id, id, ID}; -#[cfg(feature = "bincode")] -use { - solana_account::{AccountSharedData, ReadableAccount, WritableAccount}, - solana_account_info::AccountInfo, - solana_instruction::Instruction, - solana_program_error::ProgramError, - solana_pubkey::Pubkey, - solana_rent::Rent, - solana_system_interface::instruction as system_instruction, -}; - -#[cfg_attr( - feature = "serde", - derive(serde_derive::Deserialize, serde_derive::Serialize) -)] -#[derive(Default, Debug, PartialEq, Eq)] -pub struct Feature { - pub activated_at: Option, -} - -impl Feature { - pub const fn size_of() -> usize { - 9 // see test_feature_size_of. - } - - #[cfg(feature = "bincode")] - pub fn from_account_info(account_info: &AccountInfo) -> Result { - if *account_info.owner != id() { - return Err(ProgramError::InvalidAccountOwner); - } - if account_info.data_len() < Feature::size_of() { - return Err(ProgramError::InvalidAccountData); - } - bincode::deserialize(&account_info.data.borrow()) - .map_err(|_| ProgramError::InvalidAccountData) - } -} - -#[cfg(feature = "bincode")] -/// Activate a feature -pub fn activate(feature_id: &Pubkey, funding_address: &Pubkey, rent: &Rent) -> Vec { - activate_with_lamports( - feature_id, - funding_address, - rent.minimum_balance(Feature::size_of()), - ) -} - -#[cfg(feature = "bincode")] -pub fn activate_with_lamports( - feature_id: &Pubkey, - funding_address: &Pubkey, - lamports: u64, -) -> Vec { - vec![ - system_instruction::transfer(funding_address, feature_id, lamports), - system_instruction::allocate(feature_id, Feature::size_of() as u64), - system_instruction::assign(feature_id, &id()), - ] -} - -#[cfg(feature = "bincode")] -pub fn from_account(account: &T) -> Option { - if account.owner() != &id() || account.data().len() < Feature::size_of() { - None - } else { - bincode::deserialize(account.data()).ok() - } -} +pub mod error; +pub mod instruction; +pub mod state; #[cfg(feature = "bincode")] -pub fn to_account(feature: &Feature, account: &mut AccountSharedData) -> Option<()> { - bincode::serialize_into(account.data_as_mut_slice(), feature).ok() -} - -#[cfg(feature = "bincode")] -pub fn create_account(feature: &Feature, lamports: u64) -> AccountSharedData { - let data_len = Feature::size_of().max(bincode::serialized_size(feature).unwrap() as usize); - let mut account = AccountSharedData::new(lamports, data_len, &id()); - to_account(feature, &mut account).unwrap(); - account -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_feature_size_of() { - assert_eq!(Feature::size_of() as u64, { - let feature = Feature { - activated_at: Some(0), - }; - bincode::serialized_size(&feature).unwrap() - }); - assert!( - Feature::size_of() >= bincode::serialized_size(&Feature::default()).unwrap() as usize - ); - assert_eq!(Feature::default(), Feature { activated_at: None }); - - let features = [ - Feature { - activated_at: Some(0), - }, - Feature { - activated_at: Some(u64::MAX), - }, - ]; - for feature in &features { - assert_eq!( - Feature::size_of(), - bincode::serialized_size(feature).unwrap() as usize - ); - } - } - - #[test] - fn feature_from_account_info_none() { - let key = Pubkey::new_unique(); - let mut lamports = 42; - - let mut good_data = vec![0; Feature::size_of()]; - let mut small_data = vec![0; Feature::size_of() - 1]; // Too small - - assert_eq!( - Feature::from_account_info(&AccountInfo::new( - &key, - false, - false, - &mut lamports, - &mut good_data, - &id(), - false, - )), - Ok(Feature { activated_at: None }) - ); - assert_eq!( - Feature::from_account_info(&AccountInfo::new( - &key, - false, - false, - &mut lamports, - &mut small_data, // Too small - &id(), - false, - )), - Err(ProgramError::InvalidAccountData), - ); - assert_eq!( - Feature::from_account_info(&AccountInfo::new( - &key, - false, - false, - &mut lamports, - &mut good_data, - &Pubkey::new_unique(), // Wrong owner - false, - )), - Err(ProgramError::InvalidAccountOwner), - ); - } - - #[test] - fn feature_deserialize_none() { - assert_eq!( - from_account(&AccountSharedData::new(42, Feature::size_of(), &id())), - Some(Feature { activated_at: None }) - ); - assert_eq!( - from_account(&AccountSharedData::new( - 42, - Feature::size_of() - 1, // Too small - &id() - )), - None, - ); - assert_eq!( - from_account(&AccountSharedData::new( - 42, - Feature::size_of(), - &Pubkey::new_unique(), // Wrong owner - )), - None, - ); - } -} +pub use crate::{ + instruction::{activate, activate_with_lamports}, + state::{create_account, from_account, to_account}, +}; +pub use { + crate::state::Feature, + solana_sdk_ids::feature::{check_id, id, ID}, +}; diff --git a/feature-gate-interface/src/state.rs b/feature-gate-interface/src/state.rs new file mode 100644 index 000000000..99683f857 --- /dev/null +++ b/feature-gate-interface/src/state.rs @@ -0,0 +1,160 @@ +#[cfg(feature = "bincode")] +use { + solana_account::{AccountSharedData, ReadableAccount, WritableAccount}, + solana_account_info::AccountInfo, + solana_program_error::ProgramError, + solana_sdk_ids::feature::id, +}; + +#[cfg_attr( + feature = "serde", + derive(serde_derive::Deserialize, serde_derive::Serialize) +)] +#[derive(Default, Debug, PartialEq, Eq)] +pub struct Feature { + pub activated_at: Option, +} + +impl Feature { + pub const fn size_of() -> usize { + 9 // see test_feature_size_of. + } + + #[cfg(feature = "bincode")] + pub fn from_account_info(account_info: &AccountInfo) -> Result { + if *account_info.owner != id() { + return Err(ProgramError::InvalidAccountOwner); + } + if account_info.data_len() < Feature::size_of() { + return Err(ProgramError::InvalidAccountData); + } + bincode::deserialize(&account_info.data.borrow()) + .map_err(|_| ProgramError::InvalidAccountData) + } +} + +#[cfg(feature = "bincode")] +pub fn from_account(account: &T) -> Option { + if account.owner() != &id() || account.data().len() < Feature::size_of() { + None + } else { + bincode::deserialize(account.data()).ok() + } +} + +#[cfg(feature = "bincode")] +pub fn to_account(feature: &Feature, account: &mut AccountSharedData) -> Option<()> { + bincode::serialize_into(account.data_as_mut_slice(), feature).ok() +} + +#[cfg(feature = "bincode")] +pub fn create_account(feature: &Feature, lamports: u64) -> AccountSharedData { + let data_len = Feature::size_of().max(bincode::serialized_size(feature).unwrap() as usize); + let mut account = AccountSharedData::new(lamports, data_len, &id()); + to_account(feature, &mut account).unwrap(); + account +} + +#[cfg(test)] +mod test { + use {super::*, solana_pubkey::Pubkey}; + + #[test] + fn test_feature_size_of() { + assert_eq!(Feature::size_of() as u64, { + let feature = Feature { + activated_at: Some(0), + }; + bincode::serialized_size(&feature).unwrap() + }); + assert!( + Feature::size_of() >= bincode::serialized_size(&Feature::default()).unwrap() as usize + ); + assert_eq!(Feature::default(), Feature { activated_at: None }); + + let features = [ + Feature { + activated_at: Some(0), + }, + Feature { + activated_at: Some(u64::MAX), + }, + ]; + for feature in &features { + assert_eq!( + Feature::size_of(), + bincode::serialized_size(feature).unwrap() as usize + ); + } + } + + #[test] + fn feature_from_account_info_none() { + let key = Pubkey::new_unique(); + let mut lamports = 42; + + let mut good_data = vec![0; Feature::size_of()]; + let mut small_data = vec![0; Feature::size_of() - 1]; // Too small + + assert_eq!( + Feature::from_account_info(&AccountInfo::new( + &key, + false, + false, + &mut lamports, + &mut good_data, + &id(), + false, + )), + Ok(Feature { activated_at: None }) + ); + assert_eq!( + Feature::from_account_info(&AccountInfo::new( + &key, + false, + false, + &mut lamports, + &mut small_data, // Too small + &id(), + false, + )), + Err(ProgramError::InvalidAccountData), + ); + assert_eq!( + Feature::from_account_info(&AccountInfo::new( + &key, + false, + false, + &mut lamports, + &mut good_data, + &Pubkey::new_unique(), // Wrong owner + false, + )), + Err(ProgramError::InvalidAccountOwner), + ); + } + + #[test] + fn feature_deserialize_none() { + assert_eq!( + from_account(&AccountSharedData::new(42, Feature::size_of(), &id())), + Some(Feature { activated_at: None }) + ); + assert_eq!( + from_account(&AccountSharedData::new( + 42, + Feature::size_of() - 1, // Too small + &id() + )), + None, + ); + assert_eq!( + from_account(&AccountSharedData::new( + 42, + Feature::size_of(), + &Pubkey::new_unique(), // Wrong owner + )), + None, + ); + } +} diff --git a/fee-calculator/Cargo.toml b/fee-calculator/Cargo.toml index b66c4da1d..6ba8e3861 100644 --- a/fee-calculator/Cargo.toml +++ b/fee-calculator/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-fee-calculator" description = "Solana transaction fee calculation" documentation = "https://docs.rs/solana-fee-calculator" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/fee-structure/Cargo.toml b/fee-structure/Cargo.toml index 8a7c7b358..7735fd301 100644 --- a/fee-structure/Cargo.toml +++ b/fee-structure/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-fee-structure" description = "Solana fee structures." documentation = "https://docs.rs/solana-fee-structure" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/file-download/Cargo.toml b/file-download/Cargo.toml index 112200964..c9fc01027 100644 --- a/file-download/Cargo.toml +++ b/file-download/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-file-download" description = "Solana File Download Utility" documentation = "https://docs.rs/solana-file-download" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/frozen-abi-macro/Cargo.toml b/frozen-abi-macro/Cargo.toml index fcb6ff2db..b7a8b002f 100644 --- a/frozen-abi-macro/Cargo.toml +++ b/frozen-abi-macro/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-frozen-abi-macro" description = "Solana Frozen ABI Macro" documentation = "https://docs.rs/solana-frozen-abi-macro" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/frozen-abi/Cargo.toml b/frozen-abi/Cargo.toml index d1cbe4c68..9f0a81026 100644 --- a/frozen-abi/Cargo.toml +++ b/frozen-abi/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-frozen-abi" description = "Solana Frozen ABI" documentation = "https://docs.rs/solana-frozen-abi" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/hard-forks/Cargo.toml b/hard-forks/Cargo.toml index 5823fd932..1c0385bc7 100644 --- a/hard-forks/Cargo.toml +++ b/hard-forks/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-hard-forks" description = "The list of slot boundaries at which a hard fork should occur." documentation = "https://docs.rs/solana-hard-forks" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/hash/Cargo.toml b/hash/Cargo.toml index 43a020c54..7ef2a8a88 100644 --- a/hash/Cargo.toml +++ b/hash/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-hash" description = "Solana wrapper for the 32-byte output of a hashing algorithm." documentation = "https://docs.rs/solana-hash" -version = "2.3.0" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/inflation/Cargo.toml b/inflation/Cargo.toml index 693938bff..d8b2b4191 100644 --- a/inflation/Cargo.toml +++ b/inflation/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-inflation" description = "Configuration for Solana network inflation" documentation = "https://docs.rs/solana-inflation" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/instruction-error/Cargo.toml b/instruction-error/Cargo.toml index 3b38e5f73..4e5fd894c 100644 --- a/instruction-error/Cargo.toml +++ b/instruction-error/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-instruction-error" description = "Solana InstructionError type." documentation = "https://docs.rs/solana-instruction-error" -version = "1.0.0" +version = "2.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/instruction/Cargo.toml b/instruction/Cargo.toml index c9660d92d..051a07a62 100644 --- a/instruction/Cargo.toml +++ b/instruction/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-instruction" description = "Types for directing the execution of Solana programs." documentation = "https://docs.rs/solana-instruction" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/instructions-sysvar/Cargo.toml b/instructions-sysvar/Cargo.toml index 2cee4309f..1190d9026 100644 --- a/instructions-sysvar/Cargo.toml +++ b/instructions-sysvar/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-instructions-sysvar" description = "Type for instruction introspection during execution of Solana programs." documentation = "https://docs.rs/solana-instructions-sysvar" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/keccak-hasher/Cargo.toml b/keccak-hasher/Cargo.toml index 7026d7a35..075ff3884 100644 --- a/keccak-hasher/Cargo.toml +++ b/keccak-hasher/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-keccak-hasher" description = "Solana Keccak hashing" documentation = "https://docs.rs/solana-keccak-hasher" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/keypair/Cargo.toml b/keypair/Cargo.toml index ec1a871d9..959b53243 100644 --- a/keypair/Cargo.toml +++ b/keypair/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-keypair" description = "Concrete implementation of a Solana `Signer`." documentation = "https://docs.rs/solana-keypair" -version = "2.2.3" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/last-restart-slot/Cargo.toml b/last-restart-slot/Cargo.toml index 0c0883bf3..8b12ca7d6 100644 --- a/last-restart-slot/Cargo.toml +++ b/last-restart-slot/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-last-restart-slot" description = "Types and utilities for the Solana LastRestartSlot sysvar." documentation = "https://docs.rs/solana-last-restart-slot" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/loader-v2-interface/Cargo.toml b/loader-v2-interface/Cargo.toml index 95a49b0bd..22c33bc33 100644 --- a/loader-v2-interface/Cargo.toml +++ b/loader-v2-interface/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-loader-v2-interface" description = "Solana non-upgradable BPF loader v2 instructions." documentation = "https://docs.rs/solana-loader-v2-interface" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/loader-v3-interface/Cargo.toml b/loader-v3-interface/Cargo.toml index a54352e29..fcb46226c 100644 --- a/loader-v3-interface/Cargo.toml +++ b/loader-v3-interface/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-loader-v3-interface" description = "Solana loader V3 interface." documentation = "https://docs.rs/solana-loader-v3-interface" -version = "5.0.0" +version = "6.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/loader-v4-interface/Cargo.toml b/loader-v4-interface/Cargo.toml index 98166dcfd..8c770cb2b 100644 --- a/loader-v4-interface/Cargo.toml +++ b/loader-v4-interface/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-loader-v4-interface" description = "Solana loader V4 interface." documentation = "https://docs.rs/solana-loader-v4-interface" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 914e18265..4634c3d63 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-logger" description = "Solana Logger" documentation = "https://docs.rs/solana-logger" -version = "2.3.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/msg/Cargo.toml b/msg/Cargo.toml index b5c428fcc..694763349 100644 --- a/msg/Cargo.toml +++ b/msg/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-msg" description = "Solana msg macro." documentation = "https://docs.rs/solana-msg" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/native-token/Cargo.toml b/native-token/Cargo.toml index a38bb3951..060ee15b8 100644 --- a/native-token/Cargo.toml +++ b/native-token/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-native-token" description = "Definitions for the native SOL token and its fractional lamports." documentation = "https://docs.rs/solana-native-token" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/nonce/Cargo.toml b/nonce/Cargo.toml index 8b053d170..bd3794d38 100644 --- a/nonce/Cargo.toml +++ b/nonce/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-nonce" description = "Solana durable transaction nonces." documentation = "https://docs.rs/solana-nonce" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/offchain-message/Cargo.toml b/offchain-message/Cargo.toml index 20aabebda..cf7bbfcb1 100644 --- a/offchain-message/Cargo.toml +++ b/offchain-message/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-offchain-message" description = "Solana offchain message signing" documentation = "https://docs.rs/solana-offchain-message" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/package-metadata-macro/Cargo.toml b/package-metadata-macro/Cargo.toml index ba756b4b9..5ba4d2e75 100644 --- a/package-metadata-macro/Cargo.toml +++ b/package-metadata-macro/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-package-metadata-macro" description = "Solana Package Metadata Macro" documentation = "https://docs.rs/solana-package-metadata-macro" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/package-metadata/Cargo.toml b/package-metadata/Cargo.toml index 459751a0b..a065fd450 100644 --- a/package-metadata/Cargo.toml +++ b/package-metadata/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-package-metadata" description = "Solana Package Metadata" documentation = "https://docs.rs/solana-package-metadata" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/packet/Cargo.toml b/packet/Cargo.toml index 92b3f5feb..89f80bece 100644 --- a/packet/Cargo.toml +++ b/packet/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-packet" description = "The definition of a Solana network packet." documentation = "https://docs.rs/solana-packet" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/poh-config/Cargo.toml b/poh-config/Cargo.toml index 943a2876d..8f7fdb1af 100644 --- a/poh-config/Cargo.toml +++ b/poh-config/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-poh-config" description = "Definitions of Solana's proof of history." documentation = "https://docs.rs/solana-poh-config" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/precompile-error/Cargo.toml b/precompile-error/Cargo.toml index 04cf6e75f..f965530af 100644 --- a/precompile-error/Cargo.toml +++ b/precompile-error/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-precompile-error" description = "Solana PrecompileError type" documentation = "https://docs.rs/solana-precompile-error" -version = "2.2.2" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/presigner/Cargo.toml b/presigner/Cargo.toml index 89ced3b1d..1e613d11b 100644 --- a/presigner/Cargo.toml +++ b/presigner/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-presigner" description = "A Solana `Signer` implementation representing an externally-constructed `Signature`." documentation = "https://docs.rs/solana-presigner" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/program-entrypoint/Cargo.toml b/program-entrypoint/Cargo.toml index 90a059a5d..b9d1e28bf 100644 --- a/program-entrypoint/Cargo.toml +++ b/program-entrypoint/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-program-entrypoint" description = "The Solana BPF program entrypoint supported by the latest BPF loader." documentation = "https://docs.rs/solana-program-entrypoint" -version = "2.3.0" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/program-memory/Cargo.toml b/program-memory/Cargo.toml index 3a37e1143..29b8e11fd 100644 --- a/program-memory/Cargo.toml +++ b/program-memory/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-program-memory" description = "Basic low-level memory operations for Solana." documentation = "https://docs.rs/solana-program-memory" -version = "2.3.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/program-option/Cargo.toml b/program-option/Cargo.toml index 049d384ed..6bfba39fa 100644 --- a/program-option/Cargo.toml +++ b/program-option/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-program-option" description = "A C representation of Rust's Option, used in Solana programs." documentation = "https://docs.rs/solana-program-option" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/program-pack/Cargo.toml b/program-pack/Cargo.toml index a0ddf5519..0841b3e01 100644 --- a/program-pack/Cargo.toml +++ b/program-pack/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-program-pack" description = "Solana Pack serialization trait." documentation = "https://docs.rs/solana-program-pack" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/program/Cargo.toml b/program/Cargo.toml index 1a9df8f1d..0d5cc4c99 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -29,7 +29,11 @@ borsh = [ "solana-program-error/borsh", "solana-pubkey/borsh", ] -dev-context-only-utils = ["solana-instructions-sysvar/dev-context-only-utils"] +dev-context-only-utils = [ + "solana-instructions-sysvar/dev-context-only-utils", + "dep:solana-system-interface", + "solana-system-interface/bincode", +] frozen-abi = [ "dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", @@ -91,6 +95,7 @@ solana-short-vec = { workspace = true } solana-slot-hashes = { workspace = true, features = ["serde", "sysvar"] } solana-slot-history = { workspace = true, features = ["serde", "sysvar"] } solana-stable-layout = { workspace = true } +solana-system-interface = { optional = true, workspace = true, features = ["bincode"] } solana-sysvar = { workspace = true, features = ["bincode", "bytemuck"] } solana-sysvar-id = { workspace = true } @@ -102,7 +107,6 @@ solana-define-syscall = { workspace = true } [dev-dependencies] solana-pubkey = { workspace = true, features = ["dev-context-only-utils"] } -solana-system-interface = { workspace = true } solana-sysvar = { workspace = true, features = ["dev-context-only-utils"] } [lints] diff --git a/program/src/sysvar.rs b/program/src/sysvar.rs index e30479e59..cd17415ca 100644 --- a/program/src/sysvar.rs +++ b/program/src/sysvar.rs @@ -6,7 +6,7 @@ pub use { solana_sdk_ids::sysvar::{check_id, id, ID}, solana_sysvar::{ clock, epoch_rewards, epoch_schedule, fees, last_restart_slot, recent_blockhashes, rent, - rewards, slot_hashes, slot_history, stake_history, Sysvar, SysvarSerialize, + rewards, slot_hashes, slot_history, Sysvar, SysvarSerialize, }, }; diff --git a/pubkey/Cargo.toml b/pubkey/Cargo.toml index 0534490f4..59cc93840 100644 --- a/pubkey/Cargo.toml +++ b/pubkey/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-pubkey" description = "Solana account addresses" documentation = "https://docs.rs/solana-pubkey" -version = "2.4.0" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/quic-definitions/Cargo.toml b/quic-definitions/Cargo.toml index 8df3ff2b9..edefd1c91 100644 --- a/quic-definitions/Cargo.toml +++ b/quic-definitions/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-quic-definitions" description = "Definitions related to Solana over QUIC." documentation = "https://docs.rs/solana-quic-definitions" -version = "2.3.1" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/rent-collector/src/lib.rs b/rent-collector/src/lib.rs index 486307a3b..b638a39eb 100644 --- a/rent-collector/src/lib.rs +++ b/rent-collector/src/lib.rs @@ -1,6 +1,10 @@ //! Calculate and collect rent from accounts. #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![deprecated( + since = "2.3.0", + note = "Use Rent directly, or solana_runtime::rent_collector" +)] use { solana_account::{AccountSharedData, ReadableAccount, WritableAccount}, diff --git a/rent/Cargo.toml b/rent/Cargo.toml index 7dfa013c7..0b6690966 100644 --- a/rent/Cargo.toml +++ b/rent/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-rent" description = "Configuration for Solana network rent." documentation = "https://docs.rs/solana-rent" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/reward-info/Cargo.toml b/reward-info/Cargo.toml index a332d0260..97d4f74a1 100644 --- a/reward-info/Cargo.toml +++ b/reward-info/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-reward-info" description = "Solana vote reward info types" documentation = "https://docs.rs/solana-reward-info" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/sanitize/Cargo.toml b/sanitize/Cargo.toml index c02e227e8..db1c724e2 100644 --- a/sanitize/Cargo.toml +++ b/sanitize/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-sanitize" description = "Solana Message Sanitization" documentation = "https://docs.rs/solana-sanitize" rust-version = "1.81.0" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/scripts/test-doc.sh b/scripts/test-doc.sh index 35e37afef..27c96edb7 100755 --- a/scripts/test-doc.sh +++ b/scripts/test-doc.sh @@ -5,4 +5,4 @@ here="$(dirname "$0")" src_root="$(readlink -f "${here}/..")" cd "${src_root}" -./cargo nightly hack test --doc -- --nocapture +./cargo nightly hack test --doc --all-features -- --nocapture diff --git a/sdk-ids/Cargo.toml b/sdk-ids/Cargo.toml index c43d77647..197daca68 100644 --- a/sdk-ids/Cargo.toml +++ b/sdk-ids/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-sdk-ids" description = "Solana SDK IDs" documentation = "https://docs.rs/solana-sdk-ids" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/sdk-macro/Cargo.toml b/sdk-macro/Cargo.toml index 5b2b807d5..eecfd891d 100644 --- a/sdk-macro/Cargo.toml +++ b/sdk-macro/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-sdk-macro" description = "Solana SDK Macro" documentation = "https://docs.rs/solana-sdk-macro" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 213c27d9b..26555517a 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -40,6 +40,7 @@ full = [ borsh = ["solana-program/borsh"] dev-context-only-utils = [ "solana-account/dev-context-only-utils", + "solana-program/dev-context-only-utils", "solana-transaction/dev-context-only-utils", ] frozen-abi = [ diff --git a/secp256k1-program/Cargo.toml b/secp256k1-program/Cargo.toml index c4f9bd817..2fdfe66b9 100644 --- a/secp256k1-program/Cargo.toml +++ b/secp256k1-program/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-secp256k1-program" description = "Instructions for the Solana Secp256k1 native program." documentation = "https://docs.rs/solana-secp256k1-program" -version = "2.2.3" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/secp256k1-recover/Cargo.toml b/secp256k1-recover/Cargo.toml index 1d4543aac..98d9148e1 100644 --- a/secp256k1-recover/Cargo.toml +++ b/secp256k1-recover/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-secp256k1-recover" description = "Solana SECP256K1 Recover" documentation = "https://docs.rs/solana-secp256k1-recover" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/secp256r1-program/Cargo.toml b/secp256r1-program/Cargo.toml index ee2c774be..68ba72114 100644 --- a/secp256r1-program/Cargo.toml +++ b/secp256r1-program/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-secp256r1-program" description = "Precompile implementation for the secp256r1 elliptic curve." documentation = "https://docs.rs/solana-secp256r1" -version = "2.2.4" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/seed-derivable/Cargo.toml b/seed-derivable/Cargo.toml index 4bb678f85..abab7bafa 100644 --- a/seed-derivable/Cargo.toml +++ b/seed-derivable/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-seed-derivable" description = "Solana trait defining the interface by which keys are derived." documentation = "https://docs.rs/solana-seed-derivable" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/seed-phrase/Cargo.toml b/seed-phrase/Cargo.toml index 3a367dc93..60268cc24 100644 --- a/seed-phrase/Cargo.toml +++ b/seed-phrase/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-seed-phrase" description = "Solana functions for generating keypairs from seed phrases." documentation = "https://docs.rs/solana-seed-phrase" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/serde-varint/Cargo.toml b/serde-varint/Cargo.toml index 997b14673..d4dd26efd 100644 --- a/serde-varint/Cargo.toml +++ b/serde-varint/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-serde-varint" description = "Solana definitions for integers that serialize to variable size" documentation = "https://docs.rs/solana-serde-varint" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/serde/Cargo.toml b/serde/Cargo.toml index d5c0f2c9c..ebe60a4b6 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-serde" description = "Solana serde helpers" documentation = "https://docs.rs/solana-serde" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/serialize-utils/Cargo.toml b/serialize-utils/Cargo.toml index 071e1903c..64644350e 100644 --- a/serialize-utils/Cargo.toml +++ b/serialize-utils/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-serialize-utils" description = "Solana helpers for reading and writing bytes." documentation = "https://docs.rs/solana-serialize-utils" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/sha256-hasher/Cargo.toml b/sha256-hasher/Cargo.toml index 17d278c13..3122aa634 100644 --- a/sha256-hasher/Cargo.toml +++ b/sha256-hasher/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-sha256-hasher" description = "Solana SHA256 hashing" documentation = "https://docs.rs/solana-sha256-hasher" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/short-vec/Cargo.toml b/short-vec/Cargo.toml index b1a880abd..f85d062da 100644 --- a/short-vec/Cargo.toml +++ b/short-vec/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-short-vec" description = "Solana compact serde-encoding of vectors with small length." documentation = "https://docs.rs/solana-short-vec" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/shred-version/Cargo.toml b/shred-version/Cargo.toml index a2b6896fb..33c613a83 100644 --- a/shred-version/Cargo.toml +++ b/shred-version/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-shred-version" description = "Calculation of shred versions." documentation = "https://docs.rs/solana-shred-version" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/signature/Cargo.toml b/signature/Cargo.toml index e84e189fb..314005396 100644 --- a/signature/Cargo.toml +++ b/signature/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-signature" description = "Solana 64-byte signature type" documentation = "https://docs.rs/solana-signature" -version = "2.3.0" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/signer/Cargo.toml b/signer/Cargo.toml index abcbb0bc8..49ea25085 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-signer" description = "Abstractions for Solana transaction signers. See `solana-keypair` for a concrete implementation." documentation = "https://docs.rs/solana-signer" -version = "2.2.1" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/slot-hashes/Cargo.toml b/slot-hashes/Cargo.toml index 2b4a90e82..648f401f3 100644 --- a/slot-hashes/Cargo.toml +++ b/slot-hashes/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-slot-hashes" description = "Types and utilities for the Solana SlotHashes sysvar." documentation = "https://docs.rs/solana-slot-hashes" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/slot-history/Cargo.toml b/slot-history/Cargo.toml index b9e634de2..bafbe7104 100644 --- a/slot-history/Cargo.toml +++ b/slot-history/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-slot-history" description = "Types and utilities for the Solana SlotHistory sysvar." documentation = "https://docs.rs/solana-slot-history" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/stable-layout/Cargo.toml b/stable-layout/Cargo.toml index 80c0b0e7f..3a88e6c7f 100644 --- a/stable-layout/Cargo.toml +++ b/stable-layout/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-stable-layout" description = "Solana types with stable memory layouts. Internal use only." documentation = "https://docs.rs/solana-stable-layout" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/system-interface/Cargo.toml b/system-interface/Cargo.toml new file mode 100644 index 000000000..9acb03ec6 --- /dev/null +++ b/system-interface/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "solana-system-interface" +version = "2.0.0" +description = "Instructions and constructors for the System program" +readme = "README.md" +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } +rust-version = "1.81.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[dependencies] +num-traits = { workspace = true } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } +solana-frozen-abi = { workspace = true, features = ["frozen-abi"], optional = true } +solana-frozen-abi-macro = { workspace = true, features = ["frozen-abi"], optional = true } +solana-instruction = { workspace = true, features = ["bincode", "std"], optional = true } +solana-logger = { workspace = true, optional = true } +solana-msg = { workspace = true } +solana-program-error = { workspace = true } +solana-pubkey = { workspace = true, default-features = false } + +[dev-dependencies] +anyhow = { workspace = true } +borsh = { workspace = true, features = ["derive", "unstable__schema"] } +solana-account-info = { workspace = true } +solana-cpi = { path = "../cpi" } +solana-example-mocks = { path = "../example-mocks" } +solana-nonce = { workspace = true } +solana-program-entrypoint = { workspace = true } +solana-program-error = { workspace = true, features = ["borsh"] } +solana-pubkey = { workspace = true, features = ["std"] } +solana-system-interface = { path = ".", features = ["bincode"] } +solana-sysvar = { workspace = true } +solana-sysvar-id = { workspace = true } +static_assertions = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } + +[features] +bincode = ["dep:solana-instruction", "serde"] +frozen-abi = [ + "dep:solana-frozen-abi", + "dep:solana-frozen-abi-macro", + "dep:solana-logger", + "serde", + "solana-pubkey/frozen-abi", + "solana-pubkey/std" +] +serde = ["dep:serde", "dep:serde_derive", "solana-pubkey/serde"] + +[lib] +crate-type = ["rlib"] diff --git a/system-interface/README.md b/system-interface/README.md new file mode 100644 index 000000000..bcf079394 --- /dev/null +++ b/system-interface/README.md @@ -0,0 +1,104 @@ +

+ + Solana + +

+ +# Solana System Interface + +This crate contains instructions and constructors for interacting with the [System program](https://solana.com/docs/core/programs#core-programs). + +The System program can be used to create new accounts, allocate account data, assign accounts to owning programs, transfer lamports from System Program owned accounts and pay transaction fees. + +## Getting Started + +From your project folder: + +```bash +cargo add solana-system-interface --features bincode +``` + +This will add the `solana-system-interface` dependency with the `bincode` feature enabled to your `Cargo.toml` file. The `bincode` feature contains the instruction constructors to create instructions for the System program. + +## Examples + +Creating an account: + +```rust +use solana_rpc_client::rpc_client::RpcClient; +use solana_sdk::{ + signature::{Keypair, Signer}, + transaction::Transaction, +}; +use solana_system_interface::instruction; +use anyhow::Result; + +fn create_account( + client: &RpcClient, + payer: &Keypair, + new_account: &Keypair, + owning_program: &Pubkey, + space: u64, +) -> Result<()> { + let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; + let instr = instruction::create_account( + &payer.pubkey(), + &new_account.pubkey(), + rent, + space, + owning_program, + ); + + let blockhash = client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[instr], + Some(&payer.pubkey()), + &[payer, new_account], + blockhash, + ); + + let _sig = client.send_and_confirm_transaction(&tx)?; + + Ok(()) +} +``` + +Transfer lamports between accounts: + +```rust +use solana_rpc_client::rpc_client::RpcClient; +use solana_pubkey::Pubkey; +use solana_sdk::{ + signature::{Keypair, Signer}, + transaction::Transaction, +}; +use solana_system_interface::instruction; +use anyhow::Result; + +fn transfer( + client: &RpcClient, + from: &Keypair, + recipient: &Pubkey, + lamports: u64, +) -> Result<()> { + let instr = instruction::transfer( + &from.pubkey(), + recipient, + lamports, + ); + + let blockhash = client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[instr], + Some(&from.pubkey()), + &[from], + blockhash, + ); + + let _sig = client.send_and_confirm_transaction(&tx)?; + + Ok(()) +} +``` + +More examples can be found on the crate [documentation](https://docs.rs/solana-system-interface/latest/solana-system-interface/). diff --git a/system-interface/src/error.rs b/system-interface/src/error.rs new file mode 100644 index 000000000..2b62de31e --- /dev/null +++ b/system-interface/src/error.rs @@ -0,0 +1,164 @@ +use { + num_traits::{FromPrimitive, ToPrimitive}, + solana_program_error::{ProgramError, ToStr}, +}; + +// Use strum when testing to ensure our FromPrimitive +// impl is exhaustive +#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Deserialize, serde_derive::Serialize) +)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SystemError { + /// An account with the same address already exists. + AccountAlreadyInUse, + /// Account does not have enough SOL to perform the operation. + ResultWithNegativeLamports, + /// Cannot assign account to this program id. + InvalidProgramId, + /// Cannot allocate account data of this length. + InvalidAccountDataLength, + /// Length of requested seed is too long. + MaxSeedLengthExceeded, + /// Provided address does not match addressed derived from seed. + AddressWithSeedMismatch, + /// Advancing stored nonce requires a populated RecentBlockhashes sysvar. + NonceNoRecentBlockhashes, + /// Stored nonce is still in recent_blockhashes. + NonceBlockhashNotExpired, + /// Specified nonce does not match stored nonce. + NonceUnexpectedBlockhashValue, +} + +impl FromPrimitive for SystemError { + #[inline] + fn from_i64(n: i64) -> Option { + if n == Self::AccountAlreadyInUse as i64 { + Some(Self::AccountAlreadyInUse) + } else if n == Self::ResultWithNegativeLamports as i64 { + Some(Self::ResultWithNegativeLamports) + } else if n == Self::InvalidProgramId as i64 { + Some(Self::InvalidProgramId) + } else if n == Self::InvalidAccountDataLength as i64 { + Some(Self::InvalidAccountDataLength) + } else if n == Self::MaxSeedLengthExceeded as i64 { + Some(Self::MaxSeedLengthExceeded) + } else if n == Self::AddressWithSeedMismatch as i64 { + Some(Self::AddressWithSeedMismatch) + } else if n == Self::NonceNoRecentBlockhashes as i64 { + Some(Self::NonceNoRecentBlockhashes) + } else if n == Self::NonceBlockhashNotExpired as i64 { + Some(Self::NonceBlockhashNotExpired) + } else if n == Self::NonceUnexpectedBlockhashValue as i64 { + Some(Self::NonceUnexpectedBlockhashValue) + } else { + None + } + } + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_i64(n as i64) + } +} + +impl ToPrimitive for SystemError { + #[inline] + fn to_i64(&self) -> Option { + Some(match *self { + Self::AccountAlreadyInUse => Self::AccountAlreadyInUse as i64, + Self::ResultWithNegativeLamports => Self::ResultWithNegativeLamports as i64, + Self::InvalidProgramId => Self::InvalidProgramId as i64, + Self::InvalidAccountDataLength => Self::InvalidAccountDataLength as i64, + Self::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded as i64, + Self::AddressWithSeedMismatch => Self::AddressWithSeedMismatch as i64, + Self::NonceNoRecentBlockhashes => Self::NonceNoRecentBlockhashes as i64, + Self::NonceBlockhashNotExpired => Self::NonceBlockhashNotExpired as i64, + Self::NonceUnexpectedBlockhashValue => Self::NonceUnexpectedBlockhashValue as i64, + }) + } + #[inline] + fn to_u64(&self) -> Option { + self.to_i64().map(|x| x as u64) + } +} + +impl core::error::Error for SystemError {} + +impl ToStr for SystemError { + fn to_str(&self) -> &'static str { + match self { + SystemError::AccountAlreadyInUse => "an account with the same address already exists", + SystemError::ResultWithNegativeLamports => { + "account does not have enough SOL to perform the operation" + } + SystemError::InvalidProgramId => "cannot assign account to this program id", + SystemError::InvalidAccountDataLength => "cannot allocate account data of this length", + SystemError::MaxSeedLengthExceeded => "length of requested seed is too long", + SystemError::AddressWithSeedMismatch => { + "provided address does not match addressed derived from seed" + } + SystemError::NonceNoRecentBlockhashes => { + "advancing stored nonce requires a populated RecentBlockhashes sysvar" + } + SystemError::NonceBlockhashNotExpired => "stored nonce is still in recent_blockhashes", + SystemError::NonceUnexpectedBlockhashValue => { + "specified nonce does not match stored nonce" + } + } + } +} + +impl core::fmt::Display for SystemError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str(self.to_str()) + } +} + +impl From for ProgramError { + fn from(e: SystemError) -> Self { + Self::Custom(e as u32) + } +} + +impl TryFrom for SystemError { + type Error = ProgramError; + fn try_from(error: u32) -> Result { + match error { + 0 => Ok(SystemError::AccountAlreadyInUse), + 1 => Ok(SystemError::ResultWithNegativeLamports), + 2 => Ok(SystemError::InvalidProgramId), + 3 => Ok(SystemError::InvalidAccountDataLength), + 4 => Ok(SystemError::MaxSeedLengthExceeded), + 5 => Ok(SystemError::AddressWithSeedMismatch), + 6 => Ok(SystemError::NonceNoRecentBlockhashes), + 7 => Ok(SystemError::NonceBlockhashNotExpired), + 8 => Ok(SystemError::NonceUnexpectedBlockhashValue), + _ => Err(ProgramError::InvalidArgument), + } + } +} + +impl From for SystemError { + fn from(error: u64) -> Self { + SystemError::from_u64(error).unwrap() + } +} + +#[cfg(test)] +mod tests { + use {super::SystemError, num_traits::FromPrimitive, strum::IntoEnumIterator}; + + #[test] + fn test_system_error_from_primitive_exhaustive() { + for variant in SystemError::iter() { + let variant_i64 = variant.clone() as i64; + assert_eq!( + SystemError::from_repr(variant_i64 as usize), + SystemError::from_i64(variant_i64) + ); + assert_eq!(SystemError::from(variant_i64 as u64), variant); + } + } +} diff --git a/system-interface/src/instruction.rs b/system-interface/src/instruction.rs new file mode 100644 index 000000000..d6d9f2241 --- /dev/null +++ b/system-interface/src/instruction.rs @@ -0,0 +1,1718 @@ +//! Instructions and constructors for the system program. +//! +//! The system program is responsible for the creation of accounts and [nonce +//! accounts][na]. It is responsible for transferring lamports from accounts +//! owned by the system program, including typical user wallet accounts. +//! +//! [na]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces +//! +//! Account creation typically involves three steps: [`allocate`] space, +//! [`transfer`] lamports for rent, [`assign`] to its owning program. The +//! [`create_account`] function does all three at once. All new accounts must +//! contain enough lamports to be [rent exempt], or else the creation +//! instruction will fail. +//! +//! [rent exempt]: https://solana.com/docs/core/accounts#rent-exemption +//! +//! The accounts created by the System program can either be user-controlled, +//! where the secret keys are held outside the blockchain, +//! or they can be [program derived addresses][pda], +//! where write access to accounts is granted by an owning program. +//! +//! [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +//! +//! Most of the functions in this module construct an [`Instruction`], that must +//! be submitted to the runtime for execution, either via RPC, typically with +//! [`RpcClient`], or through [cross-program invocation][cpi]. +//! +//! When invoking through CPI, the [`invoke`] or [`invoke_signed`] instruction +//! requires all account references to be provided explicitly as [`AccountInfo`] +//! values. The account references required are specified in the documentation +//! for the [`SystemInstruction`] variants for each System program instruction, +//! and these variants are linked from the documentation for their constructors. +//! +//! [`RpcClient`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html +//! [cpi]: https://docs.rs/solana-cpi/latest/solana_cpi/index.html +//! [`invoke`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +//! [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +//! [`AccountInfo`]: https://docs.rs/solana-account-info/latest/solana_account_info/struct.AccountInfo.html +//! [`Instruction`]: +//! https://docs.rs/solana-instruction/latest/solana_instruction/struct.Instruction.html + +use solana_pubkey::Pubkey; +#[cfg(feature = "bincode")] +use { + crate::program::ID, + solana_instruction::{AccountMeta, Instruction}, +}; + +// Inline some constants to avoid dependencies. +// +// Note: replace these inline IDs with the corresponding value from +// `solana_sdk_ids` once the version is updated to 2.2.0. + +#[cfg(feature = "bincode")] +const RECENT_BLOCKHASHES_ID: Pubkey = + Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111"); + +#[cfg(feature = "bincode")] +const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111"); + +#[cfg(feature = "bincode")] +#[cfg(test)] +static_assertions::const_assert_eq!(solana_nonce::state::State::size(), NONCE_STATE_SIZE); +/// The serialized size of the nonce state. +#[cfg(feature = "bincode")] +const NONCE_STATE_SIZE: usize = 80; + +/// An instruction to the system program. +#[cfg_attr( + feature = "frozen-abi", + solana_frozen_abi_macro::frozen_abi(digest = "8M189WgLE19cw1iYDAFLNJKoAUKyqF9jsKYennJi5BfK"), + derive( + solana_frozen_abi_macro::AbiExample, + solana_frozen_abi_macro::AbiEnumVisitor + ) +)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Deserialize, serde_derive::Serialize) +)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum SystemInstruction { + /// Create a new account + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE, SIGNER]` New account + CreateAccount { + /// Number of lamports to transfer to the new account + lamports: u64, + + /// Number of bytes of memory to allocate + space: u64, + + /// Address of program that will own the new account + owner: Pubkey, + }, + + /// Assign account to a program + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Assigned account public key + Assign { + /// Owner program account + owner: Pubkey, + }, + + /// Transfer lamports + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE]` Recipient account + Transfer { lamports: u64 }, + + /// Create a new account at an address derived from a base pubkey and a seed + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE]` Created account + /// 2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be + /// provided as a signer, but may be the same as the funding account + /// and provided as account 0 + CreateAccountWithSeed { + /// Base public key + base: Pubkey, + + /// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN` + seed: String, + + /// Number of lamports to transfer to the new account + lamports: u64, + + /// Number of bytes of memory to allocate + space: u64, + + /// Owner program account address + owner: Pubkey, + }, + + /// Consumes a stored nonce, replacing it with a successor + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[]` RecentBlockhashes sysvar + /// 2. `[SIGNER]` Nonce authority + AdvanceNonceAccount, + + /// Withdraw funds from a nonce account + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[WRITE]` Recipient account + /// 2. `[]` RecentBlockhashes sysvar + /// 3. `[]` Rent sysvar + /// 4. `[SIGNER]` Nonce authority + /// + /// The `u64` parameter is the lamports to withdraw, which must leave the + /// account balance above the rent exempt reserve or at zero. + WithdrawNonceAccount(u64), + + /// Drive state of Uninitialized nonce account to Initialized, setting the nonce value + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[]` RecentBlockhashes sysvar + /// 2. `[]` Rent sysvar + /// + /// The `Pubkey` parameter specifies the entity authorized to execute nonce + /// instruction on the account + /// + /// No signatures are required to execute this instruction, enabling derived + /// nonce account addresses + InitializeNonceAccount(Pubkey), + + /// Change the entity authorized to execute nonce instructions on the account + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[SIGNER]` Nonce authority + /// + /// The `Pubkey` parameter identifies the entity to authorize + AuthorizeNonceAccount(Pubkey), + + /// Allocate space in a (possibly new) account without funding + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` New account + Allocate { + /// Number of bytes of memory to allocate + space: u64, + }, + + /// Allocate space for and assign an account at an address + /// derived from a base public key and a seed + /// + /// # Account references + /// 0. `[WRITE]` Allocated account + /// 1. `[SIGNER]` Base account + AllocateWithSeed { + /// Base public key + base: Pubkey, + + /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` + seed: String, + + /// Number of bytes of memory to allocate + space: u64, + + /// Owner program account + owner: Pubkey, + }, + + /// Assign account to a program based on a seed + /// + /// # Account references + /// 0. `[WRITE]` Assigned account + /// 1. `[SIGNER]` Base account + AssignWithSeed { + /// Base public key + base: Pubkey, + + /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` + seed: String, + + /// Owner program account + owner: Pubkey, + }, + + /// Transfer lamports from a derived address + /// + /// # Account references + /// 0. `[WRITE]` Funding account + /// 1. `[SIGNER]` Base for funding account + /// 2. `[WRITE]` Recipient account + TransferWithSeed { + /// Amount to transfer + lamports: u64, + + /// Seed to use to derive the funding account address + from_seed: String, + + /// Owner to use to derive the funding account address + from_owner: Pubkey, + }, + + /// One-time idempotent upgrade of legacy nonce versions in order to bump + /// them out of chain blockhash domain. + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + UpgradeNonceAccount, +} + +/// Create an account. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::CreateAccount`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// Account creation typically involves three steps: [`allocate`] space, +/// [`transfer`] lamports for rent, [`assign`] to its owning program. The +/// [`create_account`] function does all three at once. +/// +/// # Required signers +/// +/// The `from_pubkey` and `to_pubkey` signers must sign the transaction. +/// +/// # Examples +/// +/// These examples use a single invocation of +/// [`SystemInstruction::CreateAccount`] to create a new account, allocate some +/// space, transfer it the minimum lamports for rent exemption, and assign it to +/// the system program, +/// +/// ## Example: client-side RPC +/// +/// This example submits the instruction from an RPC client. +/// The `payer` and `new_account` are signers. +/// +/// ``` +/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::{instruction, program}; +/// use anyhow::Result; +/// +/// fn create_account( +/// client: &RpcClient, +/// payer: &Keypair, +/// new_account: &Keypair, +/// space: u64, +/// ) -> Result<()> { +/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; +/// let instr = instruction::create_account( +/// &payer.pubkey(), +/// &new_account.pubkey(), +/// rent, +/// space, +/// &program::ID, +/// ); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &[instr], +/// Some(&payer.pubkey()), +/// &[payer, new_account], +/// blockhash, +/// ); +/// +/// let _sig = client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # let payer = Keypair::new(); +/// # let new_account = Keypair::new(); +/// # let client = RpcClient::new(String::new()); +/// # create_account(&client, &payer, &new_account, 0); +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +/// +/// ## Example: on-chain program +/// +/// This example submits the instruction from an on-chain Solana program. The +/// created account is a [program derived address][pda]. The `payer` and +/// `new_account_pda` are signers, with `new_account_pda` being signed for +/// virtually by the program itself via [`invoke_signed`], `payer` being signed +/// for by the client that submitted the transaction. +/// +/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +/// +/// ``` +/// use borsh::{BorshDeserialize, BorshSerialize}; +/// use solana_account_info::{next_account_info, AccountInfo}; +/// use solana_cpi::invoke_signed; +/// use solana_program_entrypoint::entrypoint; +/// use solana_program_error::ProgramResult; +/// use solana_pubkey::Pubkey; +/// use solana_system_interface::{instruction, program}; +/// use solana_sysvar::{rent::Rent, Sysvar}; +/// +/// #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// pub struct CreateAccountInstruction { +/// /// The PDA seed used to distinguish the new account from other PDAs +/// pub new_account_seed: [u8; 16], +/// /// The PDA bump seed +/// pub new_account_bump_seed: u8, +/// /// The amount of space to allocate for `new_account_pda` +/// pub space: u64, +/// } +/// +/// entrypoint!(process_instruction); +/// +/// fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; +/// +/// let account_info_iter = &mut accounts.iter(); +/// +/// let payer = next_account_info(account_info_iter)?; +/// let new_account_pda = next_account_info(account_info_iter)?; +/// let system_account = next_account_info(account_info_iter)?; +/// +/// assert!(payer.is_signer); +/// assert!(payer.is_writable); +/// // Note that `new_account_pda` is not a signer yet. +/// // This program will sign for it via `invoke_signed`. +/// assert!(!new_account_pda.is_signer); +/// assert!(new_account_pda.is_writable); +/// assert!(program::check_id(system_account.key)); +/// +/// let new_account_seed = &instr.new_account_seed; +/// let new_account_bump_seed = instr.new_account_bump_seed; +/// +/// let rent = Rent::get()? +/// .minimum_balance(instr.space.try_into().expect("overflow")); +/// +/// invoke_signed( +/// &instruction::create_account( +/// payer.key, +/// new_account_pda.key, +/// rent, +/// instr.space, +/// &program::ID +/// ), +/// &[payer.clone(), new_account_pda.clone()], +/// &[&[ +/// payer.key.as_ref(), +/// new_account_seed, +/// &[new_account_bump_seed], +/// ]], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +#[cfg(feature = "bincode")] +pub fn create_account( + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, + space: u64, + owner: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, true), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::CreateAccount { + lamports, + space, + owner: *owner, + }, + account_metas, + ) +} + +// we accept `to` as a parameter so that callers do their own error handling when +// calling create_with_seed() +#[cfg(feature = "bincode")] +pub fn create_account_with_seed( + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner) + base: &Pubkey, + seed: &str, + lamports: u64, + space: u64, + owner: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, false), + AccountMeta::new_readonly(*base, true), + ]; + + Instruction::new_with_bincode( + ID, + &SystemInstruction::CreateAccountWithSeed { + base: *base, + seed: seed.to_string(), + lamports, + space, + owner: *owner, + }, + account_metas, + ) +} + +/// Assign ownership of an account from the system program. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::Assign`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// # Required signers +/// +/// The `pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// These examples allocate space for an account, transfer it the minimum +/// balance for rent exemption, and assign the account to a program. +/// +/// ## Example: client-side RPC +/// +/// This example submits the instructions from an RPC client. +/// It assigns the account to a provided program account. +/// The `payer` and `new_account` are signers. +/// +/// ``` +/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn create_account( +/// client: &RpcClient, +/// payer: &Keypair, +/// new_account: &Keypair, +/// owning_program: &Pubkey, +/// space: u64, +/// ) -> Result<()> { +/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; +/// +/// let transfer_instr = instruction::transfer( +/// &payer.pubkey(), +/// &new_account.pubkey(), +/// rent, +/// ); +/// +/// let allocate_instr = instruction::allocate( +/// &new_account.pubkey(), +/// space, +/// ); +/// +/// let assign_instr = instruction::assign( +/// &new_account.pubkey(), +/// owning_program, +/// ); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &[transfer_instr, allocate_instr, assign_instr], +/// Some(&payer.pubkey()), +/// &[payer, new_account], +/// blockhash, +/// ); +/// +/// let _sig = client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # let client = RpcClient::new(String::new()); +/// # let payer = Keypair::new(); +/// # let new_account = Keypair::new(); +/// # let owning_program = Pubkey::new_unique(); +/// # create_account(&client, &payer, &new_account, &owning_program, 1); +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +/// +/// ## Example: on-chain program +/// +/// This example submits the instructions from an on-chain Solana program. The +/// created account is a [program derived address][pda], funded by `payer`, and +/// assigned to the running program. The `payer` and `new_account_pda` are +/// signers, with `new_account_pda` being signed for virtually by the program +/// itself via [`invoke_signed`], `payer` being signed for by the client that +/// submitted the transaction. +/// +/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +/// +/// ``` +/// use borsh::{BorshDeserialize, BorshSerialize}; +/// use solana_account_info::{next_account_info, AccountInfo}; +/// use solana_cpi::invoke_signed; +/// use solana_program_entrypoint::entrypoint; +/// use solana_program_error::ProgramResult; +/// use solana_pubkey::Pubkey; +/// use solana_system_interface::{instruction, program}; +/// use solana_sysvar::{rent::Rent, Sysvar}; +/// +/// #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// pub struct CreateAccountInstruction { +/// /// The PDA seed used to distinguish the new account from other PDAs +/// pub new_account_seed: [u8; 16], +/// /// The PDA bump seed +/// pub new_account_bump_seed: u8, +/// /// The amount of space to allocate for `new_account_pda` +/// pub space: u64, +/// } +/// +/// entrypoint!(process_instruction); +/// +/// fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; +/// +/// let account_info_iter = &mut accounts.iter(); +/// +/// let payer = next_account_info(account_info_iter)?; +/// let new_account_pda = next_account_info(account_info_iter)?; +/// let system_account = next_account_info(account_info_iter)?; +/// +/// assert!(payer.is_signer); +/// assert!(payer.is_writable); +/// // Note that `new_account_pda` is not a signer yet. +/// // This program will sign for it via `invoke_signed`. +/// assert!(!new_account_pda.is_signer); +/// assert!(new_account_pda.is_writable); +/// assert!(program::check_id(system_account.key)); +/// +/// let new_account_seed = &instr.new_account_seed; +/// let new_account_bump_seed = instr.new_account_bump_seed; +/// +/// let rent = Rent::get()? +/// .minimum_balance(instr.space.try_into().expect("overflow")); +/// +/// invoke_signed( +/// &instruction::create_account( +/// payer.key, +/// new_account_pda.key, +/// rent, +/// instr.space, +/// &program::ID +/// ), +/// &[payer.clone(), new_account_pda.clone()], +/// &[&[ +/// payer.key.as_ref(), +/// new_account_seed, +/// &[new_account_bump_seed], +/// ]], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +#[cfg(feature = "bincode")] +pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { + let account_metas = vec![AccountMeta::new(*pubkey, true)]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::Assign { owner: *owner }, + account_metas, + ) +} + +#[cfg(feature = "bincode")] +pub fn assign_with_seed( + address: &Pubkey, // must match create_with_seed(base, seed, owner) + base: &Pubkey, + seed: &str, + owner: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*address, false), + AccountMeta::new_readonly(*base, true), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::AssignWithSeed { + base: *base, + seed: seed.to_string(), + owner: *owner, + }, + account_metas, + ) +} + +/// Transfer lamports from an account owned by the system program. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::Transfer`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// # Required signers +/// +/// The `from_pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// These examples allocate space for an account, transfer it the minimum +/// balance for rent exemption, and assign the account to a program. +/// +/// # Example: client-side RPC +/// +/// This example submits the instructions from an RPC client. +/// It assigns the account to a provided program account. +/// The `payer` and `new_account` are signers. +/// +/// ``` +/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn create_account( +/// client: &RpcClient, +/// payer: &Keypair, +/// new_account: &Keypair, +/// owning_program: &Pubkey, +/// space: u64, +/// ) -> Result<()> { +/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; +/// +/// let transfer_instr = instruction::transfer( +/// &payer.pubkey(), +/// &new_account.pubkey(), +/// rent, +/// ); +/// +/// let allocate_instr = instruction::allocate( +/// &new_account.pubkey(), +/// space, +/// ); +/// +/// let assign_instr = instruction::assign( +/// &new_account.pubkey(), +/// owning_program, +/// ); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &[transfer_instr, allocate_instr, assign_instr], +/// Some(&payer.pubkey()), +/// &[payer, new_account], +/// blockhash, +/// ); +/// +/// let _sig = client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # let client = RpcClient::new(String::new()); +/// # let payer = Keypair::new(); +/// # let new_account = Keypair::new(); +/// # let owning_program = Pubkey::new_unique(); +/// # create_account(&client, &payer, &new_account, &owning_program, 1); +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +/// +/// ## Example: on-chain program +/// +/// This example submits the instructions from an on-chain Solana program. The +/// created account is a [program derived address][pda], funded by `payer`, and +/// assigned to the running program. The `payer` and `new_account_pda` are +/// signers, with `new_account_pda` being signed for virtually by the program +/// itself via [`invoke_signed`], `payer` being signed for by the client that +/// submitted the transaction. +/// +/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +/// +/// ``` +/// # use borsh::{BorshDeserialize, BorshSerialize}; +/// use solana_account_info::{next_account_info, AccountInfo}; +/// use solana_cpi::invoke_signed; +/// use solana_program_entrypoint::entrypoint; +/// use solana_program_error::ProgramResult; +/// use solana_pubkey::Pubkey; +/// use solana_system_interface::{instruction, program}; +/// use solana_sysvar::{rent::Rent, Sysvar}; +/// +/// #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// # #[borsh(crate = "borsh")] +/// pub struct CreateAccountInstruction { +/// /// The PDA seed used to distinguish the new account from other PDAs +/// pub new_account_seed: [u8; 16], +/// /// The PDA bump seed +/// pub new_account_bump_seed: u8, +/// /// The amount of space to allocate for `new_account_pda` +/// pub space: u64, +/// } +/// +/// entrypoint!(process_instruction); +/// +/// fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; +/// +/// let account_info_iter = &mut accounts.iter(); +/// +/// let payer = next_account_info(account_info_iter)?; +/// let new_account_pda = next_account_info(account_info_iter)?; +/// let system_account = next_account_info(account_info_iter)?; +/// +/// assert!(payer.is_signer); +/// assert!(payer.is_writable); +/// // Note that `new_account_pda` is not a signer yet. +/// // This program will sign for it via `invoke_signed`. +/// assert!(!new_account_pda.is_signer); +/// assert!(new_account_pda.is_writable); +/// assert!(program::check_id(system_account.key)); +/// +/// let new_account_seed = &instr.new_account_seed; +/// let new_account_bump_seed = instr.new_account_bump_seed; +/// +/// let rent = Rent::get()? +/// .minimum_balance(instr.space.try_into().expect("overflow")); +/// +/// invoke_signed( +/// &instruction::create_account( +/// payer.key, +/// new_account_pda.key, +/// rent, +/// instr.space, +/// &program::ID +/// ), +/// &[payer.clone(), new_account_pda.clone()], +/// &[&[ +/// payer.key.as_ref(), +/// new_account_seed, +/// &[new_account_bump_seed], +/// ]], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +#[cfg(feature = "bincode")] +pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, false), + ]; + Instruction::new_with_bincode(ID, &SystemInstruction::Transfer { lamports }, account_metas) +} + +#[cfg(feature = "bincode")] +pub fn transfer_with_seed( + from_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner) + from_base: &Pubkey, + from_seed: String, + from_owner: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*from_pubkey, false), + AccountMeta::new_readonly(*from_base, true), + AccountMeta::new(*to_pubkey, false), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::TransferWithSeed { + lamports, + from_seed, + from_owner: *from_owner, + }, + account_metas, + ) +} + +/// Allocate space for an account. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::Allocate`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// The transaction will fail if the account already has size greater than 0, +/// or if the requested size is greater than [`super::MAX_PERMITTED_DATA_LENGTH`]. +/// +/// # Required signers +/// +/// The `pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// These examples allocate space for an account, transfer it the minimum +/// balance for rent exemption, and assign the account to a program. +/// +/// # Example: client-side RPC +/// +/// This example submits the instructions from an RPC client. +/// It assigns the account to a provided program account. +/// The `payer` and `new_account` are signers. +/// +/// ``` +/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn create_account( +/// client: &RpcClient, +/// payer: &Keypair, +/// new_account: &Keypair, +/// owning_program: &Pubkey, +/// space: u64, +/// ) -> Result<()> { +/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; +/// +/// let transfer_instr = instruction::transfer( +/// &payer.pubkey(), +/// &new_account.pubkey(), +/// rent, +/// ); +/// +/// let allocate_instr = instruction::allocate( +/// &new_account.pubkey(), +/// space, +/// ); +/// +/// let assign_instr = instruction::assign( +/// &new_account.pubkey(), +/// owning_program, +/// ); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &[transfer_instr, allocate_instr, assign_instr], +/// Some(&payer.pubkey()), +/// &[payer, new_account], +/// blockhash, +/// ); +/// +/// let _sig = client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # let client = RpcClient::new(String::new()); +/// # let payer = Keypair::new(); +/// # let new_account = Keypair::new(); +/// # let owning_program = Pubkey::new_unique(); +/// # create_account(&client, &payer, &new_account, &owning_program, 1); +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +/// +/// ## Example: on-chain program +/// +/// This example submits the instructions from an on-chain Solana program. The +/// created account is a [program derived address][pda], funded by `payer`, and +/// assigned to the running program. The `payer` and `new_account_pda` are +/// signers, with `new_account_pda` being signed for virtually by the program +/// itself via [`invoke_signed`], `payer` being signed for by the client that +/// submitted the transaction. +/// +/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +/// +/// ``` +/// use borsh::{BorshDeserialize, BorshSerialize}; +/// use solana_account_info::{next_account_info, AccountInfo}; +/// use solana_cpi::invoke_signed; +/// use solana_program_entrypoint::entrypoint; +/// use solana_program_error::ProgramResult; +/// use solana_pubkey::Pubkey; +/// use solana_system_interface::{instruction, program}; +/// use solana_sysvar::{rent::Rent, Sysvar}; +/// +/// #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// pub struct CreateAccountInstruction { +/// /// The PDA seed used to distinguish the new account from other PDAs +/// pub new_account_seed: [u8; 16], +/// /// The PDA bump seed +/// pub new_account_bump_seed: u8, +/// /// The amount of space to allocate for `new_account_pda` +/// pub space: u64, +/// } +/// +/// entrypoint!(process_instruction); +/// +/// fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; +/// +/// let account_info_iter = &mut accounts.iter(); +/// +/// let payer = next_account_info(account_info_iter)?; +/// let new_account_pda = next_account_info(account_info_iter)?; +/// let system_account = next_account_info(account_info_iter)?; +/// +/// assert!(payer.is_signer); +/// assert!(payer.is_writable); +/// // Note that `new_account_pda` is not a signer yet. +/// // This program will sign for it via `invoke_signed`. +/// assert!(!new_account_pda.is_signer); +/// assert!(new_account_pda.is_writable); +/// assert!(program::check_id(system_account.key)); +/// +/// let new_account_seed = &instr.new_account_seed; +/// let new_account_bump_seed = instr.new_account_bump_seed; +/// +/// let rent = Rent::get()? +/// .minimum_balance(instr.space.try_into().expect("overflow")); +/// +/// invoke_signed( +/// &instruction::create_account( +/// payer.key, +/// new_account_pda.key, +/// rent, +/// instr.space, +/// &program::ID +/// ), +/// &[payer.clone(), new_account_pda.clone()], +/// &[&[ +/// payer.key.as_ref(), +/// new_account_seed, +/// &[new_account_bump_seed], +/// ]], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +#[cfg(feature = "bincode")] +pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { + let account_metas = vec![AccountMeta::new(*pubkey, true)]; + Instruction::new_with_bincode(ID, &SystemInstruction::Allocate { space }, account_metas) +} + +#[cfg(feature = "bincode")] +pub fn allocate_with_seed( + address: &Pubkey, // must match create_with_seed(base, seed, owner) + base: &Pubkey, + seed: &str, + space: u64, + owner: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*address, false), + AccountMeta::new_readonly(*base, true), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::AllocateWithSeed { + base: *base, + seed: seed.to_string(), + space, + owner: *owner, + }, + account_metas, + ) +} + +/// Transfer lamports from an account owned by the system program to multiple accounts. +/// +/// This function produces a vector of [`Instruction`]s which must be submitted +/// in a [`Transaction`] or [invoked] to take effect, containing serialized +/// [`SystemInstruction::Transfer`]s. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// # Required signers +/// +/// The `from_pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// ## Example: client-side RPC +/// +/// This example performs multiple transfers in a single transaction. +/// +/// ``` +/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn transfer_lamports_to_many( +/// client: &RpcClient, +/// from: &Keypair, +/// to_and_amount: &[(Pubkey, u64)], +/// ) -> Result<()> { +/// let instrs = instruction::transfer_many(&from.pubkey(), to_and_amount); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &instrs, +/// Some(&from.pubkey()), +/// &[from], +/// blockhash, +/// ); +/// +/// let _sig = client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # let from = Keypair::new(); +/// # let to_and_amount = vec![ +/// # (Pubkey::new_unique(), 1_000), +/// # (Pubkey::new_unique(), 2_000), +/// # (Pubkey::new_unique(), 3_000), +/// # ]; +/// # let client = RpcClient::new(String::new()); +/// # transfer_lamports_to_many(&client, &from, &to_and_amount); +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +/// +/// ## Example: on-chain program +/// +/// This example makes multiple transfers out of a "bank" account, +/// a [program derived address][pda] owned by the calling program. +/// This example submits the instructions from an on-chain Solana program. The +/// created account is a [program derived address][pda], and it is assigned to +/// the running program. The `payer` and `new_account_pda` are signers, with +/// `new_account_pda` being signed for virtually by the program itself via +/// [`invoke_signed`], `payer` being signed for by the client that submitted the +/// transaction. +/// +/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html +/// +/// ``` +/// # use borsh::{BorshDeserialize, BorshSerialize}; +/// use solana_account_info::{next_account_info, next_account_infos, AccountInfo}; +/// use solana_cpi::invoke_signed; +/// use solana_program_entrypoint::entrypoint; +/// use solana_program_error::ProgramResult; +/// use solana_pubkey::Pubkey; +/// use solana_system_interface::{instruction, program}; +/// +/// /// # Accounts +/// /// +/// /// - 0: bank_pda - writable +/// /// - 1: system_program - executable +/// /// - *: to - writable +/// # #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// # #[borsh(crate = "borsh")] +/// pub struct TransferLamportsToManyInstruction { +/// pub bank_pda_bump_seed: u8, +/// pub amount_list: Vec, +/// } +/// +/// entrypoint!(process_instruction); +/// +/// fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// let instr = TransferLamportsToManyInstruction::deserialize(&mut &instruction_data[..])?; +/// +/// let account_info_iter = &mut accounts.iter(); +/// +/// let bank_pda = next_account_info(account_info_iter)?; +/// let bank_pda_bump_seed = instr.bank_pda_bump_seed; +/// let system_account = next_account_info(account_info_iter)?; +/// +/// assert!(program::check_id(system_account.key)); +/// +/// let to_accounts = next_account_infos(account_info_iter, account_info_iter.len())?; +/// +/// for to_account in to_accounts { +/// assert!(to_account.is_writable); +/// // ... do other verification ... +/// } +/// +/// let to_and_amount = to_accounts +/// .iter() +/// .zip(instr.amount_list.iter()) +/// .map(|(to, amount)| (*to.key, *amount)) +/// .collect::>(); +/// +/// let instrs = instruction::transfer_many(bank_pda.key, to_and_amount.as_ref()); +/// +/// for instr in instrs { +/// invoke_signed(&instr, accounts, &[&[b"bank", &[bank_pda_bump_seed]]])?; +/// } +/// +/// Ok(()) +/// } +/// ``` +#[cfg(feature = "bincode")] +pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec { + to_lamports + .iter() + .map(|(to_pubkey, lamports)| transfer(from_pubkey, to_pubkey, *lamports)) + .collect() +} + +#[cfg(feature = "bincode")] +pub fn create_nonce_account_with_seed( + from_pubkey: &Pubkey, + nonce_pubkey: &Pubkey, + base: &Pubkey, + seed: &str, + authority: &Pubkey, + lamports: u64, +) -> Vec { + vec![ + create_account_with_seed( + from_pubkey, + nonce_pubkey, + base, + seed, + lamports, + NONCE_STATE_SIZE as u64, + &ID, + ), + Instruction::new_with_bincode( + ID, + &SystemInstruction::InitializeNonceAccount(*authority), + vec![ + AccountMeta::new(*nonce_pubkey, false), + #[allow(deprecated)] + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), + ], + ), + ] +} + +/// Create an account containing a durable transaction nonce. +/// +/// This function produces a vector of [`Instruction`]s which must be submitted +/// in a [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::CreateAccount`] and +/// [`SystemInstruction::InitializeNonceAccount`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// A [durable transaction nonce][dtn] is a special account that enables +/// execution of transactions that have been signed in the past. +/// +/// Standard Solana transactions include a [recent blockhash][rbh] (sometimes +/// referred to as a _[nonce]_). During execution the Solana runtime verifies +/// the recent blockhash is approximately less than two minutes old, and that in +/// those two minutes no other identical transaction with the same blockhash has +/// been executed. These checks prevent accidental replay of transactions. +/// Consequently, it is not possible to sign a transaction, wait more than two +/// minutes, then successfully execute that transaction. +/// +/// [dtn]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces +/// [rbh]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash +/// [nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce +/// +/// Durable transaction nonces are an alternative to the standard recent +/// blockhash nonce. They are stored in accounts on chain, and every time they +/// are used their value is changed to a new value for their next use. The +/// runtime verifies that each durable nonce value is only used once, and there +/// are no restrictions on how "old" the nonce is. Because they are stored on +/// chain and require additional instructions to use, transacting with durable +/// transaction nonces is more expensive than with standard transactions. +/// +/// The value of the durable nonce is itself a blockhash and is accessible via +/// the [`blockhash`] field of [`nonce::state::Data`], which is deserialized +/// from the nonce account data. +/// +/// [`blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash +/// [`nonce::state::Data`]: https://docs.rs/solana-nonce/latest/solana_nonce/state/struct.Data.html +/// +/// The basic durable transaction nonce lifecycle is +/// +/// 1) Create the nonce account with the `create_nonce_account` instruction. +/// 2) Submit specially-formed transactions that include the +/// [`advance_nonce_account`] instruction. +/// 3) Destroy the nonce account by withdrawing its lamports with the +/// [`withdraw_nonce_account`] instruction. +/// +/// Nonce accounts have an associated _authority_ account, which is stored in +/// their account data, and can be changed with the [`authorize_nonce_account`] +/// instruction. The authority must sign transactions that include the +/// `advance_nonce_account`, `authorize_nonce_account` and +/// `withdraw_nonce_account` instructions. +/// +/// Nonce accounts are owned by the system program. +/// +/// This constructor creates a [`SystemInstruction::CreateAccount`] instruction +/// and a [`SystemInstruction::InitializeNonceAccount`] instruction. +/// +/// # Required signers +/// +/// The `from_pubkey` and `nonce_pubkey` signers must sign the transaction. +/// +/// # Examples +/// +/// Create a nonce account from an off-chain client: +/// +/// ``` +/// # use solana_example_mocks::solana_keypair; +/// # use solana_example_mocks::solana_signer; +/// # use solana_example_mocks::solana_rpc_client; +/// # use solana_example_mocks::solana_transaction; +/// use solana_keypair::Keypair; +/// use solana_nonce::state::State; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_signer::Signer; +/// use solana_system_interface::instruction; +/// use solana_transaction::Transaction; +/// use anyhow::Result; +/// +/// fn submit_create_nonce_account_tx( +/// client: &RpcClient, +/// payer: &Keypair, +/// ) -> Result<()> { +/// +/// let nonce_account = Keypair::new(); +/// +/// let nonce_rent = client.get_minimum_balance_for_rent_exemption(State::size())?; +/// let instr = instruction::create_nonce_account( +/// &payer.pubkey(), +/// &nonce_account.pubkey(), +/// &payer.pubkey(), // Make the fee payer the nonce account authority +/// nonce_rent, +/// ); +/// +/// let mut tx = Transaction::new_with_payer(&instr, Some(&payer.pubkey())); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// tx.try_sign(&[&nonce_account, payer], blockhash)?; +/// +/// client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let payer = Keypair::new(); +/// # submit_create_nonce_account_tx(&client, &payer)?; +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +#[cfg(feature = "bincode")] +pub fn create_nonce_account( + from_pubkey: &Pubkey, + nonce_pubkey: &Pubkey, + authority: &Pubkey, + lamports: u64, +) -> Vec { + vec![ + create_account( + from_pubkey, + nonce_pubkey, + lamports, + NONCE_STATE_SIZE as u64, + &ID, + ), + Instruction::new_with_bincode( + ID, + &SystemInstruction::InitializeNonceAccount(*authority), + vec![ + AccountMeta::new(*nonce_pubkey, false), + #[allow(deprecated)] + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), + ], + ), + ] +} + +/// Advance the value of a durable transaction nonce. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::AdvanceNonceAccount`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// Every transaction that relies on a durable transaction nonce must contain a +/// [`SystemInstruction::AdvanceNonceAccount`] instruction as the first +/// instruction in the [`Message`], as created by this function. When included +/// in the first position, the Solana runtime recognizes the transaction as one +/// that relies on a durable transaction nonce and processes it accordingly. The +/// [`Message::new_with_nonce`] function can be used to construct a `Message` in +/// the correct format without calling `advance_nonce_account` directly. +/// +/// When constructing a transaction that includes an `AdvanceNonceInstruction` +/// the [`recent_blockhash`] must be treated differently — instead of +/// setting it to a recent blockhash, the value of the nonce must be retrieved +/// and deserialized from the nonce account, and that value specified as the +/// "recent blockhash". A nonce account can be deserialized with the +/// [`solana_rpc_client_nonce_utils::data_from_account`][dfa] function. +/// +/// For further description of durable transaction nonces see +/// [`create_nonce_account`]. +/// +/// [`Message`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html +/// [`Message::new_with_nonce`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#method.new_with_nonce +/// [`recent_blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash +/// [dfa]: https://docs.rs/solana-rpc-client-nonce-utils/latest/solana_rpc_client_nonce_utils/fn.data_from_account.html +/// +/// # Required signers +/// +/// The `authorized_pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// Create and sign a transaction with a durable nonce: +/// +/// ``` +/// # use solana_example_mocks::solana_sdk; +/// # use solana_example_mocks::solana_rpc_client; +/// # use solana_example_mocks::solana_rpc_client_nonce_utils; +/// # use solana_sdk::account::Account; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// message::Message, +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use std::path::Path; +/// use anyhow::Result; +/// +/// fn create_transfer_tx_with_nonce( +/// client: &RpcClient, +/// nonce_account_pubkey: &Pubkey, +/// payer: &Keypair, +/// receiver: &Pubkey, +/// amount: u64, +/// tx_path: &Path, +/// ) -> Result<()> { +/// +/// let instr_transfer = instruction::transfer( +/// &payer.pubkey(), +/// receiver, +/// amount, +/// ); +/// +/// // In this example, `payer` is `nonce_account_pubkey`'s authority +/// let instr_advance_nonce_account = instruction::advance_nonce_account( +/// nonce_account_pubkey, +/// &payer.pubkey(), +/// ); +/// +/// // The `advance_nonce_account` instruction must be the first issued in +/// // the transaction. +/// let message = Message::new( +/// &[ +/// instr_advance_nonce_account, +/// instr_transfer +/// ], +/// Some(&payer.pubkey()), +/// ); +/// +/// let mut tx = Transaction::new_unsigned(message); +/// +/// // Sign the tx with nonce_account's `blockhash` instead of the +/// // network's latest blockhash. +/// # client.set_get_account_response(*nonce_account_pubkey, Account { +/// # lamports: 1, +/// # data: vec![0], +/// # owner: solana_sdk::system_program::ID, +/// # executable: false, +/// # }); +/// let nonce_account = client.get_account(nonce_account_pubkey)?; +/// let nonce_data = solana_rpc_client_nonce_utils::data_from_account(&nonce_account)?; +/// let blockhash = nonce_data.blockhash(); +/// +/// tx.try_sign(&[payer], blockhash)?; +/// +/// // Save the signed transaction locally for later submission. +/// save_tx_to_file(&tx_path, &tx)?; +/// +/// Ok(()) +/// } +/// # +/// # fn save_tx_to_file(path: &Path, tx: &Transaction) -> Result<()> { +/// # Ok(()) +/// # } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let nonce_account_pubkey = Pubkey::new_unique(); +/// # let payer = Keypair::new(); +/// # let receiver = Pubkey::new_unique(); +/// # create_transfer_tx_with_nonce(&client, &nonce_account_pubkey, &payer, &receiver, 1024, Path::new("new_tx"))?; +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +#[cfg(feature = "bincode")] +pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*nonce_pubkey, false), + #[allow(deprecated)] + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(*authorized_pubkey, true), + ]; + Instruction::new_with_bincode(ID, &SystemInstruction::AdvanceNonceAccount, account_metas) +} + +/// Withdraw lamports from a durable transaction nonce account. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::WithdrawNonceAccount`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// Withdrawing the entire balance of a nonce account will cause the runtime to +/// destroy it upon successful completion of the transaction. +/// +/// Otherwise, nonce accounts must maintain a balance greater than or equal to +/// the minimum required for [rent exemption]. If the result of this instruction +/// would leave the nonce account with a balance less than required for rent +/// exemption, but also greater than zero, then the transaction will fail. +/// +/// [rent exemption]: https://solana.com/docs/core/accounts#rent-exemption +/// +/// This constructor creates a [`SystemInstruction::WithdrawNonceAccount`] +/// instruction. +/// +/// # Required signers +/// +/// The `authorized_pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// ``` +/// # use solana_example_mocks::solana_sdk; +/// # use solana_example_mocks::solana_rpc_client; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn submit_withdraw_nonce_account_tx( +/// client: &RpcClient, +/// nonce_account_pubkey: &Pubkey, +/// authorized_account: &Keypair, +/// ) -> Result<()> { +/// +/// let nonce_balance = client.get_balance(nonce_account_pubkey)?; +/// +/// let instr = instruction::withdraw_nonce_account( +/// nonce_account_pubkey, +/// &authorized_account.pubkey(), +/// &authorized_account.pubkey(), +/// nonce_balance, +/// ); +/// +/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey())); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// tx.try_sign(&[authorized_account], blockhash)?; +/// +/// client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let nonce_account_pubkey = Pubkey::new_unique(); +/// # let payer = Keypair::new(); +/// # submit_withdraw_nonce_account_tx(&client, &nonce_account_pubkey, &payer)?; +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +#[cfg(feature = "bincode")] +pub fn withdraw_nonce_account( + nonce_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*nonce_pubkey, false), + AccountMeta::new(*to_pubkey, false), + #[allow(deprecated)] + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), + AccountMeta::new_readonly(*authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::WithdrawNonceAccount(lamports), + account_metas, + ) +} + +/// Change the authority of a durable transaction nonce account. +/// +/// This function produces an [`Instruction`] which must be submitted in a +/// [`Transaction`] or [invoked] to take effect, containing a serialized +/// [`SystemInstruction::AuthorizeNonceAccount`]. +/// +/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html +/// +/// This constructor creates a [`SystemInstruction::AuthorizeNonceAccount`] +/// instruction. +/// +/// # Required signers +/// +/// The `authorized_pubkey` signer must sign the transaction. +/// +/// # Examples +/// +/// ``` +/// # use solana_example_mocks::solana_sdk; +/// # use solana_example_mocks::solana_rpc_client; +/// use solana_rpc_client::rpc_client::RpcClient; +/// use solana_pubkey::Pubkey; +/// use solana_sdk::{ +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// use solana_system_interface::instruction; +/// use anyhow::Result; +/// +/// fn authorize_nonce_account_tx( +/// client: &RpcClient, +/// nonce_account_pubkey: &Pubkey, +/// authorized_account: &Keypair, +/// new_authority_pubkey: &Pubkey, +/// ) -> Result<()> { +/// +/// let instr = instruction::authorize_nonce_account( +/// nonce_account_pubkey, +/// &authorized_account.pubkey(), +/// new_authority_pubkey, +/// ); +/// +/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey())); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// tx.try_sign(&[authorized_account], blockhash)?; +/// +/// client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let nonce_account_pubkey = Pubkey::new_unique(); +/// # let payer = Keypair::new(); +/// # let new_authority_pubkey = Pubkey::new_unique(); +/// # authorize_nonce_account_tx(&client, &nonce_account_pubkey, &payer, &new_authority_pubkey)?; +/// # +/// # Ok::<(), anyhow::Error>(()) +/// ``` +#[cfg(feature = "bincode")] +pub fn authorize_nonce_account( + nonce_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + new_authority: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*nonce_pubkey, false), + AccountMeta::new_readonly(*authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + ID, + &SystemInstruction::AuthorizeNonceAccount(*new_authority), + account_metas, + ) +} + +/// One-time idempotent upgrade of legacy nonce versions in order to bump +/// them out of chain blockhash domain. +#[cfg(feature = "bincode")] +pub fn upgrade_nonce_account(nonce_pubkey: Pubkey) -> Instruction { + let account_metas = vec![AccountMeta::new(nonce_pubkey, /*is_signer:*/ false)]; + Instruction::new_with_bincode(ID, &SystemInstruction::UpgradeNonceAccount, account_metas) +} + +#[cfg(feature = "bincode")] +#[cfg(test)] +mod tests { + use {super::*, solana_sysvar_id::SysvarId}; + + fn get_keys(instruction: &Instruction) -> Vec { + instruction.accounts.iter().map(|x| x.pubkey).collect() + } + + #[allow(deprecated)] + #[test] + fn test_constants() { + // Ensure that the constants are in sync with the solana program. + assert_eq!( + RECENT_BLOCKHASHES_ID, + solana_sysvar::recent_blockhashes::RecentBlockhashes::id(), + ); + + // Ensure that the constants are in sync with the solana rent. + assert_eq!(RENT_ID, solana_sysvar::rent::Rent::id()); + } + + #[test] + fn test_move_many() { + let alice_pubkey = Pubkey::new_unique(); + let bob_pubkey = Pubkey::new_unique(); + let carol_pubkey = Pubkey::new_unique(); + let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)]; + + let instructions = transfer_many(&alice_pubkey, &to_lamports); + assert_eq!(instructions.len(), 2); + assert_eq!(get_keys(&instructions[0]), vec![alice_pubkey, bob_pubkey]); + assert_eq!(get_keys(&instructions[1]), vec![alice_pubkey, carol_pubkey]); + } + + #[test] + fn test_create_nonce_account() { + let from_pubkey = Pubkey::new_unique(); + let nonce_pubkey = Pubkey::new_unique(); + let authorized = nonce_pubkey; + let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42); + assert_eq!(ixs.len(), 2); + let ix = &ixs[0]; + assert_eq!(ix.program_id, crate::program::ID); + let pubkeys: Vec<_> = ix.accounts.iter().map(|am| am.pubkey).collect(); + assert!(pubkeys.contains(&from_pubkey)); + assert!(pubkeys.contains(&nonce_pubkey)); + } +} diff --git a/system-interface/src/lib.rs b/system-interface/src/lib.rs new file mode 100644 index 000000000..e0514cd55 --- /dev/null +++ b/system-interface/src/lib.rs @@ -0,0 +1,29 @@ +//! The System program interface. + +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +pub mod error; +pub mod instruction; + +#[cfg(test)] +static_assertions::const_assert!(MAX_PERMITTED_DATA_LENGTH <= u32::MAX as u64); +/// Maximum permitted size of account data (10 MiB). +/// +// SBF program entrypoint assumes that the max account data length +// will fit inside a u32. If this constant no longer fits in a u32, +// the entrypoint deserialization code in the SDK must be updated. +pub const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024; + +#[cfg(test)] +static_assertions::const_assert_eq!(MAX_PERMITTED_DATA_LENGTH, 10_485_760); +/// Maximum permitted size of new allocations per transaction, in bytes. +/// +/// The value was chosen such that at least one max sized account could be created, +/// plus some additional resize allocations. +pub const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION: i64 = + MAX_PERMITTED_DATA_LENGTH as i64 * 2; + +pub mod program { + solana_pubkey::declare_id!("11111111111111111111111111111111"); +} diff --git a/sysvar-id/Cargo.toml b/sysvar-id/Cargo.toml index a86f5a349..e66f77c8b 100644 --- a/sysvar-id/Cargo.toml +++ b/sysvar-id/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-sysvar-id" description = "Definition for the sysvar id trait and associated macros." documentation = "https://docs.rs/solana-sysvar-id" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/sysvar/Cargo.toml b/sysvar/Cargo.toml index 4eababe33..704c6175f 100644 --- a/sysvar/Cargo.toml +++ b/sysvar/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-sysvar" description = "Solana sysvar account types" documentation = "https://docs.rs/solana-sysvar" -version = "2.2.2" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } @@ -15,14 +15,10 @@ all-features = true rustdoc-args = ["--cfg=docsrs"] [features] -bincode = ["dep:bincode", "serde", "solana-stake-interface/bincode"] +bincode = ["dep:bincode", "serde"] bytemuck = ["dep:bytemuck", "dep:bytemuck_derive"] dev-context-only-utils = ["bincode", "bytemuck"] -frozen-abi = [ - "dep:solana-frozen-abi", - "dep:solana-frozen-abi-macro", - "solana-stake-interface/frozen-abi", -] +frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] serde = [ "dep:serde", "dep:serde_derive", @@ -34,7 +30,6 @@ serde = [ "solana-rent/serde", "solana-slot-hashes/serde", "solana-slot-history/serde", - "solana-stake-interface/serde", ] [dependencies] @@ -52,7 +47,6 @@ solana-fee-calculator = { workspace = true } solana-frozen-abi = { workspace = true, optional = true } solana-frozen-abi-macro = { workspace = true, optional = true } solana-hash = { workspace = true, features = ["bytemuck"] } -solana-instruction = { workspace = true } solana-last-restart-slot = { workspace = true, features = ["sysvar"] } solana-program-entrypoint = { workspace = true } solana-program-error = { workspace = true } @@ -62,11 +56,11 @@ solana-sdk-ids = { workspace = true } solana-sdk-macro = { workspace = true } solana-slot-hashes = { workspace = true, features = ["sysvar"] } solana-slot-history = { workspace = true, features = ["sysvar"] } -solana-stake-interface = { workspace = true } solana-sysvar-id = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] base64 = { workspace = true } +solana-instruction = { workspace = true, features = ["std"] } solana-program-memory = { workspace = true } [target.'cfg(target_os = "solana")'.dependencies] diff --git a/sysvar/src/lib.rs b/sysvar/src/lib.rs index ab33395e3..d91e744e3 100644 --- a/sysvar/src/lib.rs +++ b/sysvar/src/lib.rs @@ -98,7 +98,6 @@ pub mod rent; pub mod rewards; pub mod slot_hashes; pub mod slot_history; -pub mod stake_history; /// Return value indicating that the `offset + length` is greater than the length of /// the sysvar data. @@ -205,7 +204,7 @@ macro_rules! impl_sysvar_get { /// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar` /// syscall. -pub(crate) fn get_sysvar( +pub fn get_sysvar( dst: &mut [u8], sysvar_id: &Pubkey, offset: u64, diff --git a/sysvar/src/stake_history.rs b/sysvar/src/stake_history.rs deleted file mode 100644 index b7c9c97c8..000000000 --- a/sysvar/src/stake_history.rs +++ /dev/null @@ -1,260 +0,0 @@ -//! History of stake activations and de-activations. -//! -//! The _stake history sysvar_ provides access to the [`StakeHistory`] type. -//! -//! The [`Sysvar::get`] method always returns -//! [`ProgramError::UnsupportedSysvar`], and in practice the data size of this -//! sysvar is too large to process on chain. One can still use the -//! [`SysvarId::id`], [`SysvarId::check_id`] and [`SysvarSerialize::size_of`] methods in -//! an on-chain program, and it can be accessed off-chain through RPC. -//! -//! [`ProgramError::UnsupportedSysvar`]: https://docs.rs/solana-program-error/latest/solana_program_error/enum.ProgramError.html#variant.UnsupportedSysvar -//! [`SysvarId::id`]: https://docs.rs/solana-sysvar-id/latest/solana_sysvar_id/trait.SysvarId.html -//! [`SysvarId::check_id`]: https://docs.rs/solana-sysvar-id/latest/solana_sysvar_id/trait.SysvarId.html#tymethod.check_id -//! -//! # Examples -//! -//! Calling via the RPC client: -//! -//! ``` -//! # use solana_example_mocks::{solana_account, solana_rpc_client}; -//! # use solana_stake_interface::stake_history::StakeHistory; -//! # use solana_account::Account; -//! # use solana_rpc_client::rpc_client::RpcClient; -//! # use solana_sdk_ids::sysvar::stake_history; -//! # use anyhow::Result; -//! # -//! fn print_sysvar_stake_history(client: &RpcClient) -> Result<()> { -//! # client.set_get_account_response(stake_history::ID, Account { -//! # lamports: 114979200, -//! # data: vec![0, 0, 0, 0, 0, 0, 0, 0], -//! # owner: solana_sdk_ids::system_program::ID, -//! # executable: false, -//! # }); -//! # -//! let stake_history = client.get_account(&stake_history::ID)?; -//! let data: StakeHistory = bincode::deserialize(&stake_history.data)?; -//! -//! Ok(()) -//! } -//! # -//! # let client = RpcClient::new(String::new()); -//! # print_sysvar_stake_history(&client)?; -//! # -//! # Ok::<(), anyhow::Error>(()) -//! ``` - -#[cfg(feature = "bincode")] -use crate::SysvarSerialize; -pub use solana_sdk_ids::sysvar::stake_history::{check_id, id, ID}; -#[deprecated( - since = "2.2.0", - note = "Use solana_stake_interface::stake_history instead" -)] -pub use solana_stake_interface::stake_history::{ - StakeHistory, StakeHistoryEntry, StakeHistoryGetEntry, MAX_ENTRIES, -}; -use { - crate::{get_sysvar, Sysvar}, - solana_clock::Epoch, -}; - -impl Sysvar for StakeHistory {} -#[cfg(feature = "bincode")] -impl SysvarSerialize for StakeHistory { - // override - fn size_of() -> usize { - // hard-coded so that we don't have to construct an empty - 16392 // golden, update if MAX_ENTRIES changes - } -} - -// we do not provide Default because this requires the real current epoch -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct StakeHistorySysvar(pub Epoch); - -// precompute so we can statically allocate buffer -const EPOCH_AND_ENTRY_SERIALIZED_SIZE: u64 = 32; - -impl StakeHistoryGetEntry for StakeHistorySysvar { - fn get_entry(&self, target_epoch: Epoch) -> Option { - let current_epoch = self.0; - - // if current epoch is zero this returns None because there is no history yet - let newest_historical_epoch = current_epoch.checked_sub(1)?; - let oldest_historical_epoch = current_epoch.saturating_sub(MAX_ENTRIES as u64); - - // target epoch is old enough to have fallen off history; presume fully active/deactive - if target_epoch < oldest_historical_epoch { - return None; - } - - // epoch delta is how many epoch-entries we offset in the stake history vector, which may be zero - // None means target epoch is current or in the future; this is a user error - let epoch_delta = newest_historical_epoch.checked_sub(target_epoch)?; - - // offset is the number of bytes to our desired entry, including eight for vector length - let offset = epoch_delta - .checked_mul(EPOCH_AND_ENTRY_SERIALIZED_SIZE)? - .checked_add(std::mem::size_of::() as u64)?; - - let mut entry_buf = [0; EPOCH_AND_ENTRY_SERIALIZED_SIZE as usize]; - let result = get_sysvar( - &mut entry_buf, - &id(), - offset, - EPOCH_AND_ENTRY_SERIALIZED_SIZE, - ); - - match result { - Ok(()) => { - // All safe because `entry_buf` is a 32-length array - let entry_epoch = u64::from_le_bytes(entry_buf[0..8].try_into().unwrap()); - let effective = u64::from_le_bytes(entry_buf[8..16].try_into().unwrap()); - let activating = u64::from_le_bytes(entry_buf[16..24].try_into().unwrap()); - let deactivating = u64::from_le_bytes(entry_buf[24..32].try_into().unwrap()); - - // this would only fail if stake history skipped an epoch or the binary format of the sysvar changed - assert_eq!(entry_epoch, target_epoch); - - Some(StakeHistoryEntry { - effective, - activating, - deactivating, - }) - } - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use {super::*, crate::tests::mock_get_sysvar_syscall, serial_test::serial}; - - #[test] - fn test_size_of() { - let mut stake_history = StakeHistory::default(); - for i in 0..MAX_ENTRIES as u64 { - stake_history.add( - i, - StakeHistoryEntry { - activating: i, - ..StakeHistoryEntry::default() - }, - ); - } - - assert_eq!( - bincode::serialized_size(&stake_history).unwrap() as usize, - StakeHistory::size_of() - ); - - let stake_history_inner: Vec<(Epoch, StakeHistoryEntry)> = - bincode::deserialize(&bincode::serialize(&stake_history).unwrap()).unwrap(); - let epoch_entry = stake_history_inner.into_iter().next().unwrap(); - - assert_eq!( - bincode::serialized_size(&epoch_entry).unwrap(), - EPOCH_AND_ENTRY_SERIALIZED_SIZE - ); - } - - #[serial] - #[test] - fn test_stake_history_get_entry() { - let unique_entry_for_epoch = |epoch: u64| StakeHistoryEntry { - activating: epoch.saturating_mul(2), - deactivating: epoch.saturating_mul(3), - effective: epoch.saturating_mul(5), - }; - - let current_epoch = MAX_ENTRIES.saturating_add(2) as u64; - - // make a stake history object with at least one valid entry that has expired - let mut stake_history = StakeHistory::default(); - for i in 0..current_epoch { - stake_history.add(i, unique_entry_for_epoch(i)); - } - assert_eq!(stake_history.len(), MAX_ENTRIES); - assert_eq!(stake_history.iter().map(|entry| entry.0).min().unwrap(), 2); - - // set up sol_get_sysvar - mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap()); - - // make a syscall interface object - let stake_history_sysvar = StakeHistorySysvar(current_epoch); - - // now test the stake history interfaces - - assert_eq!(stake_history.get(0), None); - assert_eq!(stake_history.get(1), None); - assert_eq!(stake_history.get(current_epoch), None); - - assert_eq!(stake_history.get_entry(0), None); - assert_eq!(stake_history.get_entry(1), None); - assert_eq!(stake_history.get_entry(current_epoch), None); - - assert_eq!(stake_history_sysvar.get_entry(0), None); - assert_eq!(stake_history_sysvar.get_entry(1), None); - assert_eq!(stake_history_sysvar.get_entry(current_epoch), None); - - for i in 2..current_epoch { - let entry = Some(unique_entry_for_epoch(i)); - - assert_eq!(stake_history.get(i), entry.as_ref(),); - - assert_eq!(stake_history.get_entry(i), entry,); - - assert_eq!(stake_history_sysvar.get_entry(i), entry,); - } - } - - #[serial] - #[test] - fn test_stake_history_get_entry_zero() { - let mut current_epoch = 0; - - // first test that an empty history returns None - let stake_history = StakeHistory::default(); - assert_eq!(stake_history.len(), 0); - - mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap()); - let stake_history_sysvar = StakeHistorySysvar(current_epoch); - - assert_eq!(stake_history.get(0), None); - assert_eq!(stake_history.get_entry(0), None); - assert_eq!(stake_history_sysvar.get_entry(0), None); - - // next test that we can get a zeroth entry in the first epoch - let entry_zero = StakeHistoryEntry { - effective: 100, - ..StakeHistoryEntry::default() - }; - let entry = Some(entry_zero.clone()); - - let mut stake_history = StakeHistory::default(); - stake_history.add(current_epoch, entry_zero); - assert_eq!(stake_history.len(), 1); - current_epoch = current_epoch.saturating_add(1); - - mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap()); - let stake_history_sysvar = StakeHistorySysvar(current_epoch); - - assert_eq!(stake_history.get(0), entry.as_ref()); - assert_eq!(stake_history.get_entry(0), entry); - assert_eq!(stake_history_sysvar.get_entry(0), entry); - - // finally test that we can still get a zeroth entry in later epochs - stake_history.add(current_epoch, StakeHistoryEntry::default()); - assert_eq!(stake_history.len(), 2); - current_epoch = current_epoch.saturating_add(1); - - mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap()); - let stake_history_sysvar = StakeHistorySysvar(current_epoch); - - assert_eq!(stake_history.get(0), entry.as_ref()); - assert_eq!(stake_history.get_entry(0), entry); - assert_eq!(stake_history_sysvar.get_entry(0), entry); - } -} diff --git a/time-utils/Cargo.toml b/time-utils/Cargo.toml index 17e2ceacf..2dccb71c8 100644 --- a/time-utils/Cargo.toml +++ b/time-utils/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-time-utils" description = "`std::time` utilities for Solana" documentation = "https://docs.rs/solana-time-utils" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/transaction-error/Cargo.toml b/transaction-error/Cargo.toml index a6e7d5db9..3e1a82f02 100644 --- a/transaction-error/Cargo.toml +++ b/transaction-error/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-transaction-error" description = "Solana TransactionError type" documentation = "https://docs.rs/solana-transaction-error" -version = "2.2.1" +version = "3.0.0" rust-version = "1.81.0" authors = { workspace = true } repository = { workspace = true } diff --git a/validator-exit/Cargo.toml b/validator-exit/Cargo.toml index c1af5810c..01307037c 100644 --- a/validator-exit/Cargo.toml +++ b/validator-exit/Cargo.toml @@ -2,7 +2,7 @@ name = "solana-validator-exit" description = "Solana validator exit handling." documentation = "https://docs.rs/solana-validator-exit" -version = "2.2.1" +version = "3.0.0" authors = { workspace = true } repository = { workspace = true } homepage = { workspace = true } diff --git a/vote-interface/Cargo.toml b/vote-interface/Cargo.toml index fba9deaf1..f1fdb16cc 100644 --- a/vote-interface/Cargo.toml +++ b/vote-interface/Cargo.toml @@ -36,8 +36,10 @@ frozen-abi = [ "solana-short-vec/frozen-abi", ] serde = [ + "dep:cfg_eval", "dep:serde", "dep:serde_derive", + "dep:serde_with", "dep:solana-serde-varint", "dep:solana-short-vec", "solana-hash/serde", @@ -47,10 +49,12 @@ serde = [ [dependencies] arbitrary = { workspace = true, features = ["derive"], optional = true } bincode = { workspace = true, optional = true } +cfg_eval = { workspace = true, optional = true } num-derive = { workspace = true } num-traits = { workspace = true } serde = { workspace = true, optional = true } serde_derive = { workspace = true, optional = true } +serde_with = { workspace = true, features = ["macros"], optional = true } solana-clock = { workspace = true } solana-frozen-abi = { workspace = true, features = [ "frozen-abi", diff --git a/vote-interface/src/state/mod.rs b/vote-interface/src/state/mod.rs index 85c028923..46795eff4 100644 --- a/vote-interface/src/state/mod.rs +++ b/vote-interface/src/state/mod.rs @@ -23,8 +23,15 @@ pub mod vote_state_versions; pub use vote_state_versions::*; pub mod vote_state_v3; pub use vote_state_v3::VoteStateV3; +pub mod vote_state_v4; +pub use vote_state_v4::VoteStateV4; mod vote_instruction_data; pub use vote_instruction_data::*; +#[cfg(any(target_os = "solana", feature = "bincode"))] +pub(crate) mod vote_state_deserialize; + +/// Size of a BLS public key in a compressed point representation +pub const BLS_PUBLIC_KEY_COMPRESSED_SIZE: usize = 48; // Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH pub const MAX_LOCKOUT_HISTORY: usize = 31; diff --git a/vote-interface/src/state/vote_state_deserialize.rs b/vote-interface/src/state/vote_state_deserialize.rs new file mode 100644 index 000000000..c90afa40c --- /dev/null +++ b/vote-interface/src/state/vote_state_deserialize.rs @@ -0,0 +1,205 @@ +use { + crate::{ + authorized_voters::AuthorizedVoters, + state::{ + BlockTimestamp, LandedVote, Lockout, VoteStateV3, MAX_EPOCH_CREDITS_HISTORY, MAX_ITEMS, + MAX_LOCKOUT_HISTORY, + }, + }, + solana_clock::Epoch, + solana_instruction_error::InstructionError, + solana_pubkey::Pubkey, + solana_serialize_utils::cursor::{ + read_bool, read_i64, read_option_u64, read_pubkey, read_pubkey_into, read_u32, read_u64, + read_u8, + }, + std::{collections::VecDeque, io::Cursor, ptr::addr_of_mut}, +}; + +// This is to reset vote_state to T::default() if deserialize fails or panics. +struct DropGuard { + vote_state: *mut T, +} + +impl Drop for DropGuard { + fn drop(&mut self) { + // Safety: + // + // Deserialize failed or panicked so at this point vote_state is uninitialized. We + // must write a new _valid_ value into it or after returning (or unwinding) from + // this function the caller is left with an uninitialized `&mut T`, which is UB + // (references must always be valid). + // + // This is always safe and doesn't leak memory because deserialize_into_ptr() writes + // into the fields that heap alloc only when it returns Ok(). + unsafe { + self.vote_state.write(T::default()); + } + } +} + +pub(crate) fn deserialize_into( + input: &[u8], + vote_state: &mut T, + deserialize_fn: impl FnOnce(&[u8], *mut T) -> Result<(), InstructionError>, +) -> Result<(), InstructionError> { + // Rebind vote_state to *mut T so that the &mut binding isn't accessible + // anymore, preventing accidental use after this point. + // + // NOTE: switch to ptr::from_mut() once platform-tools moves to rustc >= 1.76 + let vote_state = vote_state as *mut T; + + // Safety: vote_state is valid to_drop (see drop_in_place() docs). After + // dropping, the pointer is treated as uninitialized and only accessed + // through ptr::write, which is safe as per drop_in_place docs. + unsafe { + std::ptr::drop_in_place(vote_state); + } + + // This is to reset vote_state to T::default() if deserialize fails or panics. + let guard = DropGuard { vote_state }; + + let res = deserialize_fn(input, vote_state); + if res.is_ok() { + std::mem::forget(guard); + } + + res +} + +pub(super) fn deserialize_vote_state_into( + cursor: &mut Cursor<&[u8]>, + vote_state: *mut VoteStateV3, + has_latency: bool, +) -> Result<(), InstructionError> { + // General safety note: we must use add_or_mut! to access the `vote_state` fields as the value + // is assumed to be _uninitialized_, so creating references to the state or any of its inner + // fields is UB. + + read_pubkey_into( + cursor, + // Safety: if vote_state is non-null, node_pubkey is guaranteed to be valid too + unsafe { addr_of_mut!((*vote_state).node_pubkey) }, + )?; + read_pubkey_into( + cursor, + // Safety: if vote_state is non-null, authorized_withdrawer is guaranteed to be valid too + unsafe { addr_of_mut!((*vote_state).authorized_withdrawer) }, + )?; + let commission = read_u8(cursor)?; + let votes = read_votes(cursor, has_latency)?; + let root_slot = read_option_u64(cursor)?; + let authorized_voters = read_authorized_voters(cursor)?; + read_prior_voters_into(cursor, vote_state)?; + let epoch_credits = read_epoch_credits(cursor)?; + read_last_timestamp_into(cursor, vote_state)?; + + // Safety: if vote_state is non-null, all the fields are guaranteed to be + // valid pointers. + // + // Heap allocated collections - votes, authorized_voters and epoch_credits - + // are guaranteed not to leak after this point as the VoteStateV3 is fully + // initialized and will be regularly dropped. + unsafe { + addr_of_mut!((*vote_state).commission).write(commission); + addr_of_mut!((*vote_state).votes).write(votes); + addr_of_mut!((*vote_state).root_slot).write(root_slot); + addr_of_mut!((*vote_state).authorized_voters).write(authorized_voters); + addr_of_mut!((*vote_state).epoch_credits).write(epoch_credits); + } + + Ok(()) +} + +fn read_votes>( + cursor: &mut Cursor, + has_latency: bool, +) -> Result, InstructionError> { + let vote_count = read_u64(cursor)? as usize; + let mut votes = VecDeque::with_capacity(vote_count.min(MAX_LOCKOUT_HISTORY)); + + for _ in 0..vote_count { + let latency = if has_latency { read_u8(cursor)? } else { 0 }; + + let slot = read_u64(cursor)?; + let confirmation_count = read_u32(cursor)?; + let lockout = Lockout::new_with_confirmation_count(slot, confirmation_count); + + votes.push_back(LandedVote { latency, lockout }); + } + + Ok(votes) +} + +fn read_authorized_voters>( + cursor: &mut Cursor, +) -> Result { + let authorized_voter_count = read_u64(cursor)?; + let mut authorized_voters = AuthorizedVoters::default(); + + for _ in 0..authorized_voter_count { + let epoch = read_u64(cursor)?; + let authorized_voter = read_pubkey(cursor)?; + authorized_voters.insert(epoch, authorized_voter); + } + + Ok(authorized_voters) +} + +fn read_prior_voters_into>( + cursor: &mut Cursor, + vote_state: *mut VoteStateV3, +) -> Result<(), InstructionError> { + // Safety: if vote_state is non-null, prior_voters is guaranteed to be valid too + unsafe { + let prior_voters = addr_of_mut!((*vote_state).prior_voters); + let prior_voters_buf = addr_of_mut!((*prior_voters).buf) as *mut (Pubkey, Epoch, Epoch); + + for i in 0..MAX_ITEMS { + let prior_voter = read_pubkey(cursor)?; + let from_epoch = read_u64(cursor)?; + let until_epoch = read_u64(cursor)?; + + prior_voters_buf + .add(i) + .write((prior_voter, from_epoch, until_epoch)); + } + + (*vote_state).prior_voters.idx = read_u64(cursor)? as usize; + (*vote_state).prior_voters.is_empty = read_bool(cursor)?; + } + Ok(()) +} + +fn read_epoch_credits>( + cursor: &mut Cursor, +) -> Result, InstructionError> { + let epoch_credit_count = read_u64(cursor)? as usize; + let mut epoch_credits = Vec::with_capacity(epoch_credit_count.min(MAX_EPOCH_CREDITS_HISTORY)); + + for _ in 0..epoch_credit_count { + let epoch = read_u64(cursor)?; + let credits = read_u64(cursor)?; + let prev_credits = read_u64(cursor)?; + epoch_credits.push((epoch, credits, prev_credits)); + } + + Ok(epoch_credits) +} + +fn read_last_timestamp_into>( + cursor: &mut Cursor, + vote_state: *mut VoteStateV3, +) -> Result<(), InstructionError> { + let slot = read_u64(cursor)?; + let timestamp = read_i64(cursor)?; + + let last_timestamp = BlockTimestamp { slot, timestamp }; + + // Safety: if vote_state is non-null, last_timestamp is guaranteed to be valid too + unsafe { + addr_of_mut!((*vote_state).last_timestamp).write(last_timestamp); + } + + Ok(()) +} diff --git a/vote-interface/src/state/vote_state_v3.rs b/vote-interface/src/state/vote_state_v3.rs index 3ba8b26b6..aef392d16 100644 --- a/vote-interface/src/state/vote_state_v3.rs +++ b/vote-interface/src/state/vote_state_v3.rs @@ -145,49 +145,8 @@ impl VoteStateV3 { input: &[u8], vote_state: &mut VoteStateV3, ) -> Result<(), InstructionError> { - // Rebind vote_state to *mut VoteStateV3 so that the &mut binding isn't - // accessible anymore, preventing accidental use after this point. - // - // NOTE: switch to ptr::from_mut() once platform-tools moves to rustc >= 1.76 - let vote_state = vote_state as *mut VoteStateV3; - - // Safety: vote_state is valid to_drop (see drop_in_place() docs). After - // dropping, the pointer is treated as uninitialized and only accessed - // through ptr::write, which is safe as per drop_in_place docs. - unsafe { - std::ptr::drop_in_place(vote_state); - } - - // This is to reset vote_state to VoteStateV3::default() if deserialize fails or panics. - struct DropGuard { - vote_state: *mut VoteStateV3, - } - - impl Drop for DropGuard { - fn drop(&mut self) { - // Safety: - // - // Deserialize failed or panicked so at this point vote_state is uninitialized. We - // must write a new _valid_ value into it or after returning (or unwinding) from - // this function the caller is left with an uninitialized `&mut VoteStateV3`, which is - // UB (references must always be valid). - // - // This is always safe and doesn't leak memory because deserialize_into_ptr() writes - // into the fields that heap alloc only when it returns Ok(). - unsafe { - self.vote_state.write(VoteStateV3::default()); - } - } - } - - let guard = DropGuard { vote_state }; - - let res = VoteStateV3::deserialize_into_ptr(input, vote_state); - if res.is_ok() { - std::mem::forget(guard); - } - - res + use super::vote_state_deserialize; + vote_state_deserialize::deserialize_into(input, vote_state, Self::deserialize_into_ptr) } /// Deserializes the input `VoteStateVersions` buffer directly into the provided @@ -214,7 +173,7 @@ impl VoteStateV3 { input: &[u8], vote_state: *mut VoteStateV3, ) -> Result<(), InstructionError> { - use vote_state_deserialize::deserialize_vote_state_into; + use super::vote_state_deserialize::deserialize_vote_state_into; let mut cursor = std::io::Cursor::new(input); @@ -564,162 +523,3 @@ impl VoteStateV3 { && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET] } } - -#[cfg(any(target_os = "solana", feature = "bincode"))] -mod vote_state_deserialize { - use { - crate::{ - authorized_voters::AuthorizedVoters, - state::{ - BlockTimestamp, LandedVote, Lockout, VoteStateV3, MAX_EPOCH_CREDITS_HISTORY, - MAX_ITEMS, MAX_LOCKOUT_HISTORY, - }, - }, - solana_clock::Epoch, - solana_instruction_error::InstructionError, - solana_pubkey::Pubkey, - solana_serialize_utils::cursor::{ - read_bool, read_i64, read_option_u64, read_pubkey, read_pubkey_into, read_u32, - read_u64, read_u8, - }, - std::{collections::VecDeque, io::Cursor, ptr::addr_of_mut}, - }; - - pub(super) fn deserialize_vote_state_into( - cursor: &mut Cursor<&[u8]>, - vote_state: *mut VoteStateV3, - has_latency: bool, - ) -> Result<(), InstructionError> { - // General safety note: we must use add_or_mut! to access the `vote_state` fields as the value - // is assumed to be _uninitialized_, so creating references to the state or any of its inner - // fields is UB. - - read_pubkey_into( - cursor, - // Safety: if vote_state is non-null, node_pubkey is guaranteed to be valid too - unsafe { addr_of_mut!((*vote_state).node_pubkey) }, - )?; - read_pubkey_into( - cursor, - // Safety: if vote_state is non-null, authorized_withdrawer is guaranteed to be valid too - unsafe { addr_of_mut!((*vote_state).authorized_withdrawer) }, - )?; - let commission = read_u8(cursor)?; - let votes = read_votes(cursor, has_latency)?; - let root_slot = read_option_u64(cursor)?; - let authorized_voters = read_authorized_voters(cursor)?; - read_prior_voters_into(cursor, vote_state)?; - let epoch_credits = read_epoch_credits(cursor)?; - read_last_timestamp_into(cursor, vote_state)?; - - // Safety: if vote_state is non-null, all the fields are guaranteed to be - // valid pointers. - // - // Heap allocated collections - votes, authorized_voters and epoch_credits - - // are guaranteed not to leak after this point as the VoteStateV3 is fully - // initialized and will be regularly dropped. - unsafe { - addr_of_mut!((*vote_state).commission).write(commission); - addr_of_mut!((*vote_state).votes).write(votes); - addr_of_mut!((*vote_state).root_slot).write(root_slot); - addr_of_mut!((*vote_state).authorized_voters).write(authorized_voters); - addr_of_mut!((*vote_state).epoch_credits).write(epoch_credits); - } - - Ok(()) - } - - fn read_votes>( - cursor: &mut Cursor, - has_latency: bool, - ) -> Result, InstructionError> { - let vote_count = read_u64(cursor)? as usize; - let mut votes = VecDeque::with_capacity(vote_count.min(MAX_LOCKOUT_HISTORY)); - - for _ in 0..vote_count { - let latency = if has_latency { read_u8(cursor)? } else { 0 }; - - let slot = read_u64(cursor)?; - let confirmation_count = read_u32(cursor)?; - let lockout = Lockout::new_with_confirmation_count(slot, confirmation_count); - - votes.push_back(LandedVote { latency, lockout }); - } - - Ok(votes) - } - - fn read_authorized_voters>( - cursor: &mut Cursor, - ) -> Result { - let authorized_voter_count = read_u64(cursor)?; - let mut authorized_voters = AuthorizedVoters::default(); - - for _ in 0..authorized_voter_count { - let epoch = read_u64(cursor)?; - let authorized_voter = read_pubkey(cursor)?; - authorized_voters.insert(epoch, authorized_voter); - } - - Ok(authorized_voters) - } - - fn read_prior_voters_into>( - cursor: &mut Cursor, - vote_state: *mut VoteStateV3, - ) -> Result<(), InstructionError> { - // Safety: if vote_state is non-null, prior_voters is guaranteed to be valid too - unsafe { - let prior_voters = addr_of_mut!((*vote_state).prior_voters); - let prior_voters_buf = addr_of_mut!((*prior_voters).buf) as *mut (Pubkey, Epoch, Epoch); - - for i in 0..MAX_ITEMS { - let prior_voter = read_pubkey(cursor)?; - let from_epoch = read_u64(cursor)?; - let until_epoch = read_u64(cursor)?; - - prior_voters_buf - .add(i) - .write((prior_voter, from_epoch, until_epoch)); - } - - (*vote_state).prior_voters.idx = read_u64(cursor)? as usize; - (*vote_state).prior_voters.is_empty = read_bool(cursor)?; - } - Ok(()) - } - - fn read_epoch_credits>( - cursor: &mut Cursor, - ) -> Result, InstructionError> { - let epoch_credit_count = read_u64(cursor)? as usize; - let mut epoch_credits = - Vec::with_capacity(epoch_credit_count.min(MAX_EPOCH_CREDITS_HISTORY)); - - for _ in 0..epoch_credit_count { - let epoch = read_u64(cursor)?; - let credits = read_u64(cursor)?; - let prev_credits = read_u64(cursor)?; - epoch_credits.push((epoch, credits, prev_credits)); - } - - Ok(epoch_credits) - } - - fn read_last_timestamp_into>( - cursor: &mut Cursor, - vote_state: *mut VoteStateV3, - ) -> Result<(), InstructionError> { - let slot = read_u64(cursor)?; - let timestamp = read_i64(cursor)?; - - let last_timestamp = BlockTimestamp { slot, timestamp }; - - // Safety: if vote_state is non-null, last_timestamp is guaranteed to be valid too - unsafe { - addr_of_mut!((*vote_state).last_timestamp).write(last_timestamp); - } - - Ok(()) - } -} diff --git a/vote-interface/src/state/vote_state_v4.rs b/vote-interface/src/state/vote_state_v4.rs new file mode 100644 index 000000000..1ca30d395 --- /dev/null +++ b/vote-interface/src/state/vote_state_v4.rs @@ -0,0 +1,67 @@ +#[cfg(feature = "dev-context-only-utils")] +use arbitrary::Arbitrary; +#[cfg(feature = "serde")] +use serde_derive::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde_with::serde_as; +#[cfg(feature = "frozen-abi")] +use solana_frozen_abi_macro::{frozen_abi, AbiExample}; +use { + super::{BlockTimestamp, LandedVote, BLS_PUBLIC_KEY_COMPRESSED_SIZE}, + crate::authorized_voters::AuthorizedVoters, + solana_clock::{Epoch, Slot}, + solana_pubkey::Pubkey, + std::{collections::VecDeque, fmt::Debug}, +}; + +#[cfg_attr( + feature = "frozen-abi", + frozen_abi(digest = "2H9WgTh7LgdnpinvEwxzP3HF6SDuKp6qdwFmJk9jHDRP"), + derive(AbiExample) +)] +#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_as)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Debug, Default, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))] +pub struct VoteStateV4 { + /// The node that votes in this account. + pub node_pubkey: Pubkey, + /// The signer for withdrawals. + pub authorized_withdrawer: Pubkey, + + /// The collector account for inflation rewards. + pub inflation_rewards_collector: Pubkey, + /// The collector account for block revenue. + pub block_revenue_collector: Pubkey, + + /// Basis points (0-10,000) that represent how much of the inflation + /// rewards should be given to this vote account. + pub inflation_rewards_commission_bps: u16, + /// Basis points (0-10,000) that represent how much of the block revenue + /// should be given to this vote account. + pub block_revenue_commission_bps: u16, + + /// Reward amount pending distribution to stake delegators. + pub pending_delegator_rewards: u64, + + /// Compressed BLS pubkey for Alpenglow. + #[cfg_attr( + feature = "serde", + serde_as(as = "Option<[_; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>") + )] + pub bls_pubkey_compressed: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>, + + pub votes: VecDeque, + pub root_slot: Option, + + /// The signer for vote transactions. + /// Contains entries for the current epoch and the previous epoch. + pub authorized_voters: AuthorizedVoters, + + /// History of credits earned by the end of each epoch. + /// Each tuple is (Epoch, credits, prev_credits). + pub epoch_credits: Vec<(Epoch, u64, u64)>, + + /// Most recent timestamp submitted with a vote. + pub last_timestamp: BlockTimestamp, +}