diff --git a/Cargo.lock b/Cargo.lock index d9175d0d9c0682..291c7b1a6ec0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,6 +2210,21 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "five8_const" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b4f62f0f8ca357f93ae90c8c2dd1041a1f665fde2f889ea9b1787903829015" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a72055cd9cffc40c9f75f1e5810c80559e158796cf2202292ce4745889588" + [[package]] name = "fixedbitset" version = "0.4.0" @@ -6222,7 +6237,6 @@ dependencies = [ name = "solana-decode-error" version = "2.1.0" dependencies = [ - "num-derive", "num-traits", ] @@ -6877,10 +6891,51 @@ dependencies = [ name = "solana-program" version = "2.1.0" dependencies = [ - "anyhow", "arbitrary", "array-bytes", "assert_matches", + "bincode", + "blake3", + "borsh 0.10.3", + "borsh 1.5.1", + "bs58", + "getrandom 0.2.10", + "itertools 0.12.1", + "lazy_static", + "libsecp256k1", + "log", + "memoffset 0.9.1", + "num-bigint 0.4.6", + "num-derive", + "num-traits", + "rand 0.8.5", + "rustc_version 0.4.0", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-msg", + "solana-program-core", + "solana-program-memory", + "solana-sanitize", + "solana-secp256k1-recover", + "solana-short-vec", + "thiserror", +] + +[[package]] +name = "solana-program-core" +version = "2.1.0" +dependencies = [ + "anyhow", + "arbitrary", "base64 0.22.1", "bincode", "bitflags 2.6.0", @@ -6894,17 +6949,14 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", + "five8_const", "getrandom 0.2.10", "itertools 0.12.1", "js-sys", "lazy_static", - "libsecp256k1", "log", "memoffset 0.9.1", - "num-bigint 0.4.6", - "num-derive", "num-traits", - "parking_lot 0.12.3", "qualifier_attr", "rand 0.8.5", "rustc_version 0.4.0", @@ -6922,13 +6974,11 @@ dependencies = [ "solana-frozen-abi-macro", "solana-logger", "solana-msg", + "solana-program-core", "solana-program-memory", "solana-sanitize", - "solana-sdk-macro", - "solana-secp256k1-recover", "solana-short-vec", "static_assertions", - "thiserror", "wasm-bindgen", ] @@ -7403,6 +7453,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-logger", "solana-program", + "solana-program-core", "solana-program-memory", "solana-sanitize", "solana-sdk", diff --git a/Cargo.toml b/Cargo.toml index de49b9b8ac1016..47a389bf215f54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ members = [ "sdk/msg", "sdk/package-metadata-macro", "sdk/program", + "sdk/program-core", "sdk/program-memory", "send-transaction-service", "short-vec", @@ -226,6 +227,7 @@ env_logger = "0.9.3" etcd-client = "0.11.1" fast-math = "0.1" fd-lock = "3.0.13" +five8_const = "0.1.3" flate2 = "1.0.30" fnv = "1.0.7" fs_extra = "1.3.0" @@ -394,6 +396,7 @@ solana-perf = { path = "perf", version = "=2.1.0" } solana-poh = { path = "poh", version = "=2.1.0" } solana-poseidon = { path = "poseidon", version = "=2.1.0" } solana-program = { path = "sdk/program", version = "=2.1.0", default-features = false } +solana-program-core = { path = "sdk/program-core", version = "=2.1.0", default-features = false } solana-program-memory = { path = "sdk/program-memory", version = "=2.1.0" } solana-program-runtime = { path = "program-runtime", version = "=2.1.0" } solana-program-test = { path = "program-test", version = "=2.1.0" } diff --git a/accounts-db/benches/accounts_index.rs b/accounts-db/benches/accounts_index.rs index 711f061175d22a..11536cade0598a 100644 --- a/accounts-db/benches/accounts_index.rs +++ b/accounts-db/benches/accounts_index.rs @@ -2,6 +2,8 @@ extern crate test; +#[allow(deprecated)] +use solana_sdk::pubkey; use { rand::{thread_rng, Rng}, solana_accounts_db::{ @@ -11,7 +13,7 @@ use { ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS, }, }, - solana_sdk::{account::AccountSharedData, pubkey}, + solana_sdk::account::AccountSharedData, std::sync::Arc, test::Bencher, }; diff --git a/ci/nits.sh b/ci/nits.sh index 856a4d323cddf0..ec4c9697ca8e47 100755 --- a/ci/nits.sh +++ b/ci/nits.sh @@ -29,8 +29,8 @@ declare print_free_tree=( ':sdk/**.rs' ':^sdk/cargo-build-sbf/**.rs' ':^sdk/msg/src/lib.rs' - ':^sdk/program/src/program_option.rs' - ':^sdk/program/src/program_stubs.rs' + ':^sdk/program-core/src/program_option.rs' + ':^sdk/program-core/src/program_stubs.rs' ':programs/**.rs' ':^**bin**.rs' ':^**bench**.rs' diff --git a/core/benches/banking_stage.rs b/core/benches/banking_stage.rs index d0efbfafddfc0b..7313c59c28f597 100644 --- a/core/benches/banking_stage.rs +++ b/core/benches/banking_stage.rs @@ -8,6 +8,8 @@ use { extern crate test; +#[allow(deprecated)] +use solana_sdk::pubkey; use { crossbeam_channel::{unbounded, Receiver}, log::*, @@ -46,7 +48,6 @@ use { genesis_config::GenesisConfig, hash::Hash, message::Message, - pubkey, signature::{Keypair, Signature, Signer}, system_instruction, system_transaction, timing::{duration_as_us, timestamp}, diff --git a/keygen/src/keygen.rs b/keygen/src/keygen.rs index 0965b4ce8ad04b..56722c5e946cc0 100644 --- a/keygen/src/keygen.rs +++ b/keygen/src/keygen.rs @@ -50,8 +50,10 @@ use { }; mod smallest_length_44_public_key { + #[allow(deprecated)] use solana_sdk::{pubkey, pubkey::Pubkey}; + #[allow(deprecated)] pub(super) static PUBKEY: Pubkey = pubkey!("21111111111111111111111111111111111111111111"); #[test] diff --git a/ledger/benches/protobuf.rs b/ledger/benches/protobuf.rs index e211e85c87ff35..826076d55b576c 100644 --- a/ledger/benches/protobuf.rs +++ b/ledger/benches/protobuf.rs @@ -2,6 +2,8 @@ #![feature(test)] extern crate test; +#[allow(deprecated)] +use solana_sdk::pubkey; use { bincode::{deserialize, serialize}, solana_ledger::{ @@ -10,7 +12,7 @@ use { get_tmp_ledger_path_auto_delete, }, solana_runtime::bank::RewardType, - solana_sdk::{clock::Slot, pubkey}, + solana_sdk::clock::Slot, solana_transaction_status::{Reward, Rewards}, std::path::Path, test::Bencher, diff --git a/program-runtime/src/sysvar_cache.rs b/program-runtime/src/sysvar_cache.rs index 79124bd93f379e..28a60ad0c6c71d 100644 --- a/program-runtime/src/sysvar_cache.rs +++ b/program-runtime/src/sysvar_cache.rs @@ -50,7 +50,9 @@ pub struct SysvarCache { // declare_deprecated_sysvar_id doesn't support const. // These sysvars are going away anyway. +#[allow(deprecated)] const FEES_ID: Pubkey = solana_sdk::pubkey!("SysvarFees111111111111111111111111111111111"); +#[allow(deprecated)] const RECENT_BLOCKHASHES_ID: Pubkey = solana_sdk::pubkey!("SysvarRecentB1ockHashes11111111111111111111"); diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 16a475c9d77ac0..0e4522e90fd000 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -1649,6 +1649,21 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "five8_const" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b4f62f0f8ca357f93ae90c8c2dd1041a1f665fde2f889ea9b1787903829015" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a72055cd9cffc40c9f75f1e5810c80559e158796cf2202292ce4745889588" + [[package]] name = "fixedbitset" version = "0.4.1" @@ -5310,6 +5325,40 @@ dependencies = [ [[package]] name = "solana-program" version = "2.1.0" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.3", + "borsh 1.5.1", + "bs58", + "getrandom 0.2.10", + "lazy_static", + "log", + "memoffset 0.9.0", + "num-bigint 0.4.6", + "num-derive", + "num-traits", + "rand 0.8.5", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "sha2 0.10.8", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-msg", + "solana-program-core", + "solana-program-memory", + "solana-sanitize", + "solana-secp256k1-recover", + "solana-short-vec", + "thiserror", +] + +[[package]] +name = "solana-program-core" +version = "2.1.0" dependencies = [ "base64 0.22.1", "bincode", @@ -5324,16 +5373,13 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", + "five8_const", "getrandom 0.2.10", "js-sys", "lazy_static", "log", "memoffset 0.9.0", - "num-bigint 0.4.6", - "num-derive", "num-traits", - "parking_lot 0.12.2", - "rand 0.8.5", "rustc_version", "serde", "serde_bytes", @@ -5346,10 +5392,7 @@ dependencies = [ "solana-msg", "solana-program-memory", "solana-sanitize", - "solana-sdk-macro", - "solana-secp256k1-recover", "solana-short-vec", - "thiserror", "wasm-bindgen", ] @@ -6209,6 +6252,7 @@ dependencies = [ "solana-bn254", "solana-decode-error", "solana-program", + "solana-program-core", "solana-program-memory", "solana-sanitize", "solana-sdk-macro", diff --git a/runtime/src/bank/fee_distribution.rs b/runtime/src/bank/fee_distribution.rs index 8ac0f8e7338b31..ab2a1f9fefc7ad 100644 --- a/runtime/src/bank/fee_distribution.rs +++ b/runtime/src/bank/fee_distribution.rs @@ -352,6 +352,8 @@ impl Bank { #[cfg(test)] pub mod tests { + #[allow(deprecated)] + use solana_sdk::pubkey; use { super::*, crate::genesis_utils::{ @@ -359,8 +361,8 @@ pub mod tests { create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs, }, solana_sdk::{ - account::AccountSharedData, feature_set, native_token::sol_to_lamports, pubkey, - rent::Rent, signature::Signer, + account::AccountSharedData, feature_set, native_token::sol_to_lamports, rent::Rent, + signature::Signer, }, std::sync::RwLock, }; diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 7db5d780c76b07..f0525c92c64321 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -86,6 +86,7 @@ solana-decode-error = { workspace = true } solana-frozen-abi = { workspace = true, optional = true } solana-frozen-abi-macro = { workspace = true, optional = true } solana-program = { workspace = true } +solana-program-core = { workspace = true } solana-program-memory = { workspace = true } solana-sanitize = { workspace = true } solana-sdk-macro = { workspace = true } diff --git a/sdk/decode-error/Cargo.toml b/sdk/decode-error/Cargo.toml index 13fb6370791604..50606afa6d319d 100644 --- a/sdk/decode-error/Cargo.toml +++ b/sdk/decode-error/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-decode-error" -description = "Solana DecodeError Trait" +description = "Solana DecodeError Trait. Obsolete." documentation = "https://docs.rs/solana-decode-error" version = { workspace = true } authors = { workspace = true } @@ -11,6 +11,3 @@ edition = { workspace = true } [dependencies] num-traits = { workspace = true } - -[dev-dependencies] -num-derive = { workspace = true } diff --git a/sdk/decode-error/src/lib.rs b/sdk/decode-error/src/lib.rs index d225ba81a8a039..7d4dac9ce6a2ef 100644 --- a/sdk/decode-error/src/lib.rs +++ b/sdk/decode-error/src/lib.rs @@ -2,25 +2,7 @@ use num_traits::FromPrimitive; -/// Allows custom errors to be decoded back to their original enum. -/// -/// Some Solana error enums, like [`ProgramError`], include a `Custom` variant, -/// like [`ProgramError::Custom`], that contains a `u32` error code. This code -/// may represent any error that is not covered by the error enum's named -/// variants. It is common for programs to convert their own error enums to an -/// error code and store it in the `Custom` variant, possibly with the help of -/// the [`ToPrimitive`] trait. -/// -/// This trait builds on the [`FromPrimitive`] trait to help convert those error -/// codes to the original error enum they represent. -/// -/// As this allows freely converting `u32` to any type that implements -/// `FromPrimitive`, it is only used correctly when the caller is certain of the -/// original error type. -/// -/// [`ProgramError`]: crate::program_error::ProgramError -/// [`ProgramError::Custom`]: crate::program_error::ProgramError::Custom -/// [`ToPrimitive`]: num_traits::ToPrimitive +/// Obsolete, will be deleted. pub trait DecodeError { fn decode_custom_error_to_enum(custom: u32) -> Option where @@ -30,28 +12,3 @@ pub trait DecodeError { } fn type_of() -> &'static str; } - -#[cfg(test)] -mod tests { - use {super::*, num_derive::FromPrimitive}; - - #[test] - fn test_decode_custom_error_to_enum() { - #[derive(Debug, FromPrimitive, PartialEq, Eq)] - enum TestEnum { - A, - B, - C, - } - impl DecodeError for TestEnum { - fn type_of() -> &'static str { - "TestEnum" - } - } - assert_eq!(TestEnum::decode_custom_error_to_enum(0), Some(TestEnum::A)); - assert_eq!(TestEnum::decode_custom_error_to_enum(1), Some(TestEnum::B)); - assert_eq!(TestEnum::decode_custom_error_to_enum(2), Some(TestEnum::C)); - let option: Option = TestEnum::decode_custom_error_to_enum(3); - assert_eq!(option, None); - } -} diff --git a/sdk/program-core/Cargo.toml b/sdk/program-core/Cargo.toml new file mode 100644 index 00000000000000..3ae7435679f3a6 --- /dev/null +++ b/sdk/program-core/Cargo.toml @@ -0,0 +1,131 @@ +[package] +name = "solana-program-core" +description = "Solana Program Core SDK" +documentation = "https://docs.rs/solana-program-core" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } +rust-version = "1.75.0" # solana platform-tools rust version + +[dependencies] +bincode = { workspace = true, optional = true } +blake3 = { workspace = true, features = ["digest", "traits-preview"], optional = true } +borsh = { workspace = true, optional = true } +borsh0-10 = { package = "borsh", version = "0.10.3", optional = true } +bs58 = { workspace = true } +bv = { workspace = true, optional = true } +bytemuck = { workspace = true, optional = true } +bytemuck_derive = { workspace = true, optional = true } +five8_const = { workspace = true } +lazy_static = { workspace = true } +log = { workspace = true, optional = true } +memoffset = { workspace = true } +num-traits = { workspace = true, optional = true } +qualifier_attr = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +serde_bytes = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } +sha2 = { workspace = true, optional = true } +sha3 = { workspace = true, optional = true } +solana-atomic-u64 = { workspace = true } +solana-decode-error = { workspace = true } +solana-frozen-abi = { workspace = true, optional = true } +solana-frozen-abi-macro = { workspace = true, optional = true } +solana-msg = { workspace = true } +solana-program-memory = { workspace = true } +solana-sanitize = { workspace = true } +solana-short-vec = { workspace = true, optional = true } + +# This is currently needed to build on-chain programs reliably. +# Borsh 0.10 may pull in hashbrown 0.13, which uses ahash 0.8, which uses +# getrandom 0.2 underneath. This explicit dependency allows for no-std if cargo +# upgrades Borsh's dependency to hashbrown 0.13. +# Remove this once borsh 0.11 or 1.0 is released, which correctly declares the +# hashbrown dependency as optional. +[target.'cfg(target_os = "solana")'.dependencies] +getrandom = { workspace = true, features = ["custom"], optional = true } +solana-define-syscall = { workspace = true } + +[target.'cfg(not(target_os = "solana"))'.dependencies] +arbitrary = { workspace = true, features = ["derive"], optional = true } +base64 = { workspace = true, features = ["alloc", "std"], optional = true } +bitflags = { workspace = true } +curve25519-dalek = { workspace = true, optional = true } + +[target.'cfg(not(target_os = "solana"))'.dev-dependencies] +arbitrary = { workspace = true, features = ["derive"] } +rand = { workspace = true } +solana-logger = { workspace = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = { workspace = true } +console_log = { workspace = true } +getrandom = { workspace = true, features = ["js", "wasm-bindgen"] } +js-sys = { workspace = true } +log = { workspace = true } +wasm-bindgen = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } +serde_json = { workspace = true } +serial_test = { workspace = true } +solana-program-core = { path = ".", features = ["dev-context-only-utils"] } +static_assertions = { workspace = true } + +[build-dependencies] +rustc_version = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +base64 = ["dep:base64"] +bincode = ["dep:bincode", "serde"] +blake3 = ["dep:blake3"] +borsh = ["dep:borsh", "dep:borsh0-10", "dep:getrandom"] +bv = ["dep:bv"] +bytemuck = ["dep:bytemuck", "dep:bytemuck_derive"] +curve25519 = ["dep:curve25519-dalek"] +dev-context-only-utils = [ + "dep:arbitrary", + "dep:qualifier_attr", + "base64", + "bincode", + "blake3", + "borsh", + "bv", + "bytemuck", + "curve25519", + "log", + "num-traits", + "serde", + "sha2", + "sha3" +] +frozen-abi = [ + "dep:solana-frozen-abi", + "dep:solana-frozen-abi-macro", + "solana-short-vec/frozen-abi", +] +log = ["dep:log"] +num-traits = [ + "dep:num-traits", + "solana-program-memory/num-traits" +] +serde = [ + "dep:serde", + "dep:serde_bytes", + "dep:serde_derive", + "dep:solana-short-vec", + "bv/serde" +] +sha2 = ["dep:sha2"] +sha3 = ["dep:sha3"] diff --git a/sdk/program-core/build.rs b/sdk/program-core/build.rs new file mode 120000 index 00000000000000..84539eddaa6ded --- /dev/null +++ b/sdk/program-core/build.rs @@ -0,0 +1 @@ +../../frozen-abi/build.rs \ No newline at end of file diff --git a/sdk/program/src/account_info.rs b/sdk/program-core/src/account_info.rs similarity index 98% rename from sdk/program/src/account_info.rs rename to sdk/program-core/src/account_info.rs index 485195381a9f64..98fe6f52031341 100644 --- a/sdk/program/src/account_info.rs +++ b/sdk/program-core/src/account_info.rs @@ -132,7 +132,7 @@ impl<'a> AccountInfo<'a> { /// memory. /// /// Note: Account data can be increased within a single call by up to - /// `solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes. + /// `solana_program_core::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes. /// /// Note: Memory used to grow is already zero-initialized upon program /// entrypoint and re-zeroing it wastes compute units. If within the same @@ -216,10 +216,12 @@ impl<'a> AccountInfo<'a> { } } + #[cfg(feature = "bincode")] pub fn deserialize_data(&self) -> Result { bincode::deserialize(&self.data.borrow()) } + #[cfg(feature = "bincode")] pub fn serialize_data(&self, state: &T) -> Result<(), bincode::Error> { if bincode::serialized_size(state)? > self.data_len() as u64 { return Err(Box::new(bincode::ErrorKind::SizeLimit)); @@ -292,12 +294,12 @@ impl<'a, T: Account> IntoAccountInfo<'a> for &'a mut (Pubkey, T) { /// # Examples /// /// ``` -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{AccountInfo, next_account_info}, /// entrypoint::ProgramResult, /// pubkey::Pubkey, /// }; -/// # use solana_program::program_error::ProgramError; +/// # use solana_program_core::program_error::ProgramError; /// /// pub fn process_instruction( /// program_id: &Pubkey, @@ -343,12 +345,12 @@ pub fn next_account_info<'a, 'b, I: Iterator>>( /// # Examples /// /// ``` -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{AccountInfo, next_account_info, next_account_infos}, /// entrypoint::ProgramResult, /// pubkey::Pubkey, /// }; -/// # use solana_program::program_error::ProgramError; +/// # use solana_program_core::program_error::ProgramError; /// /// pub fn process_instruction( /// program_id: &Pubkey, diff --git a/sdk/program-core/src/address_lookup_table/error.rs b/sdk/program-core/src/address_lookup_table/error.rs new file mode 100644 index 00000000000000..cbc0452f05bc4b --- /dev/null +++ b/sdk/program-core/src/address_lookup_table/error.rs @@ -0,0 +1,37 @@ +use core::fmt; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum AddressLookupError { + /// Attempted to lookup addresses from a table that does not exist + LookupTableAccountNotFound, + + /// Attempted to lookup addresses from an account owned by the wrong program + InvalidAccountOwner, + + /// Attempted to lookup addresses from an invalid account + InvalidAccountData, + + /// Address lookup contains an invalid index + InvalidLookupIndex, +} + +impl std::error::Error for AddressLookupError {} + +impl fmt::Display for AddressLookupError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AddressLookupError::LookupTableAccountNotFound => { + f.write_str("Attempted to lookup addresses from a table that does not exist") + } + AddressLookupError::InvalidAccountOwner => f.write_str( + "Attempted to lookup addresses from an account owned by the wrong program", + ), + AddressLookupError::InvalidAccountData => { + f.write_str("Attempted to lookup addresses from an invalid account") + } + AddressLookupError::InvalidLookupIndex => { + f.write_str("Address lookup contains an invalid index") + } + } + } +} diff --git a/sdk/program/src/address_lookup_table/instruction.rs b/sdk/program-core/src/address_lookup_table/instruction.rs similarity index 84% rename from sdk/program/src/address_lookup_table/instruction.rs rename to sdk/program-core/src/address_lookup_table/instruction.rs index 5687ab6d05a1aa..e9a220bd5bc28c 100644 --- a/sdk/program/src/address_lookup_table/instruction.rs +++ b/sdk/program-core/src/address_lookup_table/instruction.rs @@ -1,15 +1,11 @@ -use { - crate::{ - address_lookup_table::program::id, - clock::Slot, - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - system_program, - }, - serde_derive::{Deserialize, Serialize}, -}; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[cfg(feature = "bincode")] +use crate::instruction::AccountMeta; +#[cfg(feature = "bincode")] +use crate::instruction::Instruction; +use crate::{clock::Slot, pubkey::Pubkey}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum ProgramInstruction { /// Create an address lookup table /// @@ -67,6 +63,7 @@ pub enum ProgramInstruction { CloseLookupTable, } +#[cfg(any(all(feature = "curve25519", feature = "sha2"), target_os = "solana"))] /// Derives the address of an address table account from a wallet address and a recent block's slot. pub fn derive_lookup_table_address( authority_address: &Pubkey, @@ -74,10 +71,11 @@ pub fn derive_lookup_table_address( ) -> (Pubkey, u8) { Pubkey::find_program_address( &[authority_address.as_ref(), &recent_block_slot.to_le_bytes()], - &id(), + &crate::address_lookup_table::program::id(), ) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Constructs an instruction to create a table account and returns /// the instruction and the table account's derived address. fn create_lookup_table_common( @@ -89,7 +87,7 @@ fn create_lookup_table_common( let (lookup_table_address, bump_seed) = derive_lookup_table_address(&authority_address, recent_slot); let instruction = Instruction::new_with_bincode( - id(), + crate::address_lookup_table::program::id(), &ProgramInstruction::CreateLookupTable { recent_slot, bump_seed, @@ -98,13 +96,14 @@ fn create_lookup_table_common( AccountMeta::new(lookup_table_address, false), AccountMeta::new_readonly(authority_address, authority_is_signer), AccountMeta::new(payer_address, true), - AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new_readonly(crate::system_program::id(), false), ], ); (instruction, lookup_table_address) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Constructs an instruction to create a table account and returns /// the instruction and the table account's derived address. /// @@ -121,6 +120,7 @@ pub fn create_lookup_table_signed( create_lookup_table_common(authority_address, payer_address, recent_slot, true) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Constructs an instruction to create a table account and returns /// the instruction and the table account's derived address. /// @@ -140,9 +140,10 @@ pub fn create_lookup_table( /// Constructs an instruction that freezes an address lookup /// table so that it can never be closed or extended again. Empty /// lookup tables cannot be frozen. +#[cfg(feature = "bincode")] pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubkey) -> Instruction { Instruction::new_with_bincode( - id(), + crate::address_lookup_table::program::id(), &ProgramInstruction::FreezeLookupTable, vec![ AccountMeta::new(lookup_table_address, false), @@ -151,6 +152,7 @@ pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubk ) } +#[cfg(feature = "bincode")] /// Constructs an instruction which extends an address lookup /// table account with new addresses. pub fn extend_lookup_table( @@ -167,17 +169,18 @@ pub fn extend_lookup_table( if let Some(payer_address) = payer_address { accounts.extend([ AccountMeta::new(payer_address, true), - AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new_readonly(crate::system_program::id(), false), ]); } Instruction::new_with_bincode( - id(), + crate::address_lookup_table::program::id(), &ProgramInstruction::ExtendLookupTable { new_addresses }, accounts, ) } +#[cfg(feature = "bincode")] /// Constructs an instruction that deactivates an address lookup /// table so that it cannot be extended again and will be unusable /// and eligible for closure after a short amount of time. @@ -186,7 +189,7 @@ pub fn deactivate_lookup_table( authority_address: Pubkey, ) -> Instruction { Instruction::new_with_bincode( - id(), + crate::address_lookup_table::program::id(), &ProgramInstruction::DeactivateLookupTable, vec![ AccountMeta::new(lookup_table_address, false), @@ -195,6 +198,7 @@ pub fn deactivate_lookup_table( ) } +#[cfg(feature = "bincode")] /// Returns an instruction that closes an address lookup table /// account. The account will be deallocated and the lamports /// will be drained to the recipient address. @@ -204,7 +208,7 @@ pub fn close_lookup_table( recipient_address: Pubkey, ) -> Instruction { Instruction::new_with_bincode( - id(), + crate::address_lookup_table::program::id(), &ProgramInstruction::CloseLookupTable, vec![ AccountMeta::new(lookup_table_address, false), diff --git a/sdk/program/src/address_lookup_table/mod.rs b/sdk/program-core/src/address_lookup_table/mod.rs similarity index 100% rename from sdk/program/src/address_lookup_table/mod.rs rename to sdk/program-core/src/address_lookup_table/mod.rs diff --git a/sdk/program/src/address_lookup_table/state.rs b/sdk/program-core/src/address_lookup_table/state.rs similarity index 97% rename from sdk/program/src/address_lookup_table/state.rs rename to sdk/program-core/src/address_lookup_table/state.rs index df564f78fe2577..6d4869b6db65f9 100644 --- a/sdk/program/src/address_lookup_table/state.rs +++ b/sdk/program-core/src/address_lookup_table/state.rs @@ -1,11 +1,11 @@ +#[cfg(feature = "bincode")] +use crate::instruction::InstructionError; #[cfg(feature = "frozen-abi")] use solana_frozen_abi_macro::{AbiEnumVisitor, AbiExample}; use { - serde_derive::{Deserialize, Serialize}, - solana_program::{ + crate::{ address_lookup_table::error::AddressLookupError, clock::Slot, - instruction::InstructionError, pubkey::Pubkey, slot_hashes::{SlotHashes, MAX_ENTRIES}, }, @@ -28,7 +28,8 @@ pub enum LookupTableStatus { /// Address lookup table metadata #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct LookupTableMeta { /// Lookup tables cannot be closed until the deactivation slot is /// no longer "recent" (not accessible in the `SlotHashes` sysvar). @@ -106,7 +107,8 @@ impl LookupTableMeta { /// Program account states #[cfg_attr(feature = "frozen-abi", derive(AbiEnumVisitor, AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] #[allow(clippy::large_enum_variant)] pub enum ProgramState { /// Account is not initialized. @@ -123,6 +125,7 @@ pub struct AddressLookupTable<'a> { } impl<'a> AddressLookupTable<'a> { + #[cfg(feature = "bincode")] /// Serialize an address table's updated meta data and zero /// any leftover bytes. pub fn overwrite_meta_data( @@ -181,6 +184,7 @@ impl<'a> AddressLookupTable<'a> { .ok_or(AddressLookupError::InvalidLookupIndex) } + #[cfg(feature = "bincode")] /// Serialize an address table including its addresses pub fn serialize_for_tests(self) -> Result, InstructionError> { let mut data = vec![0; LOOKUP_TABLE_META_SIZE]; @@ -191,6 +195,7 @@ impl<'a> AddressLookupTable<'a> { Ok(data) } + #[cfg(all(feature = "bincode", feature = "bytemuck"))] /// Efficiently deserialize an address table without allocating /// for stored addresses. pub fn deserialize(data: &'a [u8]) -> Result, InstructionError> { diff --git a/sdk/program/src/bpf_loader.rs b/sdk/program-core/src/bpf_loader.rs similarity index 100% rename from sdk/program/src/bpf_loader.rs rename to sdk/program-core/src/bpf_loader.rs diff --git a/sdk/program/src/bpf_loader_deprecated.rs b/sdk/program-core/src/bpf_loader_deprecated.rs similarity index 100% rename from sdk/program/src/bpf_loader_deprecated.rs rename to sdk/program-core/src/bpf_loader_deprecated.rs diff --git a/sdk/program/src/bpf_loader_upgradeable.rs b/sdk/program-core/src/bpf_loader_upgradeable.rs similarity index 93% rename from sdk/program/src/bpf_loader_upgradeable.rs rename to sdk/program-core/src/bpf_loader_upgradeable.rs index 82e9292fde2429..294f814d921287 100644 --- a/sdk/program/src/bpf_loader_upgradeable.rs +++ b/sdk/program-core/src/bpf_loader_upgradeable.rs @@ -15,18 +15,22 @@ //! //! [`loader_upgradeable_instruction`]: crate::loader_upgradeable_instruction +use crate::pubkey::Pubkey; +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] +use crate::sysvar; +#[cfg(feature = "bincode")] use crate::{ instruction::{AccountMeta, Instruction, InstructionError}, loader_upgradeable_instruction::UpgradeableLoaderInstruction, - pubkey::Pubkey, - system_instruction, sysvar, + system_instruction, }; crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111"); /// Upgradeable loader account states #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum UpgradeableLoaderState { /// Account is not initialized. Uninitialized, @@ -84,11 +88,13 @@ impl UpgradeableLoaderState { } } +#[cfg(any(all(feature = "curve25519", feature = "sha2"), target_os = "solana"))] /// Returns the program data address for a program ID pub fn get_program_data_address(program_address: &Pubkey) -> Pubkey { Pubkey::find_program_address(&[program_address.as_ref()], &id()).0 } +#[cfg(feature = "bincode")] /// Returns the instructions required to initialize a Buffer account. pub fn create_buffer( payer_address: &Pubkey, @@ -116,6 +122,7 @@ pub fn create_buffer( ]) } +#[cfg(feature = "bincode")] /// Returns the instructions required to write a chunk of program data to a /// buffer account. pub fn write( @@ -134,6 +141,7 @@ pub fn write( ) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Returns the instructions required to deploy a program with a specified /// maximum program length. The maximum length must be large enough to /// accommodate any future upgrades. @@ -171,6 +179,7 @@ pub fn deploy_with_max_program_len( ]) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Returns the instructions required to upgrade a program. pub fn upgrade( program_address: &Pubkey, @@ -210,6 +219,7 @@ pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 7 == instruction_data[0] } +#[cfg(feature = "bincode")] /// Returns the instructions required to set a buffers's authority. pub fn set_buffer_authority( buffer_address: &Pubkey, @@ -227,6 +237,7 @@ pub fn set_buffer_authority( ) } +#[cfg(feature = "bincode")] /// Returns the instructions required to set a buffers's authority. If using this instruction, the new authority /// must sign. pub fn set_buffer_authority_checked( @@ -245,6 +256,7 @@ pub fn set_buffer_authority_checked( ) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Returns the instructions required to set a program's authority. pub fn set_upgrade_authority( program_address: &Pubkey, @@ -263,6 +275,7 @@ pub fn set_upgrade_authority( Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Returns the instructions required to set a program's authority. If using this instruction, the new authority /// must sign. pub fn set_upgrade_authority_checked( @@ -284,6 +297,7 @@ pub fn set_upgrade_authority_checked( ) } +#[cfg(feature = "bincode")] /// Returns the instructions required to close a buffer account pub fn close( close_address: &Pubkey, @@ -298,6 +312,7 @@ pub fn close( ) } +#[cfg(feature = "bincode")] /// Returns the instructions required to close program, buffer, or uninitialized account pub fn close_any( close_address: &Pubkey, @@ -318,6 +333,7 @@ pub fn close_any( Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas) } +#[cfg(all(feature = "bincode", any(all(feature = "curve25519", feature = "sha2"), target_os = "solana")))] /// Returns the instruction required to extend the size of a program's /// executable data account pub fn extend_program( diff --git a/sdk/program/src/clock.rs b/sdk/program-core/src/clock.rs similarity index 90% rename from sdk/program/src/clock.rs rename to sdk/program-core/src/clock.rs index 5cf609d3000c26..ce9985d2106b4f 100644 --- a/sdk/program/src/clock.rs +++ b/sdk/program-core/src/clock.rs @@ -20,8 +20,6 @@ //! //! [oracle]: https://docs.solanalabs.com/implemented-proposals/validator-timestamp-oracle -use solana_sdk_macro::CloneZeroed; - /// The default tick rate that the cluster attempts to achieve (160 per second). /// /// Note that the actual tick rate at any given time should be expected to drift. @@ -172,7 +170,8 @@ pub type UnixTimestamp = i64; /// /// All members of `Clock` start from 0 upon network boot. #[repr(C)] -#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq, Eq)] pub struct Clock { /// The current `Slot`. pub slot: Slot, @@ -195,6 +194,25 @@ pub struct Clock { pub unix_timestamp: UnixTimestamp, } +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for Clock { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).slot).write(self.slot); + std::ptr::addr_of_mut!((*ptr).epoch_start_timestamp).write(self.epoch_start_timestamp); + std::ptr::addr_of_mut!((*ptr).epoch).write(self.epoch); + std::ptr::addr_of_mut!((*ptr).leader_schedule_epoch).write(self.leader_schedule_epoch); + std::ptr::addr_of_mut!((*ptr).unix_timestamp).write(self.unix_timestamp); + value.assume_init() + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/sdk/program/src/compute_units.rs b/sdk/program-core/src/compute_units.rs similarity index 100% rename from sdk/program/src/compute_units.rs rename to sdk/program-core/src/compute_units.rs diff --git a/sdk/program/src/debug_account_data.rs b/sdk/program-core/src/debug_account_data.rs similarity index 100% rename from sdk/program/src/debug_account_data.rs rename to sdk/program-core/src/debug_account_data.rs diff --git a/sdk/program/src/ed25519_program.rs b/sdk/program-core/src/ed25519_program.rs similarity index 100% rename from sdk/program/src/ed25519_program.rs rename to sdk/program-core/src/ed25519_program.rs diff --git a/sdk/program/src/entrypoint.rs b/sdk/program-core/src/entrypoint.rs similarity index 99% rename from sdk/program/src/entrypoint.rs rename to sdk/program-core/src/entrypoint.rs index d0d579411dcfe8..811ee3d54966bc 100644 --- a/sdk/program/src/entrypoint.rs +++ b/sdk/program-core/src/entrypoint.rs @@ -99,7 +99,7 @@ pub const NON_DUP_MARKER: u8 = u8::MAX; /// #[cfg(not(feature = "no-entrypoint"))] /// pub mod entrypoint { /// -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::AccountInfo, /// entrypoint, /// entrypoint::ProgramResult, diff --git a/sdk/program/src/epoch_rewards.rs b/sdk/program-core/src/epoch_rewards.rs similarity index 70% rename from sdk/program/src/epoch_rewards.rs rename to sdk/program-core/src/epoch_rewards.rs index 9060f3e208090f..bb0f0e27a94a0e 100644 --- a/sdk/program/src/epoch_rewards.rs +++ b/sdk/program-core/src/epoch_rewards.rs @@ -6,11 +6,12 @@ //! //! [`sysvar::epoch_rewards`]: crate::sysvar::epoch_rewards -use {crate::hash::Hash, solana_sdk_macro::CloneZeroed, std::ops::AddAssign}; +use {crate::hash::Hash, std::ops::AddAssign}; #[repr(C, align(16))] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, CloneZeroed)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Default)] pub struct EpochRewards { /// The starting block height of the rewards distribution in the current /// epoch @@ -40,6 +41,28 @@ pub struct EpochRewards { pub active: bool, } +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for EpochRewards { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).distribution_starting_block_height) + .write(self.distribution_starting_block_height); + std::ptr::addr_of_mut!((*ptr).num_partitions).write(self.num_partitions); + std::ptr::addr_of_mut!((*ptr).parent_blockhash).write(self.parent_blockhash); + std::ptr::addr_of_mut!((*ptr).total_points).write(self.total_points); + std::ptr::addr_of_mut!((*ptr).total_rewards).write(self.total_rewards); + std::ptr::addr_of_mut!((*ptr).distributed_rewards).write(self.distributed_rewards); + std::ptr::addr_of_mut!((*ptr).active).write(self.active); + value.assume_init() + } + } +} + impl EpochRewards { pub fn distribute(&mut self, amount: u64) { assert!(self.distributed_rewards.saturating_add(amount) <= self.total_rewards); diff --git a/sdk/program/src/epoch_schedule.rs b/sdk/program-core/src/epoch_schedule.rs similarity index 90% rename from sdk/program/src/epoch_schedule.rs rename to sdk/program-core/src/epoch_schedule.rs index 2e1398d86b9050..8283f0226aa2cb 100644 --- a/sdk/program/src/epoch_schedule.rs +++ b/sdk/program-core/src/epoch_schedule.rs @@ -12,7 +12,6 @@ //! epochs increasing in slots until they last for [`DEFAULT_SLOTS_PER_EPOCH`]. pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH}; -use solana_sdk_macro::CloneZeroed; /// The default number of slots before an epoch starts to calculate the leader schedule. pub const DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET: u64 = DEFAULT_SLOTS_PER_EPOCH; @@ -30,8 +29,12 @@ pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32; #[repr(C)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, CloneZeroed, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Debug, PartialEq, Eq)] pub struct EpochSchedule { /// The maximum number of slots in each epoch. pub slots_per_epoch: u64, @@ -54,6 +57,26 @@ pub struct EpochSchedule { pub first_normal_slot: Slot, } +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for EpochSchedule { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).slots_per_epoch).write(self.slots_per_epoch); + std::ptr::addr_of_mut!((*ptr).leader_schedule_slot_offset) + .write(self.leader_schedule_slot_offset); + std::ptr::addr_of_mut!((*ptr).warmup).write(self.warmup); + std::ptr::addr_of_mut!((*ptr).first_normal_epoch).write(self.first_normal_epoch); + std::ptr::addr_of_mut!((*ptr).first_normal_slot).write(self.first_normal_slot); + value.assume_init() + } + } +} + impl Default for EpochSchedule { fn default() -> Self { Self::custom( diff --git a/sdk/program/src/example_mocks.rs b/sdk/program-core/src/example_mocks.rs similarity index 85% rename from sdk/program/src/example_mocks.rs rename to sdk/program-core/src/example_mocks.rs index b528812e36f6b3..c17ccfa532ffdb 100644 --- a/sdk/program/src/example_mocks.rs +++ b/sdk/program-core/src/example_mocks.rs @@ -78,9 +78,20 @@ pub mod solana_rpc_client { pub mod solana_rpc_client_api { pub mod client_error { - #[derive(thiserror::Error, Debug)] - #[error("mock-error")] + use core::fmt; + + #[derive(Debug)] pub struct ClientError; + impl std::error::Error for ClientError {} + + impl fmt::Display for ClientError { + #[allow(clippy::used_underscore_binding)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[allow(unused_variables, deprecated)] + let Self {} = self; + f.write_str("mock-error") + } + } pub type Result = std::result::Result; } } @@ -89,12 +100,23 @@ pub mod solana_rpc_client_nonce_utils { use { super::solana_sdk::{account::ReadableAccount, account_utils::StateMut, pubkey::Pubkey}, crate::nonce::state::{Data, DurableNonce, Versions}, + core::fmt, }; - #[derive(thiserror::Error, Debug)] - #[error("mock-error")] + #[derive(Debug)] pub struct Error; + impl std::error::Error for Error {} + + impl fmt::Display for Error { + #[allow(clippy::used_underscore_binding)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[allow(unused_variables, deprecated)] + let Self {} = self; + f.write_str("mock-error") + } + } + pub fn data_from_account>( _account: &T, ) -> Result { @@ -188,11 +210,21 @@ pub mod solana_sdk { } pub mod signer { - use thiserror::Error; + use core::fmt; - #[derive(Error, Debug)] - #[error("mock-error")] + #[derive(Debug)] pub struct SignerError; + + impl std::error::Error for SignerError {} + + impl fmt::Display for SignerError { + #[allow(clippy::used_underscore_binding)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[allow(unused_variables, deprecated)] + let Self {} = self; + f.write_str("mock-error") + } + } } pub mod transaction { @@ -204,7 +236,6 @@ pub mod solana_sdk { message::{Message, VersionedMessage}, pubkey::Pubkey, }, - serde_derive::Serialize, }; pub struct VersionedTransaction { @@ -224,7 +255,7 @@ pub mod solana_sdk { } } - #[derive(Serialize)] + #[cfg_attr(feature = "serde", derive(serde_derive::Serialize))] pub struct Transaction { pub message: Message, } diff --git a/sdk/program/src/fee_calculator.rs b/sdk/program-core/src/fee_calculator.rs similarity index 93% rename from sdk/program/src/fee_calculator.rs rename to sdk/program-core/src/fee_calculator.rs index 5d753e4acaed3a..0bb0f5e38d91cd 100644 --- a/sdk/program/src/fee_calculator.rs +++ b/sdk/program-core/src/fee_calculator.rs @@ -1,12 +1,16 @@ //! Calculation of transaction fees. #![allow(clippy::arithmetic_side_effects)] -use {crate::clock::DEFAULT_MS_PER_SLOT, log::*}; +use crate::clock::DEFAULT_MS_PER_SLOT; #[repr(C)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Default, PartialEq, Eq, Clone, Copy, Debug)] pub struct FeeCalculator { /// The current cost of a signature. /// @@ -24,12 +28,16 @@ impl FeeCalculator { } #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(PartialEq, Eq, Clone, Debug)] pub struct FeeRateGovernor { // The current cost of a signature This amount may increase/decrease over time based on // cluster processing load. - #[serde(skip)] + #[cfg_attr(feature = "serde", serde(skip))] pub lamports_per_signature: u64, // The target cost of a signature when the cluster is operating around target_signatures_per_slot @@ -100,7 +108,8 @@ impl FeeRateGovernor { / me.target_signatures_per_slot, )); - trace!( + #[cfg(feature = "log")] + log::trace!( "desired_lamports_per_signature: {}", desired_lamports_per_signature ); @@ -116,7 +125,8 @@ impl FeeRateGovernor { let gap_adjust = std::cmp::max(1, me.target_lamports_per_signature / 20) as i64 * gap.signum(); - trace!( + #[cfg(feature = "log")] + log::trace!( "lamports_per_signature gap is {}, adjusting by {}", gap, gap_adjust @@ -134,7 +144,8 @@ impl FeeRateGovernor { me.min_lamports_per_signature = me.target_lamports_per_signature; me.max_lamports_per_signature = me.target_lamports_per_signature; } - debug!( + #[cfg(feature = "log")] + log::debug!( "new_derived(): lamports_per_signature: {}", me.lamports_per_signature ); @@ -162,7 +173,7 @@ impl FeeRateGovernor { #[cfg(test)] mod tests { - use super::*; + use {super::*, log::info}; #[test] fn test_fee_rate_governor_burn() { diff --git a/sdk/program/src/hash.rs b/sdk/program-core/src/hash.rs similarity index 85% rename from sdk/program/src/hash.rs rename to sdk/program-core/src/hash.rs index 27967c850376bb..53d9cd600ddf68 100644 --- a/sdk/program/src/hash.rs +++ b/sdk/program-core/src/hash.rs @@ -3,16 +3,17 @@ //! [SHA-256]: https://en.wikipedia.org/wiki/SHA-2 //! [`Hash`]: struct@Hash -#[cfg(target_arch = "wasm32")] -use crate::wasm_bindgen; #[cfg(feature = "borsh")] use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +#[cfg(feature = "bytemuck")] +use bytemuck_derive::{Pod, Zeroable}; +#[cfg(feature = "sha2")] +use sha2::{Digest, Sha256}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::wasm_bindgen; use { - bytemuck_derive::{Pod, Zeroable}, - sha2::{Digest, Sha256}, solana_sanitize::Sanitize, std::{convert::TryFrom, fmt, mem, str::FromStr}, - thiserror::Error, }; /// Size of a hash in bytes. @@ -37,28 +38,19 @@ const MAX_BASE58_LEN: usize = 44; derive(BorshSerialize, BorshDeserialize, BorshSchema), borsh(crate = "borsh") )] -#[derive( - Serialize, - Deserialize, - Clone, - Copy, - Default, - Eq, - PartialEq, - Ord, - PartialOrd, - Hash, - Pod, - Zeroable, -)] +#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Hash(pub(crate) [u8; HASH_BYTES]); +#[cfg(feature = "sha2")] #[derive(Clone, Default)] pub struct Hasher { hasher: Sha256, } +#[cfg(feature = "sha2")] impl Hasher { pub fn hash(&mut self, val: &[u8]) { self.hasher.update(val); @@ -99,14 +91,23 @@ impl fmt::Display for Hash { } } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ParseHashError { - #[error("string decoded to wrong size for hash")] WrongSize, - #[error("failed to decoded string to hash")] Invalid, } +impl std::error::Error for ParseHashError {} + +impl fmt::Display for ParseHashError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseHashError::WrongSize => f.write_str("string decoded to wrong size for hash"), + ParseHashError::Invalid => f.write_str("failed to decoded string to hash"), + } + } +} + impl FromStr for Hash { type Err = ParseHashError; @@ -150,6 +151,7 @@ impl Hash { } } +#[cfg(any(feature = "sha2", target_os = "solana"))] /// Return a Sha256 hash for the given data. pub fn hashv(vals: &[&[u8]]) -> Hash { // Perform the calculation inline, calling this from within a program is @@ -175,11 +177,13 @@ pub fn hashv(vals: &[&[u8]]) -> Hash { } } +#[cfg(any(feature = "sha2", target_os = "solana"))] /// Return a Sha256 hash for the given data. pub fn hash(val: &[u8]) -> Hash { hashv(&[val]) } +#[cfg(any(feature = "sha2", target_os = "solana"))] /// Return the hash of the given hash extended with the given value. pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash { let mut hash_data = id.as_ref().to_vec(); diff --git a/sdk/program/src/instruction.rs b/sdk/program-core/src/instruction.rs similarity index 72% rename from sdk/program/src/instruction.rs rename to sdk/program-core/src/instruction.rs index 2a686c75dec2e6..61ec755ab89a82 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program-core/src/instruction.rs @@ -13,13 +13,17 @@ #![allow(clippy::arithmetic_side_effects)] -#[cfg(target_arch = "wasm32")] -use crate::wasm_bindgen; +#[cfg(feature = "bincode")] +use bincode::serialize; #[cfg(feature = "borsh")] use borsh::BorshSerialize; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::wasm_bindgen; +use {crate::pubkey::Pubkey, core::fmt, solana_sanitize::Sanitize}; +#[cfg(feature = "serde")] use { - crate::pubkey::Pubkey, bincode::serialize, serde::Serialize, solana_sanitize::Sanitize, - solana_short_vec as short_vec, thiserror::Error, + serde_derive::{Deserialize, Serialize}, + solana_short_vec as short_vec, }; /// Reasons the runtime might have rejected an instruction. @@ -30,189 +34,146 @@ use { /// dangerous to include error strings from 3rd party crates because they could /// change at any time and changes to them are difficult to detect. #[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))] -#[derive(Serialize, Deserialize, Debug, Error, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum InstructionError { /// Deprecated! Use CustomError instead! /// The program instruction returned an error - #[error("generic instruction error")] GenericError, /// The arguments provided to a program were invalid - #[error("invalid program argument")] InvalidArgument, /// An instruction's data contents were invalid - #[error("invalid instruction data")] InvalidInstructionData, /// An account's data contents was invalid - #[error("invalid account data for instruction")] InvalidAccountData, /// An account's data was too small - #[error("account data too small for instruction")] AccountDataTooSmall, /// An account's balance was too small to complete the instruction - #[error("insufficient funds for instruction")] InsufficientFunds, /// The account did not have the expected program id - #[error("incorrect program id for instruction")] IncorrectProgramId, /// A signature was required but not found - #[error("missing required signature for instruction")] MissingRequiredSignature, /// An initialize instruction was sent to an account that has already been initialized. - #[error("instruction requires an uninitialized account")] AccountAlreadyInitialized, /// An attempt to operate on an account that hasn't been initialized. - #[error("instruction requires an initialized account")] UninitializedAccount, /// Program's instruction lamport balance does not equal the balance after the instruction - #[error("sum of account balances before and after instruction do not match")] UnbalancedInstruction, /// Program illegally modified an account's program id - #[error("instruction illegally modified the program id of an account")] ModifiedProgramId, /// Program spent the lamports of an account that doesn't belong to it - #[error("instruction spent from the balance of an account it does not own")] ExternalAccountLamportSpend, /// Program modified the data of an account that doesn't belong to it - #[error("instruction modified data of an account it does not own")] ExternalAccountDataModified, /// Read-only account's lamports modified - #[error("instruction changed the balance of a read-only account")] ReadonlyLamportChange, /// Read-only account's data was modified - #[error("instruction modified data of a read-only account")] ReadonlyDataModified, /// An account was referenced more than once in a single instruction // Deprecated, instructions can now contain duplicate accounts - #[error("instruction contains duplicate accounts")] DuplicateAccountIndex, /// Executable bit on account changed, but shouldn't have - #[error("instruction changed executable bit of an account")] ExecutableModified, /// Rent_epoch account changed, but shouldn't have - #[error("instruction modified rent epoch of an account")] RentEpochModified, /// The instruction expected additional account keys - #[error("insufficient account keys for instruction")] NotEnoughAccountKeys, /// Program other than the account's owner changed the size of the account data - #[error("program other than the account's owner changed the size of the account data")] AccountDataSizeChanged, /// The instruction expected an executable account - #[error("instruction expected an executable account")] AccountNotExecutable, /// Failed to borrow a reference to account data, already borrowed - #[error("instruction tries to borrow reference for an account which is already borrowed")] AccountBorrowFailed, /// Account data has an outstanding reference after a program's execution - #[error("instruction left account with an outstanding borrowed reference")] AccountBorrowOutstanding, /// The same account was multiply passed to an on-chain program's entrypoint, but the program /// modified them differently. A program can only modify one instance of the account because /// the runtime cannot determine which changes to pick or how to merge them if both are modified - #[error("instruction modifications of multiply-passed account differ")] DuplicateAccountOutOfSync, /// Allows on-chain programs to implement program-specific error types and see them returned /// by the Solana runtime. A program-specific error may be any type that is represented as /// or serialized to a u32 integer. - #[error("custom program error: {0:#x}")] Custom(u32), /// The return value from the program was invalid. Valid errors are either a defined builtin /// error value or a user-defined error in the lower 32 bits. - #[error("program returned invalid error code")] InvalidError, /// Executable account's data was modified - #[error("instruction changed executable accounts data")] ExecutableDataModified, /// Executable account's lamports modified - #[error("instruction changed the balance of an executable account")] ExecutableLamportChange, /// Executable accounts must be rent exempt - #[error("executable accounts must be rent exempt")] ExecutableAccountNotRentExempt, /// Unsupported program id - #[error("Unsupported program id")] UnsupportedProgramId, /// Cross-program invocation call depth too deep - #[error("Cross-program invocation call depth too deep")] CallDepth, /// An account required by the instruction is missing - #[error("An account required by the instruction is missing")] MissingAccount, /// Cross-program invocation reentrancy not allowed for this instruction - #[error("Cross-program invocation reentrancy not allowed for this instruction")] ReentrancyNotAllowed, /// Length of the seed is too long for address generation - #[error("Length of the seed is too long for address generation")] MaxSeedLengthExceeded, /// Provided seeds do not result in a valid address - #[error("Provided seeds do not result in a valid address")] InvalidSeeds, /// Failed to reallocate account data of this length - #[error("Failed to reallocate account data")] InvalidRealloc, /// Computational budget exceeded - #[error("Computational budget exceeded")] ComputationalBudgetExceeded, /// Cross-program invocation with unauthorized signer or writable account - #[error("Cross-program invocation with unauthorized signer or writable account")] PrivilegeEscalation, /// Failed to create program execution environment - #[error("Failed to create program execution environment")] ProgramEnvironmentSetupFailure, /// Program failed to complete - #[error("Program failed to complete")] ProgramFailedToComplete, /// Program failed to compile - #[error("Program failed to compile")] ProgramFailedToCompile, /// Account is immutable - #[error("Account is immutable")] Immutable, /// Incorrect authority provided - #[error("Incorrect authority provided")] IncorrectAuthority, /// Failed to serialize or deserialize account data @@ -224,48 +185,179 @@ pub enum InstructionError { /// Borsh versions. Only programs can use this error because they are /// consistent across Solana software versions. /// - #[error("Failed to serialize or deserialize account data: {0}")] BorshIoError(String), /// An account does not have enough lamports to be rent-exempt - #[error("An account does not have enough lamports to be rent-exempt")] AccountNotRentExempt, /// Invalid account owner - #[error("Invalid account owner")] InvalidAccountOwner, /// Program arithmetic overflowed - #[error("Program arithmetic overflowed")] ArithmeticOverflow, /// Unsupported sysvar - #[error("Unsupported sysvar")] UnsupportedSysvar, /// Illegal account owner - #[error("Provided owner is not allowed")] IllegalOwner, /// Accounts data allocations exceeded the maximum allowed per transaction - #[error("Accounts data allocations exceeded the maximum allowed per transaction")] MaxAccountsDataAllocationsExceeded, /// Max accounts exceeded - #[error("Max accounts exceeded")] MaxAccountsExceeded, /// Max instruction trace length exceeded - #[error("Max instruction trace length exceeded")] MaxInstructionTraceLengthExceeded, /// Builtin programs must consume compute units - #[error("Builtin programs must consume compute units")] BuiltinProgramsMustConsumeComputeUnits, // Note: For any new error added here an equivalent ProgramError and its // conversions must also be added } +impl std::error::Error for InstructionError {} + +impl fmt::Display for InstructionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InstructionError::GenericError => f.write_str("generic instruction error"), + InstructionError::InvalidArgument => f.write_str("invalid program argument"), + InstructionError::InvalidInstructionData => f.write_str("invalid instruction data"), + InstructionError::InvalidAccountData => { + f.write_str("invalid account data for instruction") + } + InstructionError::AccountDataTooSmall => { + f.write_str("account data too small for instruction") + } + InstructionError::InsufficientFunds => { + f.write_str("insufficient funds for instruction") + } + InstructionError::IncorrectProgramId => { + f.write_str("incorrect program id for instruction") + } + InstructionError::MissingRequiredSignature => { + f.write_str("missing required signature for instruction") + } + InstructionError::AccountAlreadyInitialized => { + f.write_str("instruction requires an uninitialized account") + } + InstructionError::UninitializedAccount => { + f.write_str("instruction requires an initialized account") + } + InstructionError::UnbalancedInstruction => { + f.write_str("sum of account balances before and after instruction do not match") + } + InstructionError::ModifiedProgramId => { + f.write_str("instruction illegally modified the program id of an account") + } + InstructionError::ExternalAccountLamportSpend => { + f.write_str("instruction spent from the balance of an account it does not own") + } + InstructionError::ExternalAccountDataModified => { + f.write_str("instruction modified data of an account it does not own") + } + InstructionError::ReadonlyLamportChange => { + f.write_str("instruction changed the balance of a read-only account") + } + InstructionError::ReadonlyDataModified => { + f.write_str("instruction modified data of a read-only account") + } + InstructionError::DuplicateAccountIndex => { + f.write_str("instruction contains duplicate accounts") + } + InstructionError::ExecutableModified => { + f.write_str("instruction changed executable bit of an account") + } + InstructionError::RentEpochModified => { + f.write_str("instruction modified rent epoch of an account") + } + InstructionError::NotEnoughAccountKeys => { + f.write_str("insufficient account keys for instruction") + } + InstructionError::AccountDataSizeChanged => f.write_str( + "program other than the account's owner changed the size of the account data", + ), + InstructionError::AccountNotExecutable => { + f.write_str("instruction expected an executable account") + } + InstructionError::AccountBorrowFailed => f.write_str( + "instruction tries to borrow reference for an account which is already borrowed", + ), + InstructionError::AccountBorrowOutstanding => { + f.write_str("instruction left account with an outstanding borrowed reference") + } + InstructionError::DuplicateAccountOutOfSync => { + f.write_str("instruction modifications of multiply-passed account differ") + } + InstructionError::Custom(num) => { + write!(f, "custom program error: {num:#x}") + } + InstructionError::InvalidError => f.write_str("program returned invalid error code"), + InstructionError::ExecutableDataModified => { + f.write_str("instruction changed executable accounts data") + } + InstructionError::ExecutableLamportChange => { + f.write_str("instruction changed the balance of an executable account") + } + InstructionError::ExecutableAccountNotRentExempt => { + f.write_str("executable accounts must be rent exempt") + } + InstructionError::UnsupportedProgramId => f.write_str("Unsupported program id"), + InstructionError::CallDepth => { + f.write_str("Cross-program invocation call depth too deep") + } + InstructionError::MissingAccount => { + f.write_str("An account required by the instruction is missing") + } + InstructionError::ReentrancyNotAllowed => { + f.write_str("Cross-program invocation reentrancy not allowed for this instruction") + } + InstructionError::MaxSeedLengthExceeded => { + f.write_str("Length of the seed is too long for address generation") + } + InstructionError::InvalidSeeds => { + f.write_str("Provided seeds do not result in a valid address") + } + InstructionError::InvalidRealloc => f.write_str("Failed to reallocate account data"), + InstructionError::ComputationalBudgetExceeded => { + f.write_str("Computational budget exceeded") + } + InstructionError::PrivilegeEscalation => { + f.write_str("Cross-program invocation with unauthorized signer or writable account") + } + InstructionError::ProgramEnvironmentSetupFailure => { + f.write_str("Failed to create program execution environment") + } + InstructionError::ProgramFailedToComplete => f.write_str("Program failed to complete"), + InstructionError::ProgramFailedToCompile => f.write_str("Program failed to compile"), + InstructionError::Immutable => f.write_str("Account is immutable"), + InstructionError::IncorrectAuthority => f.write_str("Incorrect authority provided"), + InstructionError::BorshIoError(s) => { + write!(f, "Failed to serialize or deserialize account data: {s}",) + } + InstructionError::AccountNotRentExempt => { + f.write_str("An account does not have enough lamports to be rent-exempt") + } + InstructionError::InvalidAccountOwner => f.write_str("Invalid account owner"), + InstructionError::ArithmeticOverflow => f.write_str("Program arithmetic overflowed"), + InstructionError::UnsupportedSysvar => f.write_str("Unsupported sysvar"), + InstructionError::IllegalOwner => f.write_str("Provided owner is not allowed"), + InstructionError::MaxAccountsDataAllocationsExceeded => f.write_str( + "Accounts data allocations exceeded the maximum allowed per transaction", + ), + InstructionError::MaxAccountsExceeded => f.write_str("Max accounts exceeded"), + InstructionError::MaxInstructionTraceLengthExceeded => { + f.write_str("Max instruction trace length exceeded") + } + InstructionError::BuiltinProgramsMustConsumeComputeUnits => { + f.write_str("Builtin programs must consume compute units") + } + } + } +} + /// A directive for a single invocation of a Solana program. /// /// An instruction specifies which program it is calling, which accounts it may @@ -326,7 +418,8 @@ pub enum InstructionError { /// should be specified as signers during `Instruction` construction. The /// program must still validate during execution that the account is a signer. #[cfg(not(target_arch = "wasm32"))] -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Instruction { /// Pubkey of the program that executes this instruction. pub program_id: Pubkey, @@ -369,7 +462,7 @@ impl Instruction { /// # Examples /// /// ``` - /// # use solana_program::{ + /// # use solana_program_core::{ /// # pubkey::Pubkey, /// # instruction::{AccountMeta, Instruction}, /// # }; @@ -422,7 +515,7 @@ impl Instruction { /// # Examples /// /// ``` - /// # use solana_program::{ + /// # use solana_program_core::{ /// # pubkey::Pubkey, /// # instruction::{AccountMeta, Instruction}, /// # }; @@ -451,7 +544,8 @@ impl Instruction { /// ) /// } /// ``` - pub fn new_with_bincode( + #[cfg(feature = "bincode")] + pub fn new_with_bincode( program_id: Pubkey, data: &T, accounts: Vec, @@ -475,7 +569,7 @@ impl Instruction { /// # Examples /// /// ``` - /// # use solana_program::{ + /// # use solana_program_core::{ /// # pubkey::Pubkey, /// # instruction::{AccountMeta, Instruction}, /// # }; @@ -540,7 +634,8 @@ pub fn checked_add(a: u64, b: u64) -> Result { /// a minor hazard: use [`AccountMeta::new_readonly`] to specify that an account /// is not writable. #[repr(C)] -#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct AccountMeta { /// An account's public key. pub pubkey: Pubkey, @@ -556,7 +651,7 @@ impl AccountMeta { /// # Examples /// /// ``` - /// # use solana_program::{ + /// # use solana_program_core::{ /// # pubkey::Pubkey, /// # instruction::{AccountMeta, Instruction}, /// # }; @@ -592,7 +687,7 @@ impl AccountMeta { /// # Examples /// /// ``` - /// # use solana_program::{ + /// # use solana_program_core::{ /// # pubkey::Pubkey, /// # instruction::{AccountMeta, Instruction}, /// # }; @@ -634,23 +729,28 @@ impl AccountMeta { /// /// [`Message`]: crate::message::Message #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct CompiledInstruction { /// Index into the transaction keys array indicating the program account that executes this instruction. pub program_id_index: u8, /// Ordered indices into the transaction keys array indicating which accounts to pass to the program. - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub accounts: Vec, /// The program input data. - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub data: Vec, } impl Sanitize for CompiledInstruction {} impl CompiledInstruction { - pub fn new(program_ids_index: u8, data: &T, accounts: Vec) -> Self { + #[cfg(feature = "bincode")] + pub fn new(program_ids_index: u8, data: &T, accounts: Vec) -> Self { let data = serialize(data).unwrap(); Self { program_id_index: program_ids_index, diff --git a/sdk/program/src/keccak.rs b/sdk/program-core/src/keccak.rs similarity index 83% rename from sdk/program/src/keccak.rs rename to sdk/program-core/src/keccak.rs index 680d9bc63b2a16..fe660846eb3dd7 100644 --- a/sdk/program/src/keccak.rs +++ b/sdk/program-core/src/keccak.rs @@ -4,11 +4,11 @@ #[cfg(feature = "borsh")] use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +#[cfg(feature = "sha3")] +use sha3::{Digest, Keccak256}; use { - sha3::{Digest, Keccak256}, solana_sanitize::Sanitize, std::{convert::TryFrom, fmt, mem, str::FromStr}, - thiserror::Error, }; pub const HASH_BYTES: usize = 32; @@ -20,15 +20,18 @@ const MAX_BASE58_LEN: usize = 44; derive(BorshSerialize, BorshDeserialize, BorshSchema), borsh(crate = "borsh") )] -#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Hash(pub [u8; HASH_BYTES]); +#[cfg(feature = "sha3")] #[derive(Clone, Default)] pub struct Hasher { hasher: Keccak256, } +#[cfg(feature = "sha3")] impl Hasher { pub fn hash(&mut self, val: &[u8]) { self.hasher.update(val); @@ -63,14 +66,23 @@ impl fmt::Display for Hash { } } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ParseHashError { - #[error("string decoded to wrong size for hash")] WrongSize, - #[error("failed to decoded string to hash")] Invalid, } +impl std::error::Error for ParseHashError {} + +impl fmt::Display for ParseHashError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseHashError::WrongSize => f.write_str("string decoded to wrong size for hash"), + ParseHashError::Invalid => f.write_str("failed to decoded string to hash"), + } + } +} + impl FromStr for Hash { type Err = ParseHashError; @@ -114,6 +126,7 @@ impl Hash { } } +#[cfg(feature = "sha3")] /// Return a Keccak256 hash for the given data. pub fn hashv(vals: &[&[u8]]) -> Hash { // Perform the calculation inline, calling this from within a program is @@ -139,11 +152,13 @@ pub fn hashv(vals: &[&[u8]]) -> Hash { } } +#[cfg(feature = "sha3")] /// Return a Keccak256 hash for the given data. pub fn hash(val: &[u8]) -> Hash { hashv(&[val]) } +#[cfg(feature = "sha3")] /// Return the hash of the given hash extended with the given value. pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash { let mut hash_data = id.as_ref().to_vec(); diff --git a/sdk/program/src/lamports.rs b/sdk/program-core/src/lamports.rs similarity index 53% rename from sdk/program/src/lamports.rs rename to sdk/program-core/src/lamports.rs index 91102899e029b5..4aa633b07eed2b 100644 --- a/sdk/program/src/lamports.rs +++ b/sdk/program-core/src/lamports.rs @@ -1,18 +1,27 @@ //! Defines the [`LamportsError`] type. -use {crate::instruction::InstructionError, thiserror::Error}; +use {crate::instruction::InstructionError, core::fmt}; -#[derive(Debug, Error)] +#[derive(Debug)] pub enum LamportsError { /// arithmetic underflowed - #[error("Arithmetic underflowed")] ArithmeticUnderflow, /// arithmetic overflowed - #[error("Arithmetic overflowed")] ArithmeticOverflow, } +impl std::error::Error for LamportsError {} + +impl fmt::Display for LamportsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + LamportsError::ArithmeticUnderflow => f.write_str("Arithmetic underflowed"), + LamportsError::ArithmeticOverflow => f.write_str("Arithmetic overflowed"), + } + } +} + impl From for InstructionError { fn from(error: LamportsError) -> Self { match error { diff --git a/sdk/program-core/src/last_restart_slot.rs b/sdk/program-core/src/last_restart_slot.rs new file mode 100644 index 00000000000000..c2ff0af9668e67 --- /dev/null +++ b/sdk/program-core/src/last_restart_slot.rs @@ -0,0 +1,26 @@ +//! Information about the last restart slot (hard fork). + +use crate::clock::Slot; + +#[repr(C)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Default)] +pub struct LastRestartSlot { + /// The last restart `Slot`. + pub last_restart_slot: Slot, +} + +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for LastRestartSlot { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).last_restart_slot).write(self.last_restart_slot); + value.assume_init() + } + } +} diff --git a/sdk/program-core/src/lib.rs b/sdk/program-core/src/lib.rs new file mode 100644 index 00000000000000..fba603c06ac337 --- /dev/null +++ b/sdk/program-core/src/lib.rs @@ -0,0 +1,76 @@ +#![allow(incomplete_features)] +#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))] + +// Allows macro expansion of `use ::solana_program_core::*` to work within this crate +extern crate self as solana_program_core; + +pub mod account_info; +pub mod address_lookup_table; +pub mod bpf_loader; +pub mod bpf_loader_deprecated; +pub mod bpf_loader_upgradeable; +pub mod clock; +pub mod compute_units; +pub mod debug_account_data; +pub mod ed25519_program; +pub mod entrypoint; +pub mod epoch_rewards; +pub mod epoch_schedule; +pub mod fee_calculator; +pub mod hash; +pub mod instruction; +pub mod keccak; +pub mod lamports; +pub mod last_restart_slot; +pub mod loader_upgradeable_instruction; +pub mod log; +pub mod message; +pub mod nonce; +pub mod program; +pub mod program_error; +pub mod program_option; +pub mod program_pack; +pub mod program_stubs; +#[cfg(feature = "bincode")] +pub mod program_utils; +pub mod pubkey; +pub mod rent; +pub mod secp256k1_program; +pub mod serialize_utils; +pub mod slot_hashes; +#[cfg(feature = "bv")] +pub mod slot_history; +pub mod stable_layout; +pub mod stake_history; +pub mod syscalls; +pub mod system_instruction; +pub mod system_program; +pub mod sysvar; +pub mod wasm; + +/// The [config native program][np]. +/// +/// [np]: https://docs.solanalabs.com/runtime/programs#config-program +pub mod config { + pub mod program { + crate::declare_id!("Config1111111111111111111111111111111111111"); + } +} + +pub use solana_msg::msg; +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde_derive; + +#[cfg_attr(feature = "frozen-abi", macro_use)] +#[cfg(feature = "frozen-abi")] +extern crate solana_frozen_abi_macro; + +// This module is purposefully listed after all other exports: because of an +// interaction within rustdoc between the reexports inside this module of +// `solana_program`'s top-level modules, and `solana_sdk`'s glob re-export of +// `solana_program`'s top-level modules, if this module is not lexically last +// rustdoc fails to generate documentation for the re-exports within +// `solana_sdk`. +#[cfg(not(target_os = "solana"))] +pub mod example_mocks; diff --git a/sdk/program/src/loader_upgradeable_instruction.rs b/sdk/program-core/src/loader_upgradeable_instruction.rs similarity index 95% rename from sdk/program/src/loader_upgradeable_instruction.rs rename to sdk/program-core/src/loader_upgradeable_instruction.rs index 1d2a9a8f9bfad9..2ba8e2f53ef92c 100644 --- a/sdk/program/src/loader_upgradeable_instruction.rs +++ b/sdk/program-core/src/loader_upgradeable_instruction.rs @@ -3,7 +3,8 @@ //! [ubpfl]: crate::bpf_loader_upgradeable #[repr(u8)] -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum UpgradeableLoaderInstruction { /// Initialize a Buffer account. /// @@ -31,7 +32,7 @@ pub enum UpgradeableLoaderInstruction { /// Offset at which to write the given bytes. offset: u32, /// Serialized program data - #[serde(with = "serde_bytes")] + #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] bytes: Vec, }, @@ -49,8 +50,8 @@ pub enum UpgradeableLoaderInstruction { /// follows: /// /// ``` - /// # use solana_program::pubkey::Pubkey; - /// # use solana_program::bpf_loader_upgradeable; + /// # use solana_program_core::pubkey::Pubkey; + /// # use solana_program_core::bpf_loader_upgradeable; /// # let program_address = &[]; /// let (program_data_address, _) = Pubkey::find_program_address( /// &[program_address], diff --git a/sdk/program/src/log.rs b/sdk/program-core/src/log.rs similarity index 99% rename from sdk/program/src/log.rs rename to sdk/program-core/src/log.rs index 049e286c2a624a..c5222280122c9d 100644 --- a/sdk/program/src/log.rs +++ b/sdk/program-core/src/log.rs @@ -48,6 +48,7 @@ pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5); } +#[cfg(feature = "base64")] /// Print some slices as base64. pub fn sol_log_data(data: &[&[u8]]) { #[cfg(target_os = "solana")] diff --git a/sdk/program/src/message/account_keys.rs b/sdk/program-core/src/message/account_keys.rs similarity index 100% rename from sdk/program/src/message/account_keys.rs rename to sdk/program-core/src/message/account_keys.rs diff --git a/sdk/program/src/message/address_loader.rs b/sdk/program-core/src/message/address_loader.rs similarity index 51% rename from sdk/program/src/message/address_loader.rs rename to sdk/program-core/src/message/address_loader.rs index 83e514cd0bd63b..3b15910dc13e00 100644 --- a/sdk/program/src/message/address_loader.rs +++ b/sdk/program-core/src/message/address_loader.rs @@ -1,35 +1,56 @@ use { super::v0::{LoadedAddresses, MessageAddressTableLookup}, - thiserror::Error, + core::fmt, }; -#[derive(Debug, Error, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum AddressLoaderError { /// Address loading from lookup tables is disabled - #[error("Address loading from lookup tables is disabled")] Disabled, /// Failed to load slot hashes sysvar - #[error("Failed to load slot hashes sysvar")] SlotHashesSysvarNotFound, /// Attempted to lookup addresses from a table that does not exist - #[error("Attempted to lookup addresses from a table that does not exist")] LookupTableAccountNotFound, /// Attempted to lookup addresses from an account owned by the wrong program - #[error("Attempted to lookup addresses from an account owned by the wrong program")] InvalidAccountOwner, /// Attempted to lookup addresses from an invalid account - #[error("Attempted to lookup addresses from an invalid account")] InvalidAccountData, /// Address lookup contains an invalid index - #[error("Address lookup contains an invalid index")] InvalidLookupIndex, } +impl std::error::Error for AddressLoaderError {} + +impl fmt::Display for AddressLoaderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AddressLoaderError::Disabled => { + f.write_str("Address loading from lookup tables is disabled") + } + AddressLoaderError::SlotHashesSysvarNotFound => { + f.write_str("Failed to load slot hashes sysvar") + } + AddressLoaderError::LookupTableAccountNotFound => { + f.write_str("Attempted to lookup addresses from a table that does not exist") + } + AddressLoaderError::InvalidAccountOwner => f.write_str( + "Attempted to lookup addresses from an account owned by the wrong program", + ), + AddressLoaderError::InvalidAccountData => { + f.write_str("Attempted to lookup addresses from an invalid account") + } + AddressLoaderError::InvalidLookupIndex => { + f.write_str("Address lookup contains an invalid index") + } + } + } +} + pub trait AddressLoader: Clone { fn load_addresses( self, diff --git a/sdk/program/src/message/compiled_keys.rs b/sdk/program-core/src/message/compiled_keys.rs similarity index 96% rename from sdk/program/src/message/compiled_keys.rs rename to sdk/program-core/src/message/compiled_keys.rs index a9964c33448be2..565de4f5318cd8 100644 --- a/sdk/program/src/message/compiled_keys.rs +++ b/sdk/program-core/src/message/compiled_keys.rs @@ -5,8 +5,8 @@ use crate::{ }; use { crate::{instruction::Instruction, message::MessageHeader, pubkey::Pubkey}, + core::fmt, std::collections::BTreeMap, - thiserror::Error, }; /// A helper struct to collect pubkeys compiled for a set of instructions @@ -17,16 +17,32 @@ pub(crate) struct CompiledKeys { } #[cfg_attr(target_os = "solana", allow(dead_code))] -#[derive(PartialEq, Debug, Error, Eq, Clone)] +#[derive(PartialEq, Debug, Eq, Clone)] pub enum CompileError { - #[error("account index overflowed during compilation")] AccountIndexOverflow, - #[error("address lookup table index overflowed during compilation")] AddressTableLookupIndexOverflow, - #[error("encountered unknown account key `{0}` during instruction compilation")] UnknownInstructionKey(Pubkey), } +impl std::error::Error for CompileError {} + +impl fmt::Display for CompileError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + CompileError::AccountIndexOverflow => { + f.write_str("account index overflowed during compilation") + } + CompileError::AddressTableLookupIndexOverflow => { + f.write_str("address lookup table index overflowed during compilation") + } + CompileError::UnknownInstructionKey(p) => write!( + f, + "encountered unknown account key `{p}` during instruction compilation", + ), + } + } +} + #[derive(Default, Debug, Clone, PartialEq, Eq)] struct CompiledKeyMeta { is_signer: bool, diff --git a/sdk/program/src/message/legacy.rs b/sdk/program-core/src/message/legacy.rs similarity index 95% rename from sdk/program/src/message/legacy.rs rename to sdk/program-core/src/message/legacy.rs index 4c1d4c5a9da418..da9f1e917b3292 100644 --- a/sdk/program/src/message/legacy.rs +++ b/sdk/program-core/src/message/legacy.rs @@ -11,24 +11,36 @@ #![allow(clippy::arithmetic_side_effects)] -#[cfg(target_arch = "wasm32")] -use crate::wasm_bindgen; +#[cfg(feature = "bincode")] +use crate::system_instruction; #[allow(deprecated)] +#[cfg(feature = "bv")] pub use builtins::{BUILTIN_PROGRAMS_KEYS, MAYBE_BUILTIN_KEY_OR_SYSVAR}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::wasm_bindgen; +#[cfg(feature = "bv")] +use { + crate::{bpf_loader, bpf_loader_deprecated, system_program, sysvar}, + std::str::FromStr, +}; use { crate::{ - bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, + bpf_loader_upgradeable, hash::Hash, instruction::{CompiledInstruction, Instruction}, message::{compiled_keys::CompiledKeys, MessageHeader}, pubkey::Pubkey, - system_instruction, system_program, sysvar, }, solana_sanitize::{Sanitize, SanitizeError}, + std::{collections::HashSet, convert::TryFrom}, +}; +#[cfg(feature = "serde")] +use { + serde_derive::{Deserialize, Serialize}, solana_short_vec as short_vec, - std::{collections::HashSet, convert::TryFrom, str::FromStr}, }; +#[cfg(feature = "bv")] #[deprecated( since = "2.0.0", note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys` instead" @@ -75,6 +87,7 @@ mod builtins { note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys::is_reserved` instead" )] #[allow(deprecated)] +#[cfg(feature = "bv")] pub fn is_builtin_key_or_sysvar(key: &Pubkey) -> bool { if MAYBE_BUILTIN_KEY_OR_SYSVAR[key.0[0] as usize] { return sysvar::is_sysvar_id(key) || BUILTIN_PROGRAMS_KEYS.contains(key); @@ -126,15 +139,19 @@ fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec, /// The id of a recent ledger entry. @@ -142,7 +159,7 @@ pub struct Message { /// Programs that will be executed in sequence and committed in one atomic transaction if all /// succeed. - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub instructions: Vec, } @@ -156,21 +173,25 @@ pub struct Message { frozen_abi(digest = "hPGFnQYLp3Ps4XLg7NHHvC7tDfNGMMBCN3x2jGXpF3G"), derive(AbiExample) )] -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Default, Debug, PartialEq, Eq, Clone)] pub struct Message { #[wasm_bindgen(skip)] pub header: MessageHeader, + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] #[wasm_bindgen(skip)] - #[serde(with = "short_vec")] pub account_keys: Vec, /// The id of a recent ledger entry. pub recent_blockhash: Hash, #[wasm_bindgen(skip)] - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub instructions: Vec, } @@ -222,8 +243,8 @@ impl Message { /// [`anyhow`]: https://docs.rs/anyhow /// /// ``` - /// # use solana_program::example_mocks::solana_sdk; - /// # use solana_program::example_mocks::solana_rpc_client; + /// # use solana_program_core::example_mocks::solana_sdk; + /// # use solana_program_core::example_mocks::solana_rpc_client; /// use anyhow::Result; /// use borsh::{BorshSerialize, BorshDeserialize}; /// use solana_rpc_client::rpc_client::RpcClient; @@ -294,8 +315,8 @@ impl Message { /// [`anyhow`]: https://docs.rs/anyhow /// /// ``` - /// # use solana_program::example_mocks::solana_sdk; - /// # use solana_program::example_mocks::solana_rpc_client; + /// # use solana_program_core::example_mocks::solana_sdk; + /// # use solana_program_core::example_mocks::solana_rpc_client; /// use anyhow::Result; /// use borsh::{BorshSerialize, BorshDeserialize}; /// use solana_rpc_client::rpc_client::RpcClient; @@ -391,8 +412,8 @@ impl Message { /// [`anyhow`]: https://docs.rs/anyhow /// /// ``` - /// # use solana_program::example_mocks::solana_sdk; - /// # use solana_program::example_mocks::solana_rpc_client; + /// # use solana_program_core::example_mocks::solana_sdk; + /// # use solana_program_core::example_mocks::solana_rpc_client; /// use anyhow::Result; /// use borsh::{BorshSerialize, BorshDeserialize}; /// use solana_rpc_client::rpc_client::RpcClient; @@ -481,6 +502,7 @@ impl Message { /// # create_offline_initialize_tx(&client, program_id, &payer)?; /// # Ok::<(), anyhow::Error>(()) /// ``` + #[cfg(feature = "bincode")] pub fn new_with_nonce( mut instructions: Vec, payer: Option<&Pubkey>, @@ -514,14 +536,14 @@ impl Message { } /// Compute the blake3 hash of this transaction's message. - #[cfg(not(target_os = "solana"))] + #[cfg(all(feature = "bincode", feature = "blake3", not(target_os = "solana")))] pub fn hash(&self) -> Hash { let message_bytes = self.serialize(); Self::hash_raw_message(&message_bytes) } /// Compute the blake3 hash of a raw transaction message. - #[cfg(not(target_os = "solana"))] + #[cfg(all(not(target_os = "solana"), feature = "blake3"))] pub fn hash_raw_message(message_bytes: &[u8]) -> Hash { use blake3::traits::digest::Digest; let mut hasher = blake3::Hasher::new(); @@ -534,6 +556,7 @@ impl Message { compile_instruction(ix, &self.account_keys) } + #[cfg(feature = "bincode")] pub fn serialize(&self) -> Vec { bincode::serialize(self).unwrap() } @@ -621,6 +644,7 @@ impl Message { /// runtime. #[deprecated(since = "2.0.0", note = "Please use `is_maybe_writable` instead")] #[allow(deprecated)] + #[cfg(feature = "bv")] pub fn is_writable(&self, i: usize) -> bool { (self.is_writable_index(i)) && !is_builtin_key_or_sysvar(&self.account_keys[i]) diff --git a/sdk/program/src/message/mod.rs b/sdk/program-core/src/message/mod.rs similarity index 96% rename from sdk/program/src/message/mod.rs rename to sdk/program-core/src/message/mod.rs index cc7904117e9475..ac9b6acfb677af 100644 --- a/sdk/program/src/message/mod.rs +++ b/sdk/program-core/src/message/mod.rs @@ -92,8 +92,12 @@ pub const MESSAGE_HEADER_LENGTH: usize = 3; /// /// [PoH]: https://docs.solanalabs.com/consensus/synchronization #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, Copy)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] pub struct MessageHeader { /// The number of signatures required for this message to be considered /// valid. The signers of those signatures must match the first diff --git a/sdk/program/src/message/sanitized.rs b/sdk/program-core/src/message/sanitized.rs similarity index 93% rename from sdk/program/src/message/sanitized.rs rename to sdk/program-core/src/message/sanitized.rs index 33b8c3fc3495d7..5d85509106e796 100644 --- a/sdk/program/src/message/sanitized.rs +++ b/sdk/program-core/src/message/sanitized.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "bincode")] +use crate::{ + nonce::NONCED_TX_MARKER_IX_INDEX, system_instruction::SystemInstruction, system_program, +}; use { crate::{ ed25519_program, @@ -9,16 +13,13 @@ use { AccountKeys, AddressLoader, AddressLoaderError, MessageHeader, SanitizedVersionedMessage, VersionedMessage, }, - nonce::NONCED_TX_MARKER_IX_INDEX, - program_utils::limited_deserialize, pubkey::Pubkey, secp256k1_program, - solana_program::{system_instruction::SystemInstruction, system_program}, sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction}, }, + core::fmt, solana_sanitize::{Sanitize, SanitizeError}, std::{borrow::Cow, collections::HashSet, convert::TryFrom}, - thiserror::Error, }; #[derive(Debug, Clone, Eq, PartialEq)] @@ -80,16 +81,42 @@ pub enum SanitizedMessage { V0(v0::LoadedMessage<'static>), } -#[derive(PartialEq, Debug, Error, Eq, Clone)] +#[derive(PartialEq, Debug, Eq, Clone)] pub enum SanitizeMessageError { - #[error("index out of bounds")] IndexOutOfBounds, - #[error("value out of bounds")] ValueOutOfBounds, - #[error("invalid value")] InvalidValue, - #[error("{0}")] - AddressLoaderError(#[from] AddressLoaderError), + AddressLoaderError(AddressLoaderError), +} + +impl std::error::Error for SanitizeMessageError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + #[allow(deprecated)] + match self { + SanitizeMessageError::IndexOutOfBounds { .. } => None, + SanitizeMessageError::ValueOutOfBounds { .. } => None, + SanitizeMessageError::InvalidValue { .. } => None, + SanitizeMessageError::AddressLoaderError { 0: source, .. } => Some(source), + } + } +} +impl fmt::Display for SanitizeMessageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SanitizeMessageError::IndexOutOfBounds => f.write_str("index out of bounds"), + SanitizeMessageError::ValueOutOfBounds => f.write_str("value out of bounds"), + SanitizeMessageError::InvalidValue => f.write_str("invalid value"), + SanitizeMessageError::AddressLoaderError(e) => { + write!(f, "{e}") + } + } + } +} +impl ::core::convert::From for SanitizeMessageError { + #[allow(deprecated)] + fn from(source: AddressLoaderError) -> Self { + SanitizeMessageError::AddressLoaderError(source) + } } impl From for SanitizeMessageError { @@ -342,6 +369,7 @@ impl SanitizedMessage { }) } + #[cfg(feature = "bincode")] /// If the message uses a durable nonce, return the pubkey of the nonce account pub fn get_durable_nonce(&self) -> Option<&Pubkey> { self.instructions() @@ -354,7 +382,9 @@ impl SanitizedMessage { ) .filter(|ix| { matches!( - limited_deserialize(&ix.data, 4 /* serialized size of AdvanceNonceAccount */), + crate::program_utils::limited_deserialize( + &ix.data, 4 /* serialized size of AdvanceNonceAccount */ + ), Ok(SystemInstruction::AdvanceNonceAccount) ) }) diff --git a/sdk/program/src/message/versions/mod.rs b/sdk/program-core/src/message/versions/mod.rs similarity index 96% rename from sdk/program/src/message/versions/mod.rs rename to sdk/program-core/src/message/versions/mod.rs index 53b95d96acf490..322d94229dc449 100644 --- a/sdk/program/src/message/versions/mod.rs +++ b/sdk/program-core/src/message/versions/mod.rs @@ -5,14 +5,18 @@ use { message::{legacy::Message as LegacyMessage, v0::MessageAddressTableLookup, MessageHeader}, pubkey::Pubkey, }, + solana_sanitize::{Sanitize, SanitizeError}, + std::collections::HashSet, +}; +#[cfg(feature = "serde")] +use { serde::{ de::{self, Deserializer, SeqAccess, Unexpected, Visitor}, ser::{SerializeTuple, Serializer}, }, serde_derive::{Deserialize, Serialize}, - solana_sanitize::{Sanitize, SanitizeError}, solana_short_vec as short_vec, - std::{collections::HashSet, fmt}, + std::fmt, }; mod sanitized; @@ -145,16 +149,19 @@ impl VersionedMessage { } } + #[cfg(feature = "bincode")] pub fn serialize(&self) -> Vec { bincode::serialize(self).unwrap() } + #[cfg(all(feature = "bincode", feature = "blake3"))] /// Compute the blake3 hash of this transaction's message pub fn hash(&self) -> Hash { let message_bytes = self.serialize(); Self::hash_raw_message(&message_bytes) } + #[cfg(feature = "blake3")] /// Compute the blake3 hash of a raw transaction message pub fn hash_raw_message(message_bytes: &[u8]) -> Hash { use blake3::traits::digest::Digest; @@ -171,6 +178,7 @@ impl Default for VersionedMessage { } } +#[cfg(feature = "serde")] impl serde::Serialize for VersionedMessage { fn serialize(&self, serializer: S) -> Result where @@ -192,11 +200,13 @@ impl serde::Serialize for VersionedMessage { } } +#[cfg(feature = "serde")] enum MessagePrefix { Legacy(u8), Versioned(u8), } +#[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for MessagePrefix { fn deserialize(deserializer: D) -> Result where @@ -233,6 +243,7 @@ impl<'de> serde::Deserialize<'de> for MessagePrefix { } } +#[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for VersionedMessage { fn deserialize(deserializer: D) -> Result where @@ -262,10 +273,10 @@ impl<'de> serde::Deserialize<'de> for VersionedMessage { struct RemainingLegacyMessage { pub num_readonly_signed_accounts: u8, pub num_readonly_unsigned_accounts: u8, - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub account_keys: Vec, pub recent_blockhash: Hash, - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub instructions: Vec, } diff --git a/sdk/program/src/message/versions/sanitized.rs b/sdk/program-core/src/message/versions/sanitized.rs similarity index 100% rename from sdk/program/src/message/versions/sanitized.rs rename to sdk/program-core/src/message/versions/sanitized.rs diff --git a/sdk/program/src/message/versions/v0/loaded.rs b/sdk/program-core/src/message/versions/v0/loaded.rs similarity index 99% rename from sdk/program/src/message/versions/v0/loaded.rs rename to sdk/program-core/src/message/versions/v0/loaded.rs index 1825c5e748e3f4..d6caa796ef79d8 100644 --- a/sdk/program/src/message/versions/v0/loaded.rs +++ b/sdk/program-core/src/message/versions/v0/loaded.rs @@ -21,7 +21,8 @@ pub struct LoadedMessage<'a> { /// Collection of addresses loaded from on-chain lookup tables, split /// by readonly and writable. -#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct LoadedAddresses { /// List of addresses for writable loaded accounts pub writable: Vec, diff --git a/sdk/program/src/message/versions/v0/mod.rs b/sdk/program-core/src/message/versions/v0/mod.rs similarity index 96% rename from sdk/program/src/message/versions/v0/mod.rs rename to sdk/program-core/src/message/versions/v0/mod.rs index 9eaad5a9b39944..daeba14458aad9 100644 --- a/sdk/program/src/message/versions/v0/mod.rs +++ b/sdk/program-core/src/message/versions/v0/mod.rs @@ -9,6 +9,8 @@ //! [`v0`]: crate::message::v0 //! [future message format]: https://docs.solanalabs.com/proposals/versioned-transactions +#[cfg(feature = "bincode")] +use crate::message::MESSAGE_VERSION_PREFIX; pub use loaded::*; use { crate::{ @@ -18,30 +20,37 @@ use { instruction::{CompiledInstruction, Instruction}, message::{ compiled_keys::{CompileError, CompiledKeys}, - AccountKeys, MessageHeader, MESSAGE_VERSION_PREFIX, + AccountKeys, MessageHeader, }, pubkey::Pubkey, }, solana_sanitize::SanitizeError, - solana_short_vec as short_vec, std::collections::HashSet, }; - +#[cfg(feature = "serde")] +use { + serde_derive::{Deserialize, Serialize}, + solana_short_vec as short_vec, +}; mod loaded; /// Address table lookups describe an on-chain address lookup table to use /// for loading more readonly and writable accounts in a single tx. #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Default, Debug, PartialEq, Eq, Clone)] pub struct MessageAddressTableLookup { /// Address lookup table account key pub account_key: Pubkey, /// List of indexes used to load writable account addresses - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub writable_indexes: Vec, /// List of indexes used to load readonly account addresses - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub readonly_indexes: Vec, } @@ -54,8 +63,12 @@ pub struct MessageAddressTableLookup { /// /// [`message`]: crate::message #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "camelCase")] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] +#[derive(Default, Debug, PartialEq, Eq, Clone)] pub struct Message { /// The message header, identifying signed and read-only `account_keys`. /// Header values only describe static `account_keys`, they do not describe @@ -63,7 +76,7 @@ pub struct Message { pub header: MessageHeader, /// List of accounts loaded by this transaction. - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub account_keys: Vec, /// The blockhash of a recent block. @@ -82,12 +95,12 @@ pub struct Message { /// 1) message `account_keys` /// 2) ordered list of keys loaded from `writable` lookup table indexes /// 3) ordered list of keys loaded from `readable` lookup table indexes - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub instructions: Vec, /// List of address table lookups used to load additional accounts /// for this transaction. - #[serde(with = "short_vec")] + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] pub address_table_lookups: Vec, } @@ -190,7 +203,7 @@ impl Message { /// [`anyhow`]: https://docs.rs/anyhow /// /// ``` - /// # use solana_program::example_mocks::{ + /// # use solana_program_core::example_mocks::{ /// # solana_rpc_client, /// # solana_sdk, /// # }; @@ -198,7 +211,7 @@ impl Message { /// # use solana_sdk::account::Account; /// use anyhow::Result; /// use solana_rpc_client::rpc_client::RpcClient; - /// use solana_program::address_lookup_table::{self, state::{AddressLookupTable, LookupTableMeta}}; + /// use solana_program_core::address_lookup_table::{self, state::{AddressLookupTable, LookupTableMeta}}; /// use solana_sdk::{ /// address_lookup_table::AddressLookupTableAccount, /// instruction::{AccountMeta, Instruction}, @@ -288,6 +301,7 @@ impl Message { }) } + #[cfg(feature = "bincode")] /// Serialize this message with a version #0 prefix using bincode encoding. pub fn serialize(&self) -> Vec { bincode::serialize(&(MESSAGE_VERSION_PREFIX, self)).unwrap() diff --git a/sdk/program/src/nonce/mod.rs b/sdk/program-core/src/nonce/mod.rs similarity index 100% rename from sdk/program/src/nonce/mod.rs rename to sdk/program-core/src/nonce/mod.rs diff --git a/sdk/program/src/nonce/state/current.rs b/sdk/program-core/src/nonce/state/current.rs similarity index 79% rename from sdk/program/src/nonce/state/current.rs rename to sdk/program-core/src/nonce/state/current.rs index 447cdff97f50bf..c5736eddfb057c 100644 --- a/sdk/program/src/nonce/state/current.rs +++ b/sdk/program-core/src/nonce/state/current.rs @@ -1,21 +1,19 @@ -use { - crate::{ - fee_calculator::FeeCalculator, - hash::{hashv, Hash}, - pubkey::Pubkey, - }, - serde_derive::{Deserialize, Serialize}, -}; +use crate::{fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey}; +#[cfg(feature = "serde")] +use serde_derive::{Deserialize, Serialize}; +#[cfg(any(feature = "sha2", target_os = "solana"))] const DURABLE_NONCE_HASH_PREFIX: &[u8] = "DURABLE_NONCE".as_bytes(); -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct DurableNonce(Hash); /// Initialized data of a durable transaction nonce account. /// /// This is stored within [`State`] for initialized nonce accounts. -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct Data { /// Address of the account that signs transactions using the nonce account. pub authority: Pubkey, @@ -53,8 +51,12 @@ impl Data { } impl DurableNonce { + #[cfg(any(feature = "sha2", target_os = "solana"))] pub fn from_blockhash(blockhash: &Hash) -> Self { - Self(hashv(&[DURABLE_NONCE_HASH_PREFIX, blockhash.as_ref()])) + Self(crate::hash::hashv(&[ + DURABLE_NONCE_HASH_PREFIX, + blockhash.as_ref(), + ])) } /// Hash value used as recent_blockhash field in Transactions. @@ -67,7 +69,8 @@ impl DurableNonce { /// /// When created in memory with [`State::default`] or when deserialized from an /// uninitialized account, a nonce account will be [`State::Uninitialized`]. -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq, Eq, Clone)] pub enum State { #[default] Uninitialized, diff --git a/sdk/program/src/nonce/state/mod.rs b/sdk/program-core/src/nonce/state/mod.rs similarity index 98% rename from sdk/program/src/nonce/state/mod.rs rename to sdk/program-core/src/nonce/state/mod.rs index d55bc9063afcff..cd2ec1abbdf72b 100644 --- a/sdk/program/src/nonce/state/mod.rs +++ b/sdk/program-core/src/nonce/state/mod.rs @@ -4,11 +4,11 @@ mod current; pub use current::{Data, DurableNonce, State}; use { crate::{hash::Hash, pubkey::Pubkey}, - serde_derive::{Deserialize, Serialize}, std::collections::HashSet, }; -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Versions { Legacy(Box), /// Current variants have durable nonce and blockhash domains separated. @@ -52,6 +52,7 @@ impl Versions { } } + #[cfg(any(feature = "sha2", target_os = "solana"))] /// Upgrades legacy nonces out of chain blockhash domains. pub fn upgrade(self) -> Option { match self { diff --git a/sdk/program/src/program.rs b/sdk/program-core/src/program.rs similarity index 99% rename from sdk/program/src/program.rs rename to sdk/program-core/src/program.rs index 27a4a2a8cca957..6d4525fa150c77 100644 --- a/sdk/program/src/program.rs +++ b/sdk/program-core/src/program.rs @@ -91,7 +91,7 @@ use crate::{ /// A simple example of transferring lamports via CPI: /// /// ``` -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -184,7 +184,7 @@ pub fn invoke_unchecked(instruction: &Instruction, account_infos: &[AccountInfo] /// A simple example of creating an account for a PDA: /// /// ``` -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, diff --git a/sdk/program/src/program_error.rs b/sdk/program-core/src/program_error.rs similarity index 78% rename from sdk/program/src/program_error.rs rename to sdk/program-core/src/program_error.rs index f225561a52d389..89dc34e0c9274c 100644 --- a/sdk/program/src/program_error.rs +++ b/sdk/program-core/src/program_error.rs @@ -4,83 +4,126 @@ #[cfg(feature = "borsh")] use borsh::io::Error as BorshIoError; use { - crate::{instruction::InstructionError, msg, pubkey::PubkeyError}, - num_traits::{FromPrimitive, ToPrimitive}, - solana_decode_error::DecodeError, + crate::{instruction::InstructionError, pubkey::PubkeyError}, + core::fmt, std::convert::TryFrom, - thiserror::Error, }; +#[cfg(feature = "num-traits")] +use {num_traits::ToPrimitive, solana_decode_error::DecodeError, solana_msg::msg}; /// Reasons the program may fail -#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ProgramError { /// Allows on-chain programs to implement program-specific error types and see them returned /// by the Solana runtime. A program-specific error may be any type that is represented as /// or serialized to a u32 integer. - #[error("Custom program error: {0:#x}")] Custom(u32), - #[error("The arguments provided to a program instruction were invalid")] InvalidArgument, - #[error("An instruction's data contents was invalid")] InvalidInstructionData, - #[error("An account's data contents was invalid")] InvalidAccountData, - #[error("An account's data was too small")] AccountDataTooSmall, - #[error("An account's balance was too small to complete the instruction")] InsufficientFunds, - #[error("The account did not have the expected program id")] IncorrectProgramId, - #[error("A signature was required but not found")] MissingRequiredSignature, - #[error("An initialize instruction was sent to an account that has already been initialized")] AccountAlreadyInitialized, - #[error("An attempt to operate on an account that hasn't been initialized")] UninitializedAccount, - #[error("The instruction expected additional account keys")] NotEnoughAccountKeys, - #[error("Failed to borrow a reference to account data, already borrowed")] AccountBorrowFailed, - #[error("Length of the seed is too long for address generation")] MaxSeedLengthExceeded, - #[error("Provided seeds do not result in a valid address")] InvalidSeeds, - #[error("IO Error: {0}")] BorshIoError(String), - #[error("An account does not have enough lamports to be rent-exempt")] AccountNotRentExempt, - #[error("Unsupported sysvar")] UnsupportedSysvar, - #[error("Provided owner is not allowed")] IllegalOwner, - #[error("Accounts data allocations exceeded the maximum allowed per transaction")] MaxAccountsDataAllocationsExceeded, - #[error("Account data reallocation was invalid")] InvalidRealloc, - #[error("Instruction trace length exceeded the maximum allowed per transaction")] MaxInstructionTraceLengthExceeded, - #[error("Builtin programs must consume compute units")] BuiltinProgramsMustConsumeComputeUnits, - #[error("Invalid account owner")] InvalidAccountOwner, - #[error("Program arithmetic overflowed")] ArithmeticOverflow, - #[error("Account is immutable")] Immutable, - #[error("Incorrect authority provided")] IncorrectAuthority, } +impl std::error::Error for ProgramError {} + +impl fmt::Display for ProgramError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ProgramError::Custom(num) => write!(f,"Custom program error: {num:#x}"), + ProgramError::InvalidArgument + => f.write_str("The arguments provided to a program instruction were invalid"), + ProgramError::InvalidInstructionData + => f.write_str("An instruction's data contents was invalid"), + ProgramError::InvalidAccountData + => f.write_str("An account's data contents was invalid"), + ProgramError::AccountDataTooSmall + => f.write_str("An account's data was too small"), + ProgramError::InsufficientFunds + => f.write_str("An account's balance was too small to complete the instruction"), + ProgramError::IncorrectProgramId + => f.write_str("The account did not have the expected program id"), + ProgramError::MissingRequiredSignature + => f.write_str("A signature was required but not found"), + ProgramError::AccountAlreadyInitialized + => f.write_str("An initialize instruction was sent to an account that has already been initialized"), + ProgramError::UninitializedAccount + => f.write_str("An attempt to operate on an account that hasn't been initialized"), + ProgramError::NotEnoughAccountKeys + => f.write_str("The instruction expected additional account keys"), + ProgramError::AccountBorrowFailed + => f.write_str("Failed to borrow a reference to account data, already borrowed"), + ProgramError::MaxSeedLengthExceeded + => f.write_str("Length of the seed is too long for address generation"), + ProgramError::InvalidSeeds + => f.write_str("Provided seeds do not result in a valid address"), + ProgramError::BorshIoError(s) => write!(f,"IO Error: {s}"), + ProgramError::AccountNotRentExempt + => f.write_str("An account does not have enough lamports to be rent-exempt"), + ProgramError::UnsupportedSysvar + => f.write_str("Unsupported sysvar"), + ProgramError::IllegalOwner + => f.write_str("Provided owner is not allowed"), + ProgramError::MaxAccountsDataAllocationsExceeded + => f.write_str("Accounts data allocations exceeded the maximum allowed per transaction"), + ProgramError::InvalidRealloc + => f.write_str("Account data reallocation was invalid"), + ProgramError::MaxInstructionTraceLengthExceeded + => f.write_str("Instruction trace length exceeded the maximum allowed per transaction"), + ProgramError::BuiltinProgramsMustConsumeComputeUnits + => f.write_str("Builtin programs must consume compute units"), + ProgramError::InvalidAccountOwner + => f.write_str("Invalid account owner"), + ProgramError::ArithmeticOverflow + => f.write_str("Program arithmetic overflowed"), + ProgramError::Immutable + => f.write_str("Account is immutable"), + ProgramError::IncorrectAuthority => f.write_str("Incorrect authority provided"), + } + } +} + +#[cfg(feature = "num-traits")] pub trait PrintProgramError { fn print(&self) where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive; + E: 'static + + std::error::Error + + DecodeError + + PrintProgramError + + num_traits::FromPrimitive; } +#[cfg(feature = "num-traits")] impl PrintProgramError for ProgramError { fn print(&self) where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, + E: 'static + + std::error::Error + + DecodeError + + PrintProgramError + + num_traits::FromPrimitive, { match self { Self::Custom(error) => { @@ -288,6 +331,7 @@ impl TryFrom for ProgramError { } } +#[cfg(feature = "num-traits")] impl From for InstructionError where T: ToPrimitive, diff --git a/sdk/program/src/program_option.rs b/sdk/program-core/src/program_option.rs similarity index 100% rename from sdk/program/src/program_option.rs rename to sdk/program-core/src/program_option.rs diff --git a/sdk/program/src/program_pack.rs b/sdk/program-core/src/program_pack.rs similarity index 100% rename from sdk/program/src/program_pack.rs rename to sdk/program-core/src/program_pack.rs diff --git a/sdk/program/src/program_stubs.rs b/sdk/program-core/src/program_stubs.rs similarity index 93% rename from sdk/program/src/program_stubs.rs rename to sdk/program-core/src/program_stubs.rs index 77dabd37c3418c..81b5da36b84b96 100644 --- a/sdk/program/src/program_stubs.rs +++ b/sdk/program-core/src/program_stubs.rs @@ -2,12 +2,13 @@ #![cfg(not(target_os = "solana"))] +#[cfg(feature = "base64")] +use base64::{prelude::BASE64_STANDARD, Engine}; use { crate::{ account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, program_error::UNSUPPORTED_SYSVAR, pubkey::Pubkey, }, - base64::{prelude::BASE64_STANDARD, Engine}, solana_program_memory::stubs, std::sync::{Arc, RwLock}, }; @@ -73,6 +74,7 @@ pub trait SyscallStubs: Sync + Send { 0 } /// # Safety + #[cfg(feature = "num-traits")] unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) { stubs::sol_memcpy(dst, src, n) } @@ -92,6 +94,7 @@ pub trait SyscallStubs: Sync + Send { None } fn sol_set_return_data(&self, _data: &[u8]) {} + #[cfg(feature = "base64")] fn sol_log_data(&self, fields: &[&[u8]]) { println!( "data: {}", @@ -155,10 +158,12 @@ pub(crate) fn sol_get_sysvar( .sol_get_sysvar(sysvar_id_addr, var_addr, offset, length) } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_clock_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS.read().unwrap().sol_get_clock_sysvar(var_addr) } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS .read() @@ -166,14 +171,17 @@ pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 { .sol_get_epoch_schedule_sysvar(var_addr) } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr) } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr) } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 { SYSCALL_STUBS .read() @@ -181,7 +189,7 @@ pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 { .sol_get_last_restart_slot(var_addr) } -pub(crate) fn sol_get_epoch_stake(vote_address: *const u8) -> u64 { +pub fn sol_get_epoch_stake(vote_address: *const u8) -> u64 { SYSCALL_STUBS .read() .unwrap() @@ -196,6 +204,7 @@ pub(crate) fn sol_set_return_data(data: &[u8]) { SYSCALL_STUBS.read().unwrap().sol_set_return_data(data) } +#[cfg(feature = "base64")] pub(crate) fn sol_log_data(data: &[&[u8]]) { SYSCALL_STUBS.read().unwrap().sol_log_data(data) } @@ -211,6 +220,7 @@ pub(crate) fn sol_get_stack_height() -> u64 { SYSCALL_STUBS.read().unwrap().sol_get_stack_height() } +#[cfg(feature = "bincode")] pub(crate) fn sol_get_epoch_rewards_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS .read() diff --git a/sdk/program/src/program_utils.rs b/sdk/program-core/src/program_utils.rs similarity index 95% rename from sdk/program/src/program_utils.rs rename to sdk/program-core/src/program_utils.rs index 5308a340adbac4..17871db45256f6 100644 --- a/sdk/program/src/program_utils.rs +++ b/sdk/program-core/src/program_utils.rs @@ -20,7 +20,7 @@ where #[cfg(test)] pub mod tests { - use {super::*, solana_program::system_instruction::SystemInstruction}; + use {super::*, crate::system_instruction::SystemInstruction}; #[test] fn test_limited_deserialize_advance_nonce_account() { diff --git a/sdk/program-core/src/pubkey.rs b/sdk/program-core/src/pubkey.rs new file mode 100644 index 00000000000000..933a3ce0f5e3c2 --- /dev/null +++ b/sdk/program-core/src/pubkey.rs @@ -0,0 +1,1175 @@ +//! Solana account addresses. + +#![allow(clippy::arithmetic_side_effects)] + +#[cfg(any(test, feature = "dev-context-only-utils"))] +use arbitrary::Arbitrary; +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +#[cfg(feature = "bytemuck")] +use bytemuck_derive::{Pod, Zeroable}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::wasm_bindgen; +use { + solana_decode_error::DecodeError, + std::{ + convert::{Infallible, TryFrom}, + fmt, mem, + str::FromStr, + }, +}; + +/// Number of bytes in a pubkey +pub const PUBKEY_BYTES: usize = 32; +/// maximum length of derived `Pubkey` seed +pub const MAX_SEED_LEN: usize = 32; +/// Maximum number of seeds +pub const MAX_SEEDS: usize = 16; +/// Maximum string length of a base58 encoded pubkey +const MAX_BASE58_LEN: usize = 44; + +#[cfg(any(feature = "sha2", target_os = "solana"))] +const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress"; + +#[cfg_attr(feature = "serde", derive(Serialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PubkeyError { + /// Length of the seed is too long for address generation + MaxSeedLengthExceeded, + InvalidSeeds, + IllegalOwner, +} + +impl std::error::Error for PubkeyError {} + +impl fmt::Display for PubkeyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + PubkeyError::MaxSeedLengthExceeded => { + f.write_str("Length of the seed is too long for address generation") + } + PubkeyError::InvalidSeeds => { + f.write_str("Provided seeds do not result in a valid address") + } + PubkeyError::IllegalOwner => f.write_str("Provided owner is not allowed"), + } + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::FromPrimitive for PubkeyError { + #[inline] + fn from_i64(n: i64) -> Option { + if n == PubkeyError::MaxSeedLengthExceeded as i64 { + Some(PubkeyError::MaxSeedLengthExceeded) + } else if n == PubkeyError::InvalidSeeds as i64 { + Some(PubkeyError::InvalidSeeds) + } else if n == PubkeyError::IllegalOwner as i64 { + Some(PubkeyError::IllegalOwner) + } else { + None + } + } + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_i64(n as i64) + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::ToPrimitive for PubkeyError { + #[inline] + fn to_i64(&self) -> Option { + Some(match *self { + PubkeyError::MaxSeedLengthExceeded => PubkeyError::MaxSeedLengthExceeded as i64, + PubkeyError::InvalidSeeds => PubkeyError::InvalidSeeds as i64, + PubkeyError::IllegalOwner => PubkeyError::IllegalOwner as i64, + }) + } + #[inline] + fn to_u64(&self) -> Option { + self.to_i64().map(|x| x as u64) + } +} + +impl DecodeError for PubkeyError { + fn type_of() -> &'static str { + "PubkeyError" + } +} +impl From for PubkeyError { + fn from(error: u64) -> Self { + match error { + 0 => PubkeyError::MaxSeedLengthExceeded, + 1 => PubkeyError::InvalidSeeds, + _ => panic!("Unsupported PubkeyError"), + } + } +} + +/// The address of a [Solana account][acc]. +/// +/// Some account addresses are [ed25519] public keys, with corresponding secret +/// keys that are managed off-chain. Often, though, account addresses do not +/// have corresponding secret keys — as with [_program derived +/// addresses_][pdas] — or the secret key is not relevant to the operation +/// of a program, and may have even been disposed of. As running Solana programs +/// can not safely create or manage secret keys, the full [`Keypair`] is not +/// defined in `solana-program` but in `solana-sdk`. +/// +/// [acc]: https://solana.com/docs/core/accounts +/// [ed25519]: https://ed25519.cr.yp.to/ +/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses +/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] +#[repr(transparent)] +#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] +#[cfg_attr( + feature = "borsh", + derive(BorshSerialize, BorshDeserialize, BorshSchema), + borsh(crate = "borsh") +)] +#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(any(test, feature = "dev-context-only-utils"), derive(Arbitrary))] +pub struct Pubkey(pub(crate) [u8; 32]); + +impl solana_sanitize::Sanitize for Pubkey {} + +#[cfg_attr(feature = "serde", derive(Serialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ParsePubkeyError { + WrongSize, + Invalid, +} + +impl std::error::Error for ParsePubkeyError {} + +impl fmt::Display for ParsePubkeyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParsePubkeyError::WrongSize => f.write_str("String is the wrong size"), + ParsePubkeyError::Invalid => f.write_str("Invalid Base58 string"), + } + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::FromPrimitive for ParsePubkeyError { + #[inline] + fn from_i64(n: i64) -> Option { + if n == ParsePubkeyError::WrongSize as i64 { + Some(ParsePubkeyError::WrongSize) + } else if n == ParsePubkeyError::Invalid as i64 { + Some(ParsePubkeyError::Invalid) + } else { + None + } + } + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_i64(n as i64) + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::ToPrimitive for ParsePubkeyError { + #[inline] + fn to_i64(&self) -> Option { + Some(match *self { + ParsePubkeyError::WrongSize => ParsePubkeyError::WrongSize as i64, + ParsePubkeyError::Invalid => ParsePubkeyError::Invalid as i64, + }) + } + #[inline] + fn to_u64(&self) -> Option { + self.to_i64().map(|x| x as u64) + } +} + +impl From for ParsePubkeyError { + fn from(_: Infallible) -> Self { + unreachable!("Infallible uninhabited"); + } +} + +impl DecodeError for ParsePubkeyError { + fn type_of() -> &'static str { + "ParsePubkeyError" + } +} + +impl FromStr for Pubkey { + type Err = ParsePubkeyError; + + fn from_str(s: &str) -> Result { + if s.len() > MAX_BASE58_LEN { + return Err(ParsePubkeyError::WrongSize); + } + let pubkey_vec = bs58::decode(s) + .into_vec() + .map_err(|_| ParsePubkeyError::Invalid)?; + if pubkey_vec.len() != mem::size_of::() { + Err(ParsePubkeyError::WrongSize) + } else { + Pubkey::try_from(pubkey_vec).map_err(|_| ParsePubkeyError::Invalid) + } + } +} + +impl From<[u8; 32]> for Pubkey { + #[inline] + fn from(from: [u8; 32]) -> Self { + Self(from) + } +} + +impl TryFrom<&[u8]> for Pubkey { + type Error = std::array::TryFromSliceError; + + #[inline] + fn try_from(pubkey: &[u8]) -> Result { + <[u8; 32]>::try_from(pubkey).map(Self::from) + } +} + +impl TryFrom> for Pubkey { + type Error = Vec; + + #[inline] + fn try_from(pubkey: Vec) -> Result { + <[u8; 32]>::try_from(pubkey).map(Self::from) + } +} + +impl TryFrom<&str> for Pubkey { + type Error = ParsePubkeyError; + fn try_from(s: &str) -> Result { + Pubkey::from_str(s) + } +} + +#[cfg(any(feature = "curve25519", target_os = "solana"))] +#[allow(clippy::used_underscore_binding)] +pub fn bytes_are_curve_point>(_bytes: T) -> bool { + #[cfg(not(target_os = "solana"))] + { + curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref()) + .decompress() + .is_some() + } + #[cfg(target_os = "solana")] + unimplemented!(); +} + +impl Pubkey { + pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self { + Self(pubkey_array) + } + + /// Decode a string into a Pubkey, usable in a const context + pub const fn from_str_const(s: &str) -> Self { + let id_array = five8_const::decode_32_const(s); + Pubkey::new_from_array(id_array) + } + + /// unique Pubkey for tests and benchmarks. + pub fn new_unique() -> Self { + use solana_atomic_u64::AtomicU64; + static I: AtomicU64 = AtomicU64::new(1); + + let mut b = [0u8; 32]; + let i = I.fetch_add(1); + // use big endian representation to ensure that recent unique pubkeys + // are always greater than less recent unique pubkeys + b[0..8].copy_from_slice(&i.to_be_bytes()); + Self::from(b) + } + + #[cfg(any(feature = "sha2", target_os = "solana"))] + pub fn create_with_seed( + base: &Pubkey, + seed: &str, + owner: &Pubkey, + ) -> Result { + if seed.len() > MAX_SEED_LEN { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + + let owner = owner.as_ref(); + if owner.len() >= PDA_MARKER.len() { + let slice = &owner[owner.len() - PDA_MARKER.len()..]; + if slice == PDA_MARKER { + return Err(PubkeyError::IllegalOwner); + } + } + let hash = crate::hash::hashv(&[base.as_ref(), seed.as_ref(), owner]); + Ok(Pubkey::from(hash.to_bytes())) + } + + /// Find a valid [program derived address][pda] and its corresponding bump seed. + /// + /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses + /// + /// Program derived addresses (PDAs) are account keys that only the program, + /// `program_id`, has the authority to sign. The address is of the same form + /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519 + /// curve and thus have no associated private key. When performing + /// cross-program invocations the program can "sign" for the key by calling + /// [`invoke_signed`] and passing the same seeds used to generate the + /// address, along with the calculated _bump seed_, which this function + /// returns as the second tuple element. The runtime will verify that the + /// program associated with this address is the caller and thus authorized + /// to be the signer. + /// + /// [`invoke_signed`]: crate::program::invoke_signed + /// + /// The `seeds` are application-specific, and must be carefully selected to + /// uniquely derive accounts per application requirements. It is common to + /// use static strings and other pubkeys as seeds. + /// + /// Because the program address must not lie on the ed25519 curve, there may + /// be seed and program id combinations that are invalid. For this reason, + /// an extra seed (the bump seed) is calculated that results in a + /// point off the curve. The bump seed must be passed as an additional seed + /// when calling `invoke_signed`. + /// + /// The processes of finding a valid program address is by trial and error, + /// and even though it is deterministic given a set of inputs it can take a + /// variable amount of time to succeed across different inputs. This means + /// that when called from an on-chain program it may incur a variable amount + /// of the program's compute budget. Programs that are meant to be very + /// performant may not want to use this function because it could take a + /// considerable amount of time. Programs that are already at risk + /// of exceeding their compute budget should call this with care since + /// there is a chance that the program's budget may be occasionally + /// and unpredictably exceeded. + /// + /// As all account addresses accessed by an on-chain Solana program must be + /// explicitly passed to the program, it is typical for the PDAs to be + /// derived in off-chain client programs, avoiding the compute cost of + /// generating the address on-chain. The address may or may not then be + /// verified by re-deriving it on-chain, depending on the requirements of + /// the program. This verification may be performed without the overhead of + /// re-searching for the bump key by using the [`create_program_address`] + /// function. + /// + /// [`create_program_address`]: Pubkey::create_program_address + /// + /// **Warning**: Because of the way the seeds are hashed there is a potential + /// for program address collisions for the same program id. The seeds are + /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"}, + /// and {"ab", "cd", "ef"} will all result in the same program address given + /// the same program id. Since the chance of collision is local to a given + /// program id, the developer of that program must take care to choose seeds + /// that do not collide with each other. For seed schemes that are susceptible + /// to this type of hash collision, a common remedy is to insert separators + /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}. + /// + /// # Panics + /// + /// Panics in the statistically improbable event that a bump seed could not be + /// found. Use [`try_find_program_address`] to handle this case. + /// + /// [`try_find_program_address`]: Pubkey::try_find_program_address + /// + /// Panics if any of the following are true: + /// + /// - the number of provided seeds is greater than, _or equal to_, [`MAX_SEEDS`], + /// - any individual seed's length is greater than [`MAX_SEED_LEN`]. + /// + /// # Examples + /// + /// This example illustrates a simple case of creating a "vault" account + /// which is derived from the payer account, but owned by an on-chain + /// program. The program derived address is derived in an off-chain client + /// program, which invokes an on-chain Solana program that uses the address + /// to create a new account owned and controlled by the program itself. + /// + /// By convention, the on-chain program will be compiled for use in two + /// different contexts: both on-chain, to interpret a custom program + /// instruction as a Solana transaction; and off-chain, as a library, so + /// that clients can share the instruction data structure, constructors, and + /// other common code. + /// + /// First the on-chain Solana program: + /// + /// ``` + /// # use borsh::{BorshSerialize, BorshDeserialize}; + /// # use solana_program_core::{ + /// # pubkey::Pubkey, + /// # entrypoint::ProgramResult, + /// # program::invoke_signed, + /// # system_instruction, + /// # account_info::{ + /// # AccountInfo, + /// # next_account_info, + /// # }, + /// # }; + /// // The custom instruction processed by our program. It includes the + /// // PDA's bump seed, which is derived by the client program. This + /// // definition is also imported into the off-chain client program. + /// // The computed address of the PDA will be passed to this program via + /// // the `accounts` vector of the `Instruction` type. + /// #[derive(BorshSerialize, BorshDeserialize, Debug)] + /// # #[borsh(crate = "borsh")] + /// pub struct InstructionData { + /// pub vault_bump_seed: u8, + /// pub lamports: u64, + /// } + /// + /// // The size in bytes of a vault account. The client program needs + /// // this information to calculate the quantity of lamports necessary + /// // to pay for the account's rent. + /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024; + /// + /// // The entrypoint of the on-chain program, as provided to the + /// // `entrypoint!` macro. + /// fn process_instruction( + /// program_id: &Pubkey, + /// accounts: &[AccountInfo], + /// instruction_data: &[u8], + /// ) -> ProgramResult { + /// let account_info_iter = &mut accounts.iter(); + /// let payer = next_account_info(account_info_iter)?; + /// // The vault PDA, derived from the payer's address + /// let vault = next_account_info(account_info_iter)?; + /// + /// let mut instruction_data = instruction_data; + /// let instr = InstructionData::deserialize(&mut instruction_data)?; + /// let vault_bump_seed = instr.vault_bump_seed; + /// let lamports = instr.lamports; + /// let vault_size = VAULT_ACCOUNT_SIZE; + /// + /// // Invoke the system program to create an account while virtually + /// // signing with the vault PDA, which is owned by this caller program. + /// invoke_signed( + /// &system_instruction::create_account( + /// &payer.key, + /// &vault.key, + /// lamports, + /// vault_size, + /// &program_id, + /// ), + /// &[ + /// payer.clone(), + /// vault.clone(), + /// ], + /// // A slice of seed slices, each seed slice being the set + /// // of seeds used to generate one of the PDAs required by the + /// // callee program, the final seed being a single-element slice + /// // containing the `u8` bump seed. + /// &[ + /// &[ + /// b"vault", + /// payer.key.as_ref(), + /// &[vault_bump_seed], + /// ], + /// ] + /// )?; + /// + /// Ok(()) + /// } + /// ``` + /// + /// The client program: + /// + /// ``` + /// # use borsh::{BorshSerialize, BorshDeserialize}; + /// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; + /// # use solana_program_core::{ + /// # pubkey::Pubkey, + /// # instruction::Instruction, + /// # hash::Hash, + /// # instruction::AccountMeta, + /// # system_program, + /// # }; + /// # use solana_sdk::{ + /// # signature::Keypair, + /// # signature::{Signer, Signature}, + /// # transaction::Transaction, + /// # }; + /// # use solana_rpc_client::rpc_client::RpcClient; + /// # use std::convert::TryFrom; + /// # use anyhow::Result; + /// # + /// # #[derive(BorshSerialize, BorshDeserialize, Debug)] + /// # #[borsh(crate = "borsh")] + /// # struct InstructionData { + /// # pub vault_bump_seed: u8, + /// # pub lamports: u64, + /// # } + /// # + /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024; + /// # + /// fn create_vault_account( + /// client: &RpcClient, + /// program_id: Pubkey, + /// payer: &Keypair, + /// ) -> Result<()> { + /// // Derive the PDA from the payer account, a string representing the unique + /// // purpose of the account ("vault"), and the address of our on-chain program. + /// let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address( + /// &[b"vault", payer.pubkey().as_ref()], + /// &program_id + /// ); + /// + /// // Get the amount of lamports needed to pay for the vault's rent + /// let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?; + /// let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?; + /// + /// // The on-chain program's instruction data, imported from that program's crate. + /// let instr_data = InstructionData { + /// vault_bump_seed, + /// lamports, + /// }; + /// + /// // The accounts required by both our on-chain program and the system program's + /// // `create_account` instruction, including the vault's address. + /// let accounts = vec![ + /// AccountMeta::new(payer.pubkey(), true), + /// AccountMeta::new(vault_pubkey, false), + /// AccountMeta::new(system_program::ID, false), + /// ]; + /// + /// // Create the instruction by serializing our instruction data via borsh + /// let instruction = Instruction::new_with_borsh( + /// program_id, + /// &instr_data, + /// accounts, + /// ); + /// + /// let blockhash = client.get_latest_blockhash()?; + /// + /// let transaction = Transaction::new_signed_with_payer( + /// &[instruction], + /// Some(&payer.pubkey()), + /// &[payer], + /// blockhash, + /// ); + /// + /// client.send_and_confirm_transaction(&transaction)?; + /// + /// Ok(()) + /// } + /// # let program_id = Pubkey::new_unique(); + /// # let payer = Keypair::new(); + /// # let client = RpcClient::new(String::new()); + /// # + /// # create_vault_account(&client, program_id, &payer)?; + /// # + /// # Ok::<(), anyhow::Error>(()) + /// ``` + #[cfg(any(all(feature = "curve25519", feature = "sha2"), target_os = "solana"))] + pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) { + Self::try_find_program_address(seeds, program_id) + .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed")) + } + + /// Find a valid [program derived address][pda] and its corresponding bump seed. + /// + /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses + /// + /// The only difference between this method and [`find_program_address`] + /// is that this one returns `None` in the statistically improbable event + /// that a bump seed cannot be found; or if any of `find_program_address`'s + /// preconditions are violated. + /// + /// See the documentation for [`find_program_address`] for a full description. + /// + /// [`find_program_address`]: Pubkey::find_program_address + #[cfg(any(all(feature = "curve25519", feature = "sha2"), target_os = "solana"))] + #[allow(clippy::same_item_push)] + pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> { + // Perform the calculation inline, calling this from within a program is + // not supported + #[cfg(not(target_os = "solana"))] + { + let mut bump_seed = [u8::MAX]; + for _ in 0..u8::MAX { + { + let mut seeds_with_bump = seeds.to_vec(); + seeds_with_bump.push(&bump_seed); + match Self::create_program_address(&seeds_with_bump, program_id) { + Ok(address) => return Some((address, bump_seed[0])), + Err(PubkeyError::InvalidSeeds) => (), + _ => break, + } + } + bump_seed[0] -= 1; + } + None + } + // Call via a system call to perform the calculation + #[cfg(target_os = "solana")] + { + let mut bytes = [0; 32]; + let mut bump_seed = u8::MAX; + let result = unsafe { + crate::syscalls::sol_try_find_program_address( + seeds as *const _ as *const u8, + seeds.len() as u64, + program_id as *const _ as *const u8, + &mut bytes as *mut _ as *mut u8, + &mut bump_seed as *mut _ as *mut u8, + ) + }; + match result { + crate::entrypoint::SUCCESS => Some((Pubkey::from(bytes), bump_seed)), + _ => None, + } + } + } + + /// Create a valid [program derived address][pda] without searching for a bump seed. + /// + /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses + /// + /// Because this function does not create a bump seed, it may unpredictably + /// return an error for any given set of seeds and is not generally suitable + /// for creating program derived addresses. + /// + /// However, it can be used for efficiently verifying that a set of seeds plus + /// bump seed generated by [`find_program_address`] derives a particular + /// address as expected. See the example for details. + /// + /// See the documentation for [`find_program_address`] for a full description + /// of program derived addresses and bump seeds. + /// + /// [`find_program_address`]: Pubkey::find_program_address + /// + /// # Examples + /// + /// Creating a program derived address involves iteratively searching for a + /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519 + /// curve. This search process is generally performed off-chain, with the + /// [`find_program_address`] function, after which the client passes the + /// bump seed to the program as instruction data. + /// + /// Depending on the application requirements, a program may wish to verify + /// that the set of seeds, plus the bump seed, do correctly generate an + /// expected address. + /// + /// The verification is performed by appending to the other seeds one + /// additional seed slice that contains the single `u8` bump seed, calling + /// `create_program_address`, checking that the return value is `Ok`, and + /// that the returned `Pubkey` has the expected value. + /// + /// ``` + /// # use solana_program_core::pubkey::Pubkey; + /// # let program_id = Pubkey::new_unique(); + /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id); + /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?; + /// assert_eq!(expected_pda, actual_pda); + /// # Ok::<(), anyhow::Error>(()) + /// ``` + #[cfg(any(all(feature = "curve25519", feature = "sha2"), target_os = "solana"))] + pub fn create_program_address( + seeds: &[&[u8]], + program_id: &Pubkey, + ) -> Result { + if seeds.len() > MAX_SEEDS { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + for seed in seeds.iter() { + if seed.len() > MAX_SEED_LEN { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + } + + // Perform the calculation inline, calling this from within a program is + // not supported + #[cfg(not(target_os = "solana"))] + { + let mut hasher = crate::hash::Hasher::default(); + for seed in seeds.iter() { + hasher.hash(seed); + } + hasher.hashv(&[program_id.as_ref(), PDA_MARKER]); + let hash = hasher.result(); + + if bytes_are_curve_point(hash) { + return Err(PubkeyError::InvalidSeeds); + } + + Ok(Pubkey::from(hash.to_bytes())) + } + // Call via a system call to perform the calculation + #[cfg(target_os = "solana")] + { + let mut bytes = [0; 32]; + let result = unsafe { + crate::syscalls::sol_create_program_address( + seeds as *const _ as *const u8, + seeds.len() as u64, + program_id as *const _ as *const u8, + &mut bytes as *mut _ as *mut u8, + ) + }; + match result { + crate::entrypoint::SUCCESS => Ok(Pubkey::from(bytes)), + _ => Err(result.into()), + } + } + } + + pub const fn to_bytes(self) -> [u8; 32] { + self.0 + } + + #[cfg(any(feature = "curve25519", target_os = "solana"))] + pub fn is_on_curve(&self) -> bool { + bytes_are_curve_point(self) + } + + /// Log a `Pubkey` from a program + pub fn log(&self) { + #[cfg(target_os = "solana")] + unsafe { + crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8) + }; + + #[cfg(not(target_os = "solana"))] + crate::program_stubs::sol_log(&self.to_string()); + } +} + +impl AsRef<[u8]> for Pubkey { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Pubkey { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl fmt::Debug for Pubkey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", bs58::encode(self.0).into_string()) + } +} + +impl fmt::Display for Pubkey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", bs58::encode(self.0).into_string()) + } +} + +/// Convenience macro to declare a static public key and functions to interact with it. +/// +/// Input: a single literal base58 string representation of a program's ID. +/// +/// # Example +/// +/// ``` +/// # // wrapper is used so that the macro invocation occurs in the item position +/// # // rather than in the statement position which isn't allowed. +/// use std::str::FromStr; +/// use solana_program_core::{declare_id, pubkey::Pubkey}; +/// +/// # mod item_wrapper { +/// # use solana_program_core::declare_id; +/// declare_id!("My11111111111111111111111111111111111111111"); +/// # } +/// # use item_wrapper::id; +/// +/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); +/// assert_eq!(id(), my_id); +/// ``` +#[macro_export] +macro_rules! declare_id { + ($address:literal) => { + /// The const program ID. + pub const ID: $crate::pubkey::Pubkey = $crate::pubkey::Pubkey::from_str_const($address); + + /// Returns `true` if given pubkey is the program ID. + // TODO make this const once `derive_const` makes it out of nightly + // and we can `derive_const(PartialEq)` on `Pubkey`. + pub fn check_id(id: &$crate::pubkey::Pubkey) -> bool { + id == &ID + } + + /// Returns the program ID. + pub const fn id() -> $crate::pubkey::Pubkey { + ID + } + + #[cfg(test)] + #[test] + fn test_id() { + assert!(check_id(&id())); + } + }; +} + +/// Same as [`declare_id`] except that it reports that this ID has been deprecated. +#[macro_export] +macro_rules! declare_deprecated_id { + ($address:literal) => { + /// The const program ID. + pub const ID: $crate::pubkey::Pubkey = $crate::pubkey::Pubkey::from_str_const($address); + + #[deprecated()] + /// Returns `true` if given pubkey is the program ID. + pub fn check_id(id: &$crate::pubkey::Pubkey) -> bool { + id == &ID + } + + #[deprecated()] + /// Returns the program ID. + pub const fn id() -> $crate::pubkey::Pubkey { + ID + } + + #[cfg(test)] + #[test] + #[allow(deprecated)] + fn test_id() { + assert!(check_id(&id())); + } + }; +} + +#[deprecated(since = "2.1.0", note = "Use `Pubkey::from_str_const` instead")] +/// Convenience macro to define a static public key. +/// +/// Input: a single literal base58 string representation of a Pubkey. +/// +/// # Example +/// +/// ``` +/// use std::str::FromStr; +/// use solana_program_core::{const_pubkey as pubkey, pubkey::Pubkey}; +/// +/// static ID: Pubkey = pubkey!("My11111111111111111111111111111111111111111"); +/// +/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); +/// assert_eq!(ID, my_id); +/// ``` +#[macro_export] +macro_rules! const_pubkey { + ($address:literal) => { + $crate::pubkey::Pubkey::from_str_const($address) + }; +} + +#[cfg(feature = "borsh")] +impl borsh0_10::de::BorshDeserialize for Pubkey { + fn deserialize_reader( + reader: &mut R, + ) -> ::core::result::Result { + Ok(Self(borsh0_10::BorshDeserialize::deserialize_reader( + reader, + )?)) + } +} + +#[cfg(feature = "borsh")] +macro_rules! impl_borsh_schema { + ($borsh:ident) => { + impl $borsh::BorshSchema for Pubkey + where + [u8; 32]: $borsh::BorshSchema, + { + fn declaration() -> $borsh::schema::Declaration { + "Pubkey".to_string() + } + fn add_definitions_recursively( + definitions: &mut $borsh::maybestd::collections::HashMap< + $borsh::schema::Declaration, + $borsh::schema::Definition, + >, + ) { + let fields = $borsh::schema::Fields::UnnamedFields(<[_]>::into_vec( + $borsh::maybestd::boxed::Box::new([ + <[u8; 32] as $borsh::BorshSchema>::declaration(), + ]), + )); + let definition = $borsh::schema::Definition::Struct { fields }; + ::add_definition( + ::declaration(), + definition, + definitions, + ); + <[u8; 32] as $borsh::BorshSchema>::add_definitions_recursively(definitions); + } + } + }; +} +#[cfg(feature = "borsh")] +impl_borsh_schema!(borsh0_10); + +#[cfg(feature = "borsh")] +macro_rules! impl_borsh_serialize { + ($borsh:ident) => { + impl $borsh::ser::BorshSerialize for Pubkey { + fn serialize( + &self, + writer: &mut W, + ) -> ::core::result::Result<(), $borsh::maybestd::io::Error> { + $borsh::BorshSerialize::serialize(&self.0, writer)?; + Ok(()) + } + } + }; +} +#[cfg(feature = "borsh")] +impl_borsh_serialize!(borsh0_10); + +#[cfg(test)] +mod tests { + use {super::*, std::str::from_utf8}; + + #[test] + fn test_new_unique() { + assert!(Pubkey::new_unique() != Pubkey::new_unique()); + } + + #[test] + fn pubkey_fromstr() { + let pubkey = Pubkey::new_unique(); + let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); + + assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); + + pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string()); + assert_eq!( + pubkey_base58_str.parse::(), + Err(ParsePubkeyError::WrongSize) + ); + + pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); + assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); + + pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); + assert_eq!( + pubkey_base58_str.parse::(), + Err(ParsePubkeyError::WrongSize) + ); + + let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); + assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); + + // throw some non-base58 stuff in there + pubkey_base58_str.replace_range(..1, "I"); + assert_eq!( + pubkey_base58_str.parse::(), + Err(ParsePubkeyError::Invalid) + ); + + // too long input string + // longest valid encoding + let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string(); + // and one to grow on + too_long.push('1'); + assert_eq!(too_long.parse::(), Err(ParsePubkeyError::WrongSize)); + } + + #[test] + fn test_create_with_seed() { + assert!( + Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok() + ); + assert_eq!( + Pubkey::create_with_seed( + &Pubkey::new_unique(), + from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(), + &Pubkey::new_unique() + ), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert!(Pubkey::create_with_seed( + &Pubkey::new_unique(), + "\ + \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ + ", + &Pubkey::new_unique() + ) + .is_ok()); + // utf-8 abuse ;) + assert_eq!( + Pubkey::create_with_seed( + &Pubkey::new_unique(), + "\ + x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ + ", + &Pubkey::new_unique() + ), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + + assert!(Pubkey::create_with_seed( + &Pubkey::new_unique(), + std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(), + &Pubkey::new_unique(), + ) + .is_ok()); + + assert!( + Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok() + ); + + assert_eq!( + Pubkey::create_with_seed( + &Pubkey::default(), + "limber chicken: 4/45", + &Pubkey::default(), + ), + Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq" + .parse() + .unwrap()) + ); + } + + #[test] + fn test_create_program_address() { + let exceeded_seed = &[127; MAX_SEED_LEN + 1]; + let max_seed = &[0; MAX_SEED_LEN]; + let exceeded_seeds: &[&[u8]] = &[ + &[1], + &[2], + &[3], + &[4], + &[5], + &[6], + &[7], + &[8], + &[9], + &[10], + &[11], + &[12], + &[13], + &[14], + &[15], + &[16], + &[17], + ]; + let max_seeds: &[&[u8]] = &[ + &[1], + &[2], + &[3], + &[4], + &[5], + &[6], + &[7], + &[8], + &[9], + &[10], + &[11], + &[12], + &[13], + &[14], + &[15], + &[16], + ]; + let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap(); + let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap(); + + assert_eq!( + Pubkey::create_program_address(&[exceeded_seed], &program_id), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert_eq!( + Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok()); + assert_eq!( + Pubkey::create_program_address(exceeded_seeds, &program_id), + Err(PubkeyError::MaxSeedLengthExceeded) + ); + assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok()); + assert_eq!( + Pubkey::create_program_address(&[b"", &[1]], &program_id), + Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id), + Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id), + Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id), + Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL" + .parse() + .unwrap()) + ); + assert_ne!( + Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(), + Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(), + ); + } + + #[test] + fn test_pubkey_off_curve() { + // try a bunch of random input, all successful generated program + // addresses must land off the curve and be unique + let mut addresses = vec![]; + for _ in 0..1_000 { + let program_id = Pubkey::new_unique(); + let bytes1 = rand::random::<[u8; 10]>(); + let bytes2 = rand::random::<[u8; 32]>(); + if let Ok(program_address) = + Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id) + { + let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice( + &program_address.to_bytes(), + ) + .decompress() + .is_some(); + assert!(!is_on_curve); + assert!(!addresses.contains(&program_address)); + addresses.push(program_address); + } + } + } + + #[test] + fn test_find_program_address() { + for _ in 0..1_000 { + let program_id = Pubkey::new_unique(); + let (address, bump_seed) = + Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id); + assert_eq!( + address, + Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id) + .unwrap() + ); + } + } + + fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result { + let key = Pubkey::new_unique(); + let owner = Pubkey::default(); + + let mut to_fake = owner.to_bytes().to_vec(); + to_fake.extend_from_slice(marker); + + let seed = &String::from_utf8(to_fake[..to_fake.len() - 32].to_vec()).expect("not utf8"); + let base = &Pubkey::try_from(&to_fake[to_fake.len() - 32..]).unwrap(); + + Pubkey::create_with_seed(&key, seed, base) + } + + #[test] + fn test_create_with_seed_rejects_illegal_owner() { + assert_eq!( + pubkey_from_seed_by_marker(PDA_MARKER), + Err(PubkeyError::IllegalOwner) + ); + assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok()); + } +} diff --git a/sdk/program/src/rent.rs b/sdk/program-core/src/rent.rs similarity index 89% rename from sdk/program/src/rent.rs rename to sdk/program-core/src/rent.rs index 6553125eede6d0..2558303d97563d 100644 --- a/sdk/program/src/rent.rs +++ b/sdk/program-core/src/rent.rs @@ -4,12 +4,13 @@ #![allow(clippy::arithmetic_side_effects)] -use {crate::clock::DEFAULT_SLOTS_PER_EPOCH, solana_sdk_macro::CloneZeroed}; +use crate::clock::DEFAULT_SLOTS_PER_EPOCH; /// Configuration of network rent. #[repr(C)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Serialize, Deserialize, PartialEq, CloneZeroed, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(PartialEq, Debug)] pub struct Rent { /// Rental rate in lamports/byte-year. pub lamports_per_byte_year: u64, @@ -25,6 +26,24 @@ pub struct Rent { pub burn_percent: u8, } +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for Rent { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).lamports_per_byte_year) + .write(self.lamports_per_byte_year); + std::ptr::addr_of_mut!((*ptr).exemption_threshold).write(self.exemption_threshold); + std::ptr::addr_of_mut!((*ptr).burn_percent).write(self.burn_percent); + value.assume_init() + } + } +} + /// Default rental rate in lamports/byte-year. /// /// This calculation is based on: diff --git a/sdk/program/src/secp256k1_program.rs b/sdk/program-core/src/secp256k1_program.rs similarity index 100% rename from sdk/program/src/secp256k1_program.rs rename to sdk/program-core/src/secp256k1_program.rs diff --git a/sdk/program-core/src/serialize_utils.rs b/sdk/program-core/src/serialize_utils.rs new file mode 100644 index 00000000000000..4bcb8c331255bd --- /dev/null +++ b/sdk/program-core/src/serialize_utils.rs @@ -0,0 +1,68 @@ +//! Helpers for reading and writing bytes. + +#![allow(clippy::arithmetic_side_effects)] +use {crate::pubkey::Pubkey, solana_sanitize::SanitizeError}; + +pub fn append_u16(buf: &mut Vec, data: u16) { + let start = buf.len(); + buf.resize(buf.len() + 2, 0); + let end = buf.len(); + buf[start..end].copy_from_slice(&data.to_le_bytes()); +} + +pub fn append_u8(buf: &mut Vec, data: u8) { + let start = buf.len(); + buf.resize(buf.len() + 1, 0); + buf[start] = data; +} + +pub fn append_slice(buf: &mut Vec, data: &[u8]) { + let start = buf.len(); + buf.resize(buf.len() + data.len(), 0); + let end = buf.len(); + buf[start..end].copy_from_slice(data); +} + +pub fn read_u8(current: &mut usize, data: &[u8]) -> Result { + if data.len() < *current + 1 { + return Err(SanitizeError::IndexOutOfBounds); + } + let e = data[*current]; + *current += 1; + Ok(e) +} + +pub fn read_pubkey(current: &mut usize, data: &[u8]) -> Result { + let len = std::mem::size_of::(); + if data.len() < *current + len { + return Err(SanitizeError::IndexOutOfBounds); + } + let e = Pubkey::try_from(&data[*current..*current + len]) + .map_err(|_| SanitizeError::ValueOutOfBounds)?; + *current += len; + Ok(e) +} + +pub fn read_u16(current: &mut usize, data: &[u8]) -> Result { + if data.len() < *current + 2 { + return Err(SanitizeError::IndexOutOfBounds); + } + let mut fixed_data = [0u8; 2]; + fixed_data.copy_from_slice(&data[*current..*current + 2]); + let e = u16::from_le_bytes(fixed_data); + *current += 2; + Ok(e) +} + +pub fn read_slice( + current: &mut usize, + data: &[u8], + data_len: usize, +) -> Result, SanitizeError> { + if data.len() < *current + data_len { + return Err(SanitizeError::IndexOutOfBounds); + } + let e = data[*current..*current + data_len].to_vec(); + *current += data_len; + Ok(e) +} diff --git a/sdk/program/src/slot_hashes.rs b/sdk/program-core/src/slot_hashes.rs similarity index 96% rename from sdk/program/src/slot_hashes.rs rename to sdk/program-core/src/slot_hashes.rs index f17512ac9fb124..ed30dffaebe21c 100644 --- a/sdk/program/src/slot_hashes.rs +++ b/sdk/program-core/src/slot_hashes.rs @@ -33,7 +33,8 @@ pub fn set_entries_for_tests_only(entries: usize) { pub type SlotHash = (Slot, Hash); #[repr(C)] -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(PartialEq, Eq, Debug, Default)] pub struct SlotHashes(Vec); impl SlotHashes { diff --git a/sdk/program/src/slot_history.rs b/sdk/program-core/src/slot_history.rs similarity index 98% rename from sdk/program/src/slot_history.rs rename to sdk/program-core/src/slot_history.rs index 5638407210aef9..710bc857e82dd9 100644 --- a/sdk/program/src/slot_history.rs +++ b/sdk/program-core/src/slot_history.rs @@ -12,7 +12,8 @@ use bv::{BitVec, BitsMut}; /// A bitvector indicating which slots are present in the past epoch. #[repr(C)] -#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, PartialEq, Eq)] pub struct SlotHistory { pub bits: BitVec, pub next_slot: Slot, diff --git a/sdk/program/src/stable_layout.rs b/sdk/program-core/src/stable_layout.rs similarity index 100% rename from sdk/program/src/stable_layout.rs rename to sdk/program-core/src/stable_layout.rs diff --git a/sdk/program/src/stable_layout/stable_instruction.rs b/sdk/program-core/src/stable_layout/stable_instruction.rs similarity index 95% rename from sdk/program/src/stable_layout/stable_instruction.rs rename to sdk/program-core/src/stable_layout/stable_instruction.rs index e01bd37477b5f7..61b1f01af781c7 100644 --- a/sdk/program/src/stable_layout/stable_instruction.rs +++ b/sdk/program-core/src/stable_layout/stable_instruction.rs @@ -21,7 +21,7 @@ use { /// Creating a `StableInstruction` from an `Instruction` /// /// ``` -/// # use solana_program::{instruction::Instruction, pubkey::Pubkey, stable_layout::stable_instruction::StableInstruction}; +/// # use solana_program_core::{instruction::Instruction, pubkey::Pubkey, stable_layout::stable_instruction::StableInstruction}; /// # let program_id = Pubkey::default(); /// # let accounts = Vec::default(); /// # let data = Vec::default(); diff --git a/sdk/program/src/stable_layout/stable_rc.rs b/sdk/program-core/src/stable_layout/stable_rc.rs similarity index 100% rename from sdk/program/src/stable_layout/stable_rc.rs rename to sdk/program-core/src/stable_layout/stable_rc.rs diff --git a/sdk/program/src/stable_layout/stable_ref_cell.rs b/sdk/program-core/src/stable_layout/stable_ref_cell.rs similarity index 100% rename from sdk/program/src/stable_layout/stable_ref_cell.rs rename to sdk/program-core/src/stable_layout/stable_ref_cell.rs diff --git a/sdk/program/src/stable_layout/stable_slice.rs b/sdk/program-core/src/stable_layout/stable_slice.rs similarity index 100% rename from sdk/program/src/stable_layout/stable_slice.rs rename to sdk/program-core/src/stable_layout/stable_slice.rs diff --git a/sdk/program/src/stable_layout/stable_vec.rs b/sdk/program-core/src/stable_layout/stable_vec.rs similarity index 98% rename from sdk/program/src/stable_layout/stable_vec.rs rename to sdk/program-core/src/stable_layout/stable_vec.rs index 25a317076d117f..f6b36364b6f62f 100644 --- a/sdk/program/src/stable_layout/stable_vec.rs +++ b/sdk/program-core/src/stable_layout/stable_vec.rs @@ -18,7 +18,7 @@ use std::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; /// Creating a `StableVec` from a `Vec` /// /// ``` -/// # use solana_program::stable_layout::stable_vec::StableVec; +/// # use solana_program_core::stable_layout::stable_vec::StableVec; /// let vec = vec!["meow", "woof", "moo"]; /// let vec = StableVec::from(vec); /// ``` diff --git a/sdk/program/src/stake_history.rs b/sdk/program-core/src/stake_history.rs similarity index 94% rename from sdk/program/src/stake_history.rs rename to sdk/program-core/src/stake_history.rs index f1bbe3b0f81da8..13b404daf08f89 100644 --- a/sdk/program/src/stake_history.rs +++ b/sdk/program-core/src/stake_history.rs @@ -13,7 +13,8 @@ pub const MAX_ENTRIES: usize = 512; // it should never take as many as 512 epoch #[repr(C)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Default, Clone)] pub struct StakeHistoryEntry { pub effective: u64, // effective stake at this epoch pub activating: u64, // sum of portion of stakes not fully warmed up @@ -58,7 +59,8 @@ impl std::ops::Add for StakeHistoryEntry { #[repr(C)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Default, Clone)] pub struct StakeHistory(Vec<(Epoch, StakeHistoryEntry)>); impl StakeHistory { diff --git a/sdk/program/src/syscalls/definitions.rs b/sdk/program-core/src/syscalls/definitions.rs similarity index 86% rename from sdk/program/src/syscalls/definitions.rs rename to sdk/program-core/src/syscalls/definitions.rs index fd22296e03ff59..4591636dcdd201 100644 --- a/sdk/program/src/syscalls/definitions.rs +++ b/sdk/program-core/src/syscalls/definitions.rs @@ -1,17 +1,3 @@ -#[cfg(target_feature = "static-syscalls")] -pub use solana_define_syscall::sys_hash; -#[deprecated(since = "2.1.0", note = "Use `solana-msg::sol_log` instead.")] -pub use solana_msg::sol_log; -#[deprecated( - since = "2.1.0", - note = "Use `solana_program_memory::syscalls` instead" -)] -pub use solana_program_memory::syscalls::{sol_memcmp_, sol_memcpy_, sol_memmove_, sol_memset_}; -#[deprecated( - since = "2.1.0", - note = "Use `solana_secp256k1_recover::sol_secp256k1_recover` instead" -)] -pub use solana_secp256k1_recover::sol_secp256k1_recover; use { crate::{ instruction::{AccountMeta, ProcessedSiblingInstruction}, diff --git a/sdk/program/src/syscalls/mod.rs b/sdk/program-core/src/syscalls/mod.rs similarity index 100% rename from sdk/program/src/syscalls/mod.rs rename to sdk/program-core/src/syscalls/mod.rs diff --git a/sdk/program/src/system_instruction.rs b/sdk/program-core/src/system_instruction.rs similarity index 90% rename from sdk/program/src/system_instruction.rs rename to sdk/program-core/src/system_instruction.rs index a1c301b85cdab9..90d19d9795c029 100644 --- a/sdk/program/src/system_instruction.rs +++ b/sdk/program-core/src/system_instruction.rs @@ -39,42 +39,124 @@ //! [`invoke_signed`]: crate::program::invoke_signed //! [`AccountInfo`]: crate::account_info::AccountInfo -#[allow(deprecated)] -use { - crate::{ - instruction::{AccountMeta, Instruction}, - nonce, - pubkey::Pubkey, - system_program, - sysvar::{recent_blockhashes, rent}, - }, - num_derive::{FromPrimitive, ToPrimitive}, - solana_decode_error::DecodeError, - thiserror::Error, +#[cfg(feature = "bincode")] +use crate::{ + instruction::{AccountMeta, Instruction}, + nonce, system_program, + sysvar::{recent_blockhashes, rent}, }; +use core::fmt; +#[allow(deprecated)] +use {crate::pubkey::Pubkey, solana_decode_error::DecodeError}; -#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] +#[cfg_attr(feature = "serde", derive(Serialize))] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SystemError { - #[error("an account with the same address already exists")] AccountAlreadyInUse, - #[error("account does not have enough SOL to perform the operation")] ResultWithNegativeLamports, - #[error("cannot assign account to this program id")] InvalidProgramId, - #[error("cannot allocate account data of this length")] InvalidAccountDataLength, - #[error("length of requested seed is too long")] MaxSeedLengthExceeded, - #[error("provided address does not match addressed derived from seed")] AddressWithSeedMismatch, - #[error("advancing stored nonce requires a populated RecentBlockhashes sysvar")] NonceNoRecentBlockhashes, - #[error("stored nonce is still in recent_blockhashes")] NonceBlockhashNotExpired, - #[error("specified nonce does not match stored nonce")] NonceUnexpectedBlockhashValue, } +impl std::error::Error for SystemError {} + +impl fmt::Display for SystemError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SystemError::AccountAlreadyInUse => { + f.write_str("an account with the same address already exists") + } + SystemError::ResultWithNegativeLamports => { + f.write_str("account does not have enough SOL to perform the operation") + } + SystemError::InvalidProgramId => { + f.write_str("cannot assign account to this program id") + } + SystemError::InvalidAccountDataLength => { + f.write_str("cannot allocate account data of this length") + } + SystemError::MaxSeedLengthExceeded => { + f.write_str("length of requested seed is too long") + } + SystemError::AddressWithSeedMismatch => { + f.write_str("provided address does not match addressed derived from seed") + } + SystemError::NonceNoRecentBlockhashes => { + f.write_str("advancing stored nonce requires a populated RecentBlockhashes sysvar") + } + SystemError::NonceBlockhashNotExpired => { + f.write_str("stored nonce is still in recent_blockhashes") + } + SystemError::NonceUnexpectedBlockhashValue => { + f.write_str("specified nonce does not match stored nonce") + } + } + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::FromPrimitive for SystemError { + #[inline] + fn from_i64(n: i64) -> Option { + if n == SystemError::AccountAlreadyInUse as i64 { + Some(SystemError::AccountAlreadyInUse) + } else if n == SystemError::ResultWithNegativeLamports as i64 { + Some(SystemError::ResultWithNegativeLamports) + } else if n == SystemError::InvalidProgramId as i64 { + Some(SystemError::InvalidProgramId) + } else if n == SystemError::InvalidAccountDataLength as i64 { + Some(SystemError::InvalidAccountDataLength) + } else if n == SystemError::MaxSeedLengthExceeded as i64 { + Some(SystemError::MaxSeedLengthExceeded) + } else if n == SystemError::AddressWithSeedMismatch as i64 { + Some(SystemError::AddressWithSeedMismatch) + } else if n == SystemError::NonceNoRecentBlockhashes as i64 { + Some(SystemError::NonceNoRecentBlockhashes) + } else if n == SystemError::NonceBlockhashNotExpired as i64 { + Some(SystemError::NonceBlockhashNotExpired) + } else if n == SystemError::NonceUnexpectedBlockhashValue as i64 { + Some(SystemError::NonceUnexpectedBlockhashValue) + } else { + None + } + } + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_i64(n as i64) + } +} + +#[cfg(feature = "num-traits")] +impl num_traits::ToPrimitive for SystemError { + #[inline] + fn to_i64(&self) -> Option { + Some(match *self { + SystemError::AccountAlreadyInUse => SystemError::AccountAlreadyInUse as i64, + SystemError::ResultWithNegativeLamports => { + SystemError::ResultWithNegativeLamports as i64 + } + SystemError::InvalidProgramId => SystemError::InvalidProgramId as i64, + SystemError::InvalidAccountDataLength => SystemError::InvalidAccountDataLength as i64, + SystemError::MaxSeedLengthExceeded => SystemError::MaxSeedLengthExceeded as i64, + SystemError::AddressWithSeedMismatch => SystemError::AddressWithSeedMismatch as i64, + SystemError::NonceNoRecentBlockhashes => SystemError::NonceNoRecentBlockhashes as i64, + SystemError::NonceBlockhashNotExpired => SystemError::NonceBlockhashNotExpired as i64, + SystemError::NonceUnexpectedBlockhashValue => { + SystemError::NonceUnexpectedBlockhashValue as i64 + } + }) + } + #[inline] + fn to_u64(&self) -> Option { + self.to_i64().map(|x| x as u64) + } +} + impl DecodeError for SystemError { fn type_of() -> &'static str { "SystemError" @@ -106,7 +188,8 @@ static_assertions::const_assert_eq!(MAX_PERMITTED_DATA_LENGTH, 10_485_760); frozen_abi(digest = "5e22s2kFu9Do77hdcCyxyhuKHD8ThAB6Q6dNaLTCjL5M"), derive(AbiExample, AbiEnumVisitor) )] -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SystemInstruction { /// Create a new account /// @@ -309,7 +392,7 @@ pub enum SystemInstruction { /// The `payer` and `new_account` are signers. /// /// ``` -/// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; +/// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -368,7 +451,7 @@ pub enum SystemInstruction { /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -438,6 +521,7 @@ pub enum SystemInstruction { /// /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn create_account( from_pubkey: &Pubkey, to_pubkey: &Pubkey, @@ -460,6 +544,7 @@ pub fn create_account( ) } +#[cfg(feature = "bincode")] // we accept `to` as a parameter so that callers do their own error handling when // calling create_with_seed() pub fn create_account_with_seed( @@ -515,7 +600,7 @@ pub fn create_account_with_seed( /// The `payer` and `new_account` are signers. /// /// ``` -/// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; +/// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -585,7 +670,7 @@ pub fn create_account_with_seed( /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -671,6 +756,7 @@ pub fn create_account_with_seed( /// /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { let account_metas = vec![AccountMeta::new(*pubkey, true)]; Instruction::new_with_bincode( @@ -680,6 +766,7 @@ pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { ) } +#[cfg(feature = "bincode")] pub fn assign_with_seed( address: &Pubkey, // must match create_with_seed(base, seed, owner) base: &Pubkey, @@ -726,7 +813,7 @@ pub fn assign_with_seed( /// The `payer` and `new_account` are signers. /// /// ``` -/// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; +/// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -796,7 +883,7 @@ pub fn assign_with_seed( /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -882,6 +969,7 @@ pub fn assign_with_seed( /// /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { let account_metas = vec![ AccountMeta::new(*from_pubkey, true), @@ -894,6 +982,7 @@ pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Inst ) } +#[cfg(feature = "bincode")] pub fn transfer_with_seed( from_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner) from_base: &Pubkey, @@ -946,7 +1035,7 @@ pub fn transfer_with_seed( /// The `payer` and `new_account` are signers. /// /// ``` -/// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; +/// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -1016,7 +1105,7 @@ pub fn transfer_with_seed( /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -1102,6 +1191,7 @@ pub fn transfer_with_seed( /// /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { let account_metas = vec![AccountMeta::new(*pubkey, true)]; Instruction::new_with_bincode( @@ -1111,6 +1201,7 @@ pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { ) } +#[cfg(feature = "bincode")] pub fn allocate_with_seed( address: &Pubkey, // must match create_with_seed(base, seed, owner) base: &Pubkey, @@ -1154,7 +1245,7 @@ pub fn allocate_with_seed( /// This example performs multiple transfers in a single transaction. /// /// ``` -/// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; +/// # use solana_program_core::example_mocks::{solana_sdk, solana_rpc_client}; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -1211,7 +1302,7 @@ pub fn allocate_with_seed( /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_program::{ +/// use solana_program_core::{ /// account_info::{next_account_info, next_account_infos, AccountInfo}, /// entrypoint, /// entrypoint::ProgramResult, @@ -1275,6 +1366,7 @@ pub fn allocate_with_seed( /// /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec { to_lamports .iter() @@ -1282,6 +1374,7 @@ pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec .collect() } +#[cfg(feature = "bincode")] pub fn create_nonce_account_with_seed( from_pubkey: &Pubkey, nonce_pubkey: &Pubkey, @@ -1381,8 +1474,8 @@ pub fn create_nonce_account_with_seed( /// Create a nonce account from an off-chain client: /// /// ``` -/// # use solana_program::example_mocks::solana_sdk; -/// # use solana_program::example_mocks::solana_rpc_client; +/// # use solana_program_core::example_mocks::solana_sdk; +/// # use solana_program_core::example_mocks::solana_rpc_client; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// # pubkey::Pubkey, @@ -1424,6 +1517,7 @@ pub fn create_nonce_account_with_seed( /// # /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn create_nonce_account( from_pubkey: &Pubkey, nonce_pubkey: &Pubkey, @@ -1492,9 +1586,9 @@ pub fn create_nonce_account( /// Create and sign a transaction with a durable nonce: /// /// ``` -/// # use solana_program::example_mocks::solana_sdk; -/// # use solana_program::example_mocks::solana_rpc_client; -/// # use solana_program::example_mocks::solana_rpc_client_nonce_utils; +/// # use solana_program_core::example_mocks::solana_sdk; +/// # use solana_program_core::example_mocks::solana_rpc_client; +/// # use solana_program_core::example_mocks::solana_rpc_client_nonce_utils; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// message::Message, @@ -1574,6 +1668,7 @@ pub fn create_nonce_account( /// # /// # 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), @@ -1617,8 +1712,8 @@ pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) /// # Examples /// /// ``` -/// # use solana_program::example_mocks::solana_sdk; -/// # use solana_program::example_mocks::solana_rpc_client; +/// # use solana_program_core::example_mocks::solana_sdk; +/// # use solana_program_core::example_mocks::solana_rpc_client; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -1660,6 +1755,7 @@ pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) /// # /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn withdraw_nonce_account( nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey, @@ -1700,8 +1796,8 @@ pub fn withdraw_nonce_account( /// # Examples /// /// ``` -/// # use solana_program::example_mocks::solana_sdk; -/// # use solana_program::example_mocks::solana_rpc_client; +/// # use solana_program_core::example_mocks::solana_sdk; +/// # use solana_program_core::example_mocks::solana_rpc_client; /// use solana_rpc_client::rpc_client::RpcClient; /// use solana_sdk::{ /// pubkey::Pubkey, @@ -1742,6 +1838,7 @@ pub fn withdraw_nonce_account( /// # /// # Ok::<(), anyhow::Error>(()) /// ``` +#[cfg(feature = "bincode")] pub fn authorize_nonce_account( nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey, @@ -1760,6 +1857,7 @@ pub fn authorize_nonce_account( /// 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( diff --git a/sdk/program/src/system_program.rs b/sdk/program-core/src/system_program.rs similarity index 100% rename from sdk/program/src/system_program.rs rename to sdk/program-core/src/system_program.rs diff --git a/sdk/program/src/sysvar/clock.rs b/sdk/program-core/src/sysvar/clock.rs similarity index 89% rename from sdk/program/src/sysvar/clock.rs rename to sdk/program-core/src/sysvar/clock.rs index c9f31e8fa9efcd..f3c74f7e0830d0 100644 --- a/sdk/program/src/sysvar/clock.rs +++ b/sdk/program-core/src/sysvar/clock.rs @@ -16,7 +16,7 @@ //! Accessing via on-chain program directly: //! //! ```no_run -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -24,7 +24,7 @@ //! # sysvar::clock::{self, Clock}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -38,7 +38,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = Clock::id(); //! # let l = &mut 1169280; //! # let d = &mut vec![240, 153, 233, 7, 0, 0, 0, 0, 11, 115, 118, 98, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, 121, 50, 119, 98, 0, 0, 0, 0]; @@ -55,7 +55,7 @@ //! Accessing via on-chain program's account parameters: //! //! ``` -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -63,7 +63,7 @@ //! # sysvar::clock::{self, Clock}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -81,7 +81,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = Clock::id(); //! # let l = &mut 1169280; //! # let d = &mut vec![240, 153, 233, 7, 0, 0, 0, 0, 11, 115, 118, 98, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, 121, 50, 119, 98, 0, 0, 0, 0]; @@ -98,8 +98,8 @@ //! Accessing via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::clock::{self, Clock}; @@ -127,10 +127,12 @@ //! ``` pub use crate::clock::Clock; +#[cfg(feature = "bincode")] use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!("SysvarC1ock11111111111111111111111111111111", Clock); +#[cfg(feature = "bincode")] impl Sysvar for Clock { impl_sysvar_get!(sol_get_clock_sysvar); } diff --git a/sdk/program/src/sysvar/epoch_rewards.rs b/sdk/program-core/src/sysvar/epoch_rewards.rs similarity index 93% rename from sdk/program/src/sysvar/epoch_rewards.rs rename to sdk/program-core/src/sysvar/epoch_rewards.rs index c8aa7bfbbc88e3..63760a7a4ce914 100755 --- a/sdk/program/src/sysvar/epoch_rewards.rs +++ b/sdk/program-core/src/sysvar/epoch_rewards.rs @@ -27,7 +27,7 @@ //! Accessing via on-chain program directly: //! //! ```no_run -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -49,7 +49,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = EpochRewards::id(); //! # let l = &mut 1559040; //! # let epoch_rewards = EpochRewards { @@ -73,7 +73,7 @@ //! Accessing via on-chain program's account parameters: //! //! ``` -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -81,7 +81,7 @@ //! # sysvar::epoch_rewards::{self, EpochRewards}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -99,7 +99,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = EpochRewards::id(); //! # let l = &mut 1559040; //! # let epoch_rewards = EpochRewards { @@ -123,8 +123,8 @@ //! Accessing via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::epoch_rewards::{self, EpochRewards}; @@ -160,10 +160,12 @@ //! ``` pub use crate::epoch_rewards::EpochRewards; +#[cfg(feature = "bincode")] use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!("SysvarEpochRewards1111111111111111111111111", EpochRewards); +#[cfg(feature = "bincode")] impl Sysvar for EpochRewards { impl_sysvar_get!(sol_get_epoch_rewards_sysvar); } diff --git a/sdk/program/src/sysvar/epoch_schedule.rs b/sdk/program-core/src/sysvar/epoch_schedule.rs similarity index 90% rename from sdk/program/src/sysvar/epoch_schedule.rs rename to sdk/program-core/src/sysvar/epoch_schedule.rs index 0f3a0aa68896f8..a013e6384bc000 100644 --- a/sdk/program/src/sysvar/epoch_schedule.rs +++ b/sdk/program-core/src/sysvar/epoch_schedule.rs @@ -16,7 +16,7 @@ //! Accessing via on-chain program directly: //! //! ```no_run -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -24,7 +24,7 @@ //! # sysvar::epoch_schedule::{self, EpochSchedule}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -38,7 +38,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = EpochSchedule::id(); //! # let l = &mut 1120560; //! # let d = &mut vec![0, 32, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @@ -55,7 +55,7 @@ //! Accessing via on-chain program's account parameters: //! //! ``` -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -63,7 +63,7 @@ //! # sysvar::epoch_schedule::{self, EpochSchedule}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -81,7 +81,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = EpochSchedule::id(); //! # let l = &mut 1120560; //! # let d = &mut vec![0, 32, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @@ -98,8 +98,8 @@ //! Accessing via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::epoch_schedule::{self, EpochSchedule}; @@ -126,10 +126,12 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` pub use crate::epoch_schedule::EpochSchedule; +#[cfg(feature = "bincode")] use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!("SysvarEpochSchedu1e111111111111111111111111", EpochSchedule); +#[cfg(feature = "bincode")] impl Sysvar for EpochSchedule { impl_sysvar_get!(sol_get_epoch_schedule_sysvar); } diff --git a/sdk/program/src/sysvar/fees.rs b/sdk/program-core/src/sysvar/fees.rs similarity index 69% rename from sdk/program/src/sysvar/fees.rs rename to sdk/program-core/src/sysvar/fees.rs index 003a87a64f4f0b..1f95da377a2e28 100644 --- a/sdk/program/src/sysvar/fees.rs +++ b/sdk/program-core/src/sysvar/fees.rs @@ -20,12 +20,9 @@ #![allow(deprecated)] -use { - crate::{ - fee_calculator::FeeCalculator, impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar, - }, - solana_sdk_macro::CloneZeroed, -}; +use crate::fee_calculator::FeeCalculator; +#[cfg(feature = "bincode")] +use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_deprecated_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees); @@ -35,11 +32,27 @@ crate::declare_deprecated_sysvar_id!("SysvarFees11111111111111111111111111111111 note = "Please do not use, will no longer be available in the future" )] #[repr(C)] -#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq, Eq)] pub struct Fees { pub fee_calculator: FeeCalculator, } +// Recursive expansion of CloneZeroed macro +// ========================================= + +impl Clone for Fees { + fn clone(&self) -> Self { + let mut value = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(&mut value, 0, 1); + let ptr = value.as_mut_ptr(); + std::ptr::addr_of_mut!((*ptr).fee_calculator).write(self.fee_calculator); + value.assume_init() + } + } +} + impl Fees { pub fn new(fee_calculator: &FeeCalculator) -> Self { #[allow(deprecated)] @@ -49,6 +62,7 @@ impl Fees { } } +#[cfg(feature = "bincode")] impl Sysvar for Fees { impl_sysvar_get!(sol_get_fees_sysvar); } diff --git a/sdk/program/src/sysvar/instructions.rs b/sdk/program-core/src/sysvar/instructions.rs similarity index 100% rename from sdk/program/src/sysvar/instructions.rs rename to sdk/program-core/src/sysvar/instructions.rs diff --git a/sdk/program/src/sysvar/last_restart_slot.rs b/sdk/program-core/src/sysvar/last_restart_slot.rs similarity index 94% rename from sdk/program/src/sysvar/last_restart_slot.rs rename to sdk/program-core/src/sysvar/last_restart_slot.rs index 17c016a6c952b0..7d1154d9c814bf 100644 --- a/sdk/program/src/sysvar/last_restart_slot.rs +++ b/sdk/program-core/src/sysvar/last_restart_slot.rs @@ -16,7 +16,7 @@ //! Accessing via on-chain program directly: //! //! ```no_run -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -40,6 +40,7 @@ //! pub use crate::last_restart_slot::LastRestartSlot; +#[cfg(feature = "bincode")] use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!( @@ -47,6 +48,7 @@ crate::declare_sysvar_id!( LastRestartSlot ); +#[cfg(feature = "bincode")] impl Sysvar for LastRestartSlot { impl_sysvar_get!(sol_get_last_restart_slot); } diff --git a/sdk/program/src/sysvar/mod.rs b/sdk/program-core/src/sysvar/mod.rs similarity index 97% rename from sdk/program/src/sysvar/mod.rs rename to sdk/program-core/src/sysvar/mod.rs index 69a3c475a0decd..ace67520c93df6 100644 --- a/sdk/program/src/sysvar/mod.rs +++ b/sdk/program-core/src/sysvar/mod.rs @@ -10,7 +10,7 @@ //! directly from the runtime, as in this example that logs the `clock` sysvar: //! //! ``` -//! use solana_program::{ +//! use solana_program_core::{ //! account_info::AccountInfo, //! clock, //! entrypoint::ProgramResult, @@ -36,7 +36,7 @@ //! again logs the [`clock`] sysvar. //! //! ``` -//! use solana_program::{ +//! use solana_program_core::{ //! account_info::{next_account_info, AccountInfo}, //! clock, //! entrypoint::ProgramResult, @@ -81,7 +81,10 @@ //! //! [sysvardoc]: https://docs.solanalabs.com/runtime/sysvars -use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +use crate::pubkey::Pubkey; +#[cfg(feature = "bincode")] +use crate::{account_info::AccountInfo, program_error::ProgramError}; +#[cfg(feature = "bv")] #[allow(deprecated)] pub use sysvar_ids::ALL_IDS; @@ -95,9 +98,11 @@ pub mod recent_blockhashes; pub mod rent; pub mod rewards; pub mod slot_hashes; +#[cfg(feature = "bv")] pub mod slot_history; pub mod stake_history; +#[cfg(feature = "bv")] #[deprecated( since = "2.0.0", note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys` instead" @@ -129,6 +134,7 @@ mod sysvar_ids { note = "please check the account's owner or use solana_sdk::reserved_account_keys::ReservedAccountKeys instead" )] #[allow(deprecated)] +#[cfg(feature = "bv")] pub fn is_sysvar_id(id: &Pubkey) -> bool { ALL_IDS.iter().any(|key| key == id) } @@ -183,6 +189,7 @@ pub trait SysvarId { fn check_id(pubkey: &Pubkey) -> bool; } +#[cfg(feature = "bincode")] /// A type that holds sysvar data. pub trait Sysvar: SysvarId + Default + Sized + serde::Serialize + serde::de::DeserializeOwned @@ -249,6 +256,7 @@ macro_rules! impl_sysvar_get { }; } +#[cfg(feature = "bincode")] /// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar` /// syscall. fn get_sysvar( diff --git a/sdk/program/src/sysvar/recent_blockhashes.rs b/sdk/program-core/src/sysvar/recent_blockhashes.rs similarity index 92% rename from sdk/program/src/sysvar/recent_blockhashes.rs rename to sdk/program-core/src/sysvar/recent_blockhashes.rs index ec3a69baf7adb9..fdbb6c30fbda64 100644 --- a/sdk/program/src/sysvar/recent_blockhashes.rs +++ b/sdk/program-core/src/sysvar/recent_blockhashes.rs @@ -18,10 +18,10 @@ #![allow(deprecated)] #![allow(clippy::arithmetic_side_effects)] +#[cfg(feature = "bincode")] +use crate::sysvar::Sysvar; use { - crate::{ - declare_deprecated_sysvar_id, fee_calculator::FeeCalculator, hash::Hash, sysvar::Sysvar, - }, + crate::{declare_deprecated_sysvar_id, fee_calculator::FeeCalculator, hash::Hash}, std::{cmp::Ordering, collections::BinaryHeap, iter::FromIterator, ops::Deref}, }; @@ -41,7 +41,8 @@ declare_deprecated_sysvar_id!( note = "Please do not use, will no longer be available in the future" )] #[repr(C)] -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Entry { pub blockhash: Hash, pub fee_calculator: FeeCalculator, @@ -91,7 +92,8 @@ impl<'a> PartialOrd for IterItem<'a> { note = "Please do not use, will no longer be available in the future" )] #[repr(C)] -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct RecentBlockhashes(Vec); impl Default for RecentBlockhashes { @@ -146,6 +148,7 @@ impl Iterator for IntoIterSorted { } } +#[cfg(feature = "bincode")] impl Sysvar for RecentBlockhashes { fn size_of() -> usize { // hard-coded so that we don't have to construct an empty diff --git a/sdk/program/src/sysvar/rent.rs b/sdk/program-core/src/sysvar/rent.rs similarity index 88% rename from sdk/program/src/sysvar/rent.rs rename to sdk/program-core/src/sysvar/rent.rs index 4767838383b22f..2ef14ffab154c0 100644 --- a/sdk/program/src/sysvar/rent.rs +++ b/sdk/program-core/src/sysvar/rent.rs @@ -17,7 +17,7 @@ //! Accessing via on-chain program directly: //! //! ```no_run -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -25,7 +25,7 @@ //! # sysvar::rent::{self, Rent}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -39,7 +39,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = Rent::id(); //! # let l = &mut 1009200; //! # let d = &mut vec![152, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 100]; @@ -56,7 +56,7 @@ //! Accessing via on-chain program's parameters: //! //! ``` -//! # use solana_program::{ +//! # use solana_program_core::{ //! # account_info::{AccountInfo, next_account_info}, //! # entrypoint::ProgramResult, //! # msg, @@ -64,7 +64,7 @@ //! # sysvar::rent::{self, Rent}, //! # sysvar::Sysvar, //! # }; -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_core::program_error::ProgramError; //! # //! fn process_instruction( //! program_id: &Pubkey, @@ -82,7 +82,7 @@ //! Ok(()) //! } //! # -//! # use solana_program::sysvar::SysvarId; +//! # use solana_program_core::sysvar::SysvarId; //! # let p = Rent::id(); //! # let l = &mut 1009200; //! # let d = &mut vec![152, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 100]; @@ -99,8 +99,8 @@ //! Accessing via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::rent::{self, Rent}; @@ -127,10 +127,12 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` pub use crate::rent::Rent; +#[cfg(feature = "bincode")] use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar}; crate::declare_sysvar_id!("SysvarRent111111111111111111111111111111111", Rent); +#[cfg(feature = "bincode")] impl Sysvar for Rent { impl_sysvar_get!(sol_get_rent_sysvar); } diff --git a/sdk/program/src/sysvar/rewards.rs b/sdk/program-core/src/sysvar/rewards.rs similarity index 73% rename from sdk/program/src/sysvar/rewards.rs rename to sdk/program-core/src/sysvar/rewards.rs index a9d8fb171d3fdd..4abca14afad596 100644 --- a/sdk/program/src/sysvar/rewards.rs +++ b/sdk/program-core/src/sysvar/rewards.rs @@ -1,11 +1,13 @@ //! This sysvar is deprecated and unused. +#[cfg(feature = "bincode")] use crate::sysvar::Sysvar; crate::declare_sysvar_id!("SysvarRewards111111111111111111111111111111", Rewards); #[repr(C)] -#[derive(Serialize, Deserialize, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Default, PartialEq)] pub struct Rewards { pub validator_point_value: f64, pub unused: f64, @@ -19,4 +21,5 @@ impl Rewards { } } +#[cfg(feature = "bincode")] impl Sysvar for Rewards {} diff --git a/sdk/program/src/sysvar/slot_hashes.rs b/sdk/program-core/src/sysvar/slot_hashes.rs similarity index 88% rename from sdk/program/src/sysvar/slot_hashes.rs rename to sdk/program-core/src/sysvar/slot_hashes.rs index 97a465165314e3..52b35384c44fe5 100644 --- a/sdk/program/src/sysvar/slot_hashes.rs +++ b/sdk/program-core/src/sysvar/slot_hashes.rs @@ -17,8 +17,8 @@ //! Calling via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::slot_hashes::{self, SlotHashes}; @@ -46,20 +46,15 @@ //! ``` pub use crate::slot_hashes::SlotHashes; -use { - crate::{ - account_info::AccountInfo, - clock::Slot, - hash::Hash, - program_error::ProgramError, - slot_hashes::MAX_ENTRIES, - sysvar::{get_sysvar, Sysvar, SysvarId}, - }, - bytemuck_derive::{Pod, Zeroable}, -}; +#[cfg(feature = "bincode")] +use crate::{account_info::AccountInfo, program_error::ProgramError, sysvar::Sysvar}; +use crate::{clock::Slot, hash::Hash}; +#[cfg(feature = "bytemuck")] +use bytemuck_derive::{Pod, Zeroable}; crate::declare_sysvar_id!("SysvarS1otHashes111111111111111111111111111", SlotHashes); +#[cfg(feature = "bincode")] impl Sysvar for SlotHashes { // override fn size_of() -> usize { @@ -72,7 +67,8 @@ impl Sysvar for SlotHashes { } } -#[derive(Copy, Clone, Default, Pod, Zeroable)] +#[derive(Copy, Clone, Default)] +#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] #[repr(C)] struct PodSlotHash { slot: Slot, @@ -82,6 +78,7 @@ struct PodSlotHash { /// API for querying the `SlotHashes` sysvar. pub struct SlotHashesSysvar; +#[cfg(all(feature = "bincode", feature = "bytemuck"))] impl SlotHashesSysvar { /// Get a value from the sysvar entries by its key. /// Returns `None` if the key is not found. @@ -105,8 +102,10 @@ impl SlotHashesSysvar { } } +#[cfg(all(feature = "bincode", feature = "bytemuck"))] fn get_pod_slot_hashes() -> Result, ProgramError> { - let mut pod_hashes = vec![PodSlotHash::default(); MAX_ENTRIES]; + use crate::sysvar::SysvarId; + let mut pod_hashes = vec![PodSlotHash::default(); crate::slot_hashes::MAX_ENTRIES]; { let data = bytemuck::try_cast_slice_mut::(&mut pod_hashes) .map_err(|_| ProgramError::InvalidAccountData)?; @@ -118,7 +117,7 @@ fn get_pod_slot_hashes() -> Result, ProgramError> { let offset = 8; // Vector length as `u64`. let length = (SlotHashes::size_of() as u64).saturating_sub(offset); - get_sysvar(data, &SlotHashes::id(), offset, length)?; + crate::sysvar::get_sysvar(data, &SlotHashes::id(), offset, length)?; } Ok(pod_hashes) } diff --git a/sdk/program/src/sysvar/slot_history.rs b/sdk/program-core/src/sysvar/slot_history.rs similarity index 93% rename from sdk/program/src/sysvar/slot_history.rs rename to sdk/program-core/src/sysvar/slot_history.rs index 668d4f194e2f8d..08912489905690 100644 --- a/sdk/program/src/sysvar/slot_history.rs +++ b/sdk/program-core/src/sysvar/slot_history.rs @@ -17,8 +17,8 @@ //! Calling via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::slot_history::{self, SlotHistory}; @@ -47,6 +47,7 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +#[cfg(feature = "bincode")] use crate::sysvar::Sysvar; pub use crate::{ account_info::AccountInfo, program_error::ProgramError, slot_history::SlotHistory, @@ -54,6 +55,7 @@ pub use crate::{ crate::declare_sysvar_id!("SysvarS1otHistory11111111111111111111111111", SlotHistory); +#[cfg(feature = "bincode")] impl Sysvar for SlotHistory { // override fn size_of() -> usize { diff --git a/sdk/program/src/sysvar/stake_history.rs b/sdk/program-core/src/sysvar/stake_history.rs similarity index 97% rename from sdk/program/src/sysvar/stake_history.rs rename to sdk/program-core/src/sysvar/stake_history.rs index 6f2008bf8e44a5..2087110b1b33b4 100644 --- a/sdk/program/src/sysvar/stake_history.rs +++ b/sdk/program-core/src/sysvar/stake_history.rs @@ -17,8 +17,8 @@ //! Calling via the RPC client: //! //! ``` -//! # use solana_program::example_mocks::solana_sdk; -//! # use solana_program::example_mocks::solana_rpc_client; +//! # use solana_program_core::example_mocks::solana_sdk; +//! # use solana_program_core::example_mocks::solana_rpc_client; //! # use solana_sdk::account::Account; //! # use solana_rpc_client::rpc_client::RpcClient; //! # use solana_sdk::sysvar::stake_history::{self, StakeHistory}; @@ -45,15 +45,17 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +use crate::clock::Epoch; pub use crate::stake_history::StakeHistory; +#[cfg(feature = "bincode")] use crate::{ - clock::Epoch, stake_history::{StakeHistoryEntry, StakeHistoryGetEntry, MAX_ENTRIES}, sysvar::{get_sysvar, Sysvar, SysvarId}, }; crate::declare_sysvar_id!("SysvarStakeHistory1111111111111111111111111", StakeHistory); +#[cfg(feature = "bincode")] impl Sysvar for StakeHistory { // override fn size_of() -> usize { @@ -66,9 +68,11 @@ impl Sysvar for StakeHistory { #[derive(Debug, PartialEq, Eq, Clone)] pub struct StakeHistorySysvar(pub Epoch); +#[cfg(feature = "bincode")] // precompute so we can statically allocate buffer const EPOCH_AND_ENTRY_SERIALIZED_SIZE: u64 = 32; +#[cfg(feature = "bincode")] impl StakeHistoryGetEntry for StakeHistorySysvar { fn get_entry(&self, target_epoch: Epoch) -> Option { let current_epoch = self.0; diff --git a/sdk/program/src/wasm/hash.rs b/sdk/program-core/src/wasm/hash.rs similarity index 100% rename from sdk/program/src/wasm/hash.rs rename to sdk/program-core/src/wasm/hash.rs diff --git a/sdk/program/src/wasm/instructions.rs b/sdk/program-core/src/wasm/instructions.rs similarity index 100% rename from sdk/program/src/wasm/instructions.rs rename to sdk/program-core/src/wasm/instructions.rs diff --git a/sdk/program/src/wasm/mod.rs b/sdk/program-core/src/wasm/mod.rs similarity index 95% rename from sdk/program/src/wasm/mod.rs rename to sdk/program-core/src/wasm/mod.rs index b7939a142a2d17..682e6bb9aef8a5 100644 --- a/sdk/program/src/wasm/mod.rs +++ b/sdk/program-core/src/wasm/mod.rs @@ -5,6 +5,7 @@ use wasm_bindgen::prelude::*; pub mod hash; pub mod instructions; pub mod pubkey; +#[cfg(feature = "bincode")] pub mod system_instruction; /// Initialize Javascript logging and panic handler diff --git a/sdk/program/src/wasm/pubkey.rs b/sdk/program-core/src/wasm/pubkey.rs similarity index 94% rename from sdk/program/src/wasm/pubkey.rs rename to sdk/program-core/src/wasm/pubkey.rs index 5f8733b88a6eac..2a69f275a91464 100644 --- a/sdk/program/src/wasm/pubkey.rs +++ b/sdk/program-core/src/wasm/pubkey.rs @@ -7,6 +7,7 @@ use { wasm_bindgen::{prelude::*, JsCast}, }; +#[cfg(all(feature = "curve25519", feature = "sha2"))] fn js_value_to_seeds_vec(array_of_uint8_arrays: &[JsValue]) -> Result>, JsValue> { let vec_vec_u8 = array_of_uint8_arrays .iter() @@ -64,6 +65,7 @@ impl Pubkey { self.to_string() } + #[cfg(feature = "curve25519")] /// Check if a `Pubkey` is on the ed25519 curve. pub fn isOnCurve(&self) -> bool { self.is_on_curve() @@ -79,11 +81,13 @@ impl Pubkey { self.0.clone().into() } + #[cfg(feature = "sha2")] /// Derive a Pubkey from another Pubkey, string seed, and a program id pub fn createWithSeed(base: &Pubkey, seed: &str, owner: &Pubkey) -> Result { Pubkey::create_with_seed(base, seed, owner).map_err(display_to_jsvalue) } + #[cfg(all(feature = "curve25519", feature = "sha2"))] /// Derive a program address from seeds and a program id pub fn createProgramAddress( seeds: Box<[JsValue]>, @@ -99,6 +103,7 @@ impl Pubkey { .map_err(display_to_jsvalue) } + #[cfg(all(feature = "curve25519", feature = "sha2"))] /// Find a valid program address /// /// Returns: diff --git a/sdk/program/src/wasm/system_instruction.rs b/sdk/program-core/src/wasm/system_instruction.rs similarity index 100% rename from sdk/program/src/wasm/system_instruction.rs rename to sdk/program-core/src/wasm/system_instruction.rs diff --git a/sdk/program-memory/Cargo.toml b/sdk/program-memory/Cargo.toml index f47f6adfd792ab..75e52258081cb4 100644 --- a/sdk/program-memory/Cargo.toml +++ b/sdk/program-memory/Cargo.toml @@ -10,10 +10,13 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -num-traits = { workspace = true } +num-traits = { workspace = true, optional = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [target.'cfg(target_os = "solana")'.dependencies] solana-define-syscall = { workspace = true } + +[features] +num-traits = ["dep:num-traits"] diff --git a/sdk/program-memory/src/lib.rs b/sdk/program-memory/src/lib.rs index d8bf1d47f7b2d7..376b1fc215d2dc 100644 --- a/sdk/program-memory/src/lib.rs +++ b/sdk/program-memory/src/lib.rs @@ -16,6 +16,7 @@ pub mod syscalls { /// /// Hidden to share with bpf_loader without being part of the API surface. #[doc(hidden)] +#[cfg(feature = "num-traits")] pub fn is_nonoverlapping(src: N, src_len: N, dst: N, dst_len: N) -> bool where N: Ord + num_traits::SaturatingSub, @@ -32,8 +33,10 @@ where #[cfg(not(target_os = "solana"))] #[allow(clippy::arithmetic_side_effects)] pub mod stubs { + #[cfg(feature = "num-traits")] use super::is_nonoverlapping; /// # Safety + #[cfg(feature = "num-traits")] pub unsafe fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) { // cannot be overlapping assert!( @@ -97,6 +100,7 @@ pub mod stubs { /// /// Specifying an `n` greater than either the length of `dst` or `src` will /// likely introduce undefined behavior. +#[cfg(any(target_os = "solana", feature = "num-traits"))] #[inline] pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) { #[cfg(target_os = "solana")] @@ -216,7 +220,7 @@ pub fn sol_memset(s: &mut [u8], c: u8, n: usize) { } } -#[cfg(test)] +#[cfg(all(feature = "num-traits", test))] mod tests { use super::*; diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index e6c76f6d2a42a5..d4bd7038ef8f92 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -17,28 +17,37 @@ blake3 = { workspace = true, features = ["digest", "traits-preview"] } borsh = { workspace = true, optional = true } borsh0-10 = { package = "borsh", version = "0.10.3", optional = true } bs58 = { workspace = true } -bv = { workspace = true, features = ["serde"] } -bytemuck = { workspace = true } -bytemuck_derive = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } memoffset = { workspace = true } num-derive = { workspace = true } num-traits = { workspace = true, features = ["i128"] } -qualifier_attr = { workspace = true, optional = true } serde = { workspace = true } serde_bytes = { workspace = true } serde_derive = { workspace = true } sha2 = { workspace = true } -sha3 = { workspace = true } solana-atomic-u64 = { workspace = true } solana-decode-error = { workspace = true } solana-frozen-abi = { workspace = true, optional = true } solana-frozen-abi-macro = { workspace = true, optional = true } +solana-logger = { workspace = true, optional = true } solana-msg = { workspace = true } +solana-program-core = { workspace = true, features = [ + "base64", + "bincode", + "blake3", + "borsh", + "bv", + "bytemuck", + "curve25519", + "log", + "num-traits", + "serde", + "sha2", + "sha3", +] } solana-program-memory = { workspace = true } solana-sanitize = { workspace = true } -solana-sdk-macro = { workspace = true } solana-secp256k1-recover = { workspace = true } solana-short-vec = { workspace = true } thiserror = { workspace = true } @@ -54,35 +63,19 @@ getrandom = { workspace = true, features = ["custom"] } solana-define-syscall = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] -base64 = { workspace = true, features = ["alloc", "std"] } -bitflags = { workspace = true } -curve25519-dalek = { workspace = true } num-bigint = { workspace = true } rand = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dev-dependencies] arbitrary = { workspace = true, features = ["derive"] } libsecp256k1 = { workspace = true, features = ["hmac"] } # used by doctests -solana-logger = { workspace = true } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -console_error_panic_hook = { workspace = true } -console_log = { workspace = true } -getrandom = { workspace = true, features = ["js", "wasm-bindgen"] } -js-sys = { workspace = true } -wasm-bindgen = { workspace = true } - -[target.'cfg(not(target_pointer_width = "64"))'.dependencies] -parking_lot = { workspace = true } [dev-dependencies] -anyhow = { workspace = true } array-bytes = { workspace = true } assert_matches = { workspace = true } itertools = { workspace = true } serde_json = { workspace = true } -serial_test = { workspace = true } -static_assertions = { workspace = true } +solana-program-core = { workspace = true, features = ["dev-context-only-utils"] } [build-dependencies] rustc_version = { workspace = true } @@ -95,6 +88,11 @@ crate-type = ["cdylib", "rlib"] [features] default = ["borsh"] -borsh = ["dep:borsh", "dep:borsh0-10"] -dev-context-only-utils = ["dep:qualifier_attr"] -frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", "solana-short-vec/frozen-abi"] +dev-context-only-utils = ["solana-program-core/dev-context-only-utils"] +borsh = ["dep:borsh", "dep:borsh0-10", "solana-program-core/borsh"] +frozen-abi = [ + "dep:solana-frozen-abi", + "dep:solana-frozen-abi-macro", + "dep:solana-logger", + "solana-short-vec/frozen-abi" +] diff --git a/sdk/program/src/address_lookup_table/error.rs b/sdk/program/src/address_lookup_table/error.rs deleted file mode 100644 index 9925dee4dbbf4c..00000000000000 --- a/sdk/program/src/address_lookup_table/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error, PartialEq, Eq, Clone)] -pub enum AddressLookupError { - /// Attempted to lookup addresses from a table that does not exist - #[error("Attempted to lookup addresses from a table that does not exist")] - LookupTableAccountNotFound, - - /// Attempted to lookup addresses from an account owned by the wrong program - #[error("Attempted to lookup addresses from an account owned by the wrong program")] - InvalidAccountOwner, - - /// Attempted to lookup addresses from an invalid account - #[error("Attempted to lookup addresses from an invalid account")] - InvalidAccountData, - - /// Address lookup contains an invalid index - #[error("Address lookup contains an invalid index")] - InvalidLookupIndex, -} diff --git a/sdk/program/src/last_restart_slot.rs b/sdk/program/src/last_restart_slot.rs deleted file mode 100644 index 7c67a574e93c45..00000000000000 --- a/sdk/program/src/last_restart_slot.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Information about the last restart slot (hard fork). - -use {crate::clock::Slot, solana_sdk_macro::CloneZeroed}; - -#[repr(C)] -#[derive(Serialize, Deserialize, Debug, CloneZeroed, PartialEq, Eq, Default)] -pub struct LastRestartSlot { - /// The last restart `Slot`. - pub last_restart_slot: Slot, -} diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 591c4563a973ae..436d31c29ac730 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -469,8 +469,6 @@ // Allows macro expansion of `use ::solana_program::*` to work within this crate extern crate self as solana_program; -pub mod account_info; -pub mod address_lookup_table; pub mod big_mod_exp; pub mod blake3; #[cfg(feature = "borsh")] @@ -479,58 +477,26 @@ pub mod borsh; pub mod borsh0_10; #[cfg(feature = "borsh")] pub mod borsh1; -pub mod bpf_loader; -pub mod bpf_loader_deprecated; -pub mod bpf_loader_upgradeable; -pub mod clock; -pub mod compute_units; -pub mod debug_account_data; -pub mod ed25519_program; -pub mod entrypoint; pub mod entrypoint_deprecated; -pub mod epoch_rewards; -pub mod epoch_schedule; pub mod epoch_stake; pub mod feature; -pub mod fee_calculator; -pub mod hash; pub mod incinerator; -pub mod instruction; -pub mod keccak; -pub mod lamports; -pub mod last_restart_slot; pub mod loader_instruction; -pub mod loader_upgradeable_instruction; pub mod loader_v4; pub mod loader_v4_instruction; -pub mod log; -pub mod message; pub mod native_token; -pub mod nonce; -pub mod program; -pub mod program_error; -pub mod program_option; -pub mod program_pack; -pub mod program_stubs; -pub mod program_utils; pub mod pubkey; -pub mod rent; -pub mod secp256k1_program; pub mod serde_varint; pub mod serialize_utils; -pub mod slot_hashes; -pub mod slot_history; -pub mod stable_layout; pub mod stake; -pub mod stake_history; -pub mod syscalls; -pub mod system_instruction; -pub mod system_program; -pub mod sysvar; pub mod vote; -pub mod wasm; -pub use solana_msg::msg; +#[allow(deprecated)] +pub use solana_program_core::const_pubkey as pubkey; +#[cfg(target_arch = "wasm32")] +pub use solana_program_core::wasm; +#[cfg(not(target_os = "solana"))] +pub use solana_program_core::{example_mocks, program_stubs}; #[deprecated(since = "2.1.0", note = "Use `solana-program-memory` crate instead")] pub use solana_program_memory as program_memory; #[deprecated(since = "2.1.0", note = "Use `solana-sanitize` crate instead")] @@ -541,6 +507,20 @@ pub use solana_secp256k1_recover as secp256k1_recover; pub use solana_short_vec as short_vec; #[cfg(target_arch = "wasm32")] pub use wasm_bindgen::prelude::wasm_bindgen; +pub use { + solana_msg::msg, + solana_program_core::{ + account_info, address_lookup_table, bpf_loader, bpf_loader_deprecated, + bpf_loader_upgradeable, clock, compute_units, custom_heap_default, custom_panic_default, + debug_account_data, declare_deprecated_id, declare_deprecated_sysvar_id, declare_id, + declare_sysvar_id, ed25519_program, entrypoint, epoch_rewards, epoch_schedule, + fee_calculator, hash, impl_sysvar_get, instruction, keccak, lamports, last_restart_slot, + loader_upgradeable_instruction, log, message, nonce, program, program_error, + program_option, program_pack, program_utils, rent, secp256k1_program, slot_hashes, + slot_history, stable_layout, stake_history, syscalls, system_instruction, system_program, + sysvar, + }, +}; /// The [config native program][np]. /// @@ -595,46 +575,6 @@ pub mod sdk_ids { #[deprecated(since = "2.1.0", note = "Use `solana-decode-error` crate instead")] pub use solana_decode_error as decode_error; -/// Same as [`declare_id`] except that it reports that this ID has been deprecated. -pub use solana_sdk_macro::program_declare_deprecated_id as declare_deprecated_id; -/// Convenience macro to declare a static public key and functions to interact with it. -/// -/// Input: a single literal base58 string representation of a program's ID. -/// -/// # Example -/// -/// ``` -/// # // wrapper is used so that the macro invocation occurs in the item position -/// # // rather than in the statement position which isn't allowed. -/// use std::str::FromStr; -/// use solana_program::{declare_id, pubkey::Pubkey}; -/// -/// # mod item_wrapper { -/// # use solana_program::declare_id; -/// declare_id!("My11111111111111111111111111111111111111111"); -/// # } -/// # use item_wrapper::id; -/// -/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); -/// assert_eq!(id(), my_id); -/// ``` -pub use solana_sdk_macro::program_declare_id as declare_id; -/// Convenience macro to define a static public key. -/// -/// Input: a single literal base58 string representation of a Pubkey. -/// -/// # Example -/// -/// ``` -/// use std::str::FromStr; -/// use solana_program::{pubkey, pubkey::Pubkey}; -/// -/// static ID: Pubkey = pubkey!("My11111111111111111111111111111111111111111"); -/// -/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); -/// assert_eq!(ID, my_id); -/// ``` -pub use solana_sdk_macro::program_pubkey as pubkey; #[macro_use] extern crate serde_derive; @@ -760,15 +700,6 @@ macro_rules! unchecked_div_by_const { }}; } -// This module is purposefully listed after all other exports: because of an -// interaction within rustdoc between the reexports inside this module of -// `solana_program`'s top-level modules, and `solana_sdk`'s glob re-export of -// `solana_program`'s top-level modules, if this module is not lexically last -// rustdoc fails to generate documentation for the re-exports within -// `solana_sdk`. -#[cfg(not(target_os = "solana"))] -pub mod example_mocks; - #[cfg(test)] mod tests { use super::unchecked_div_by_const; diff --git a/sdk/program/src/pubkey.rs b/sdk/program/src/pubkey.rs index 5d3433c1247f90..9c5b552ad01713 100644 --- a/sdk/program/src/pubkey.rs +++ b/sdk/program/src/pubkey.rs @@ -1,983 +1 @@ -//! Solana account addresses. - -#![allow(clippy::arithmetic_side_effects)] - -#[cfg(target_arch = "wasm32")] -use crate::wasm_bindgen; -#[cfg(test)] -use arbitrary::Arbitrary; -#[cfg(feature = "borsh")] -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use { - crate::hash::hashv, - bytemuck_derive::{Pod, Zeroable}, - num_derive::{FromPrimitive, ToPrimitive}, - solana_decode_error::DecodeError, - std::{ - convert::{Infallible, TryFrom}, - fmt, mem, - str::FromStr, - }, - thiserror::Error, -}; - -/// Number of bytes in a pubkey -pub const PUBKEY_BYTES: usize = 32; -/// maximum length of derived `Pubkey` seed -pub const MAX_SEED_LEN: usize = 32; -/// Maximum number of seeds -pub const MAX_SEEDS: usize = 16; -/// Maximum string length of a base58 encoded pubkey -const MAX_BASE58_LEN: usize = 44; - -const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress"; - -#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum PubkeyError { - /// Length of the seed is too long for address generation - #[error("Length of the seed is too long for address generation")] - MaxSeedLengthExceeded, - #[error("Provided seeds do not result in a valid address")] - InvalidSeeds, - #[error("Provided owner is not allowed")] - IllegalOwner, -} -impl DecodeError for PubkeyError { - fn type_of() -> &'static str { - "PubkeyError" - } -} -impl From for PubkeyError { - fn from(error: u64) -> Self { - match error { - 0 => PubkeyError::MaxSeedLengthExceeded, - 1 => PubkeyError::InvalidSeeds, - _ => panic!("Unsupported PubkeyError"), - } - } -} - -/// The address of a [Solana account][acc]. -/// -/// Some account addresses are [ed25519] public keys, with corresponding secret -/// keys that are managed off-chain. Often, though, account addresses do not -/// have corresponding secret keys — as with [_program derived -/// addresses_][pdas] — or the secret key is not relevant to the operation -/// of a program, and may have even been disposed of. As running Solana programs -/// can not safely create or manage secret keys, the full [`Keypair`] is not -/// defined in `solana-program` but in `solana-sdk`. -/// -/// [acc]: https://solana.com/docs/core/accounts -/// [ed25519]: https://ed25519.cr.yp.to/ -/// [pdas]: https://solana.com/docs/core/cpi#program-derived-addresses -/// [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html -#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] -#[repr(transparent)] -#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[cfg_attr( - feature = "borsh", - derive(BorshSerialize, BorshDeserialize, BorshSchema), - borsh(crate = "borsh") -)] -#[derive( - Clone, - Copy, - Default, - Deserialize, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Pod, - Serialize, - Zeroable, -)] -#[cfg_attr(test, derive(Arbitrary))] -pub struct Pubkey(pub(crate) [u8; 32]); - -impl solana_sanitize::Sanitize for Pubkey {} - -#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum ParsePubkeyError { - #[error("String is the wrong size")] - WrongSize, - #[error("Invalid Base58 string")] - Invalid, -} - -impl From for ParsePubkeyError { - fn from(_: Infallible) -> Self { - unreachable!("Infallible uninhabited"); - } -} - -impl DecodeError for ParsePubkeyError { - fn type_of() -> &'static str { - "ParsePubkeyError" - } -} - -impl FromStr for Pubkey { - type Err = ParsePubkeyError; - - fn from_str(s: &str) -> Result { - if s.len() > MAX_BASE58_LEN { - return Err(ParsePubkeyError::WrongSize); - } - let pubkey_vec = bs58::decode(s) - .into_vec() - .map_err(|_| ParsePubkeyError::Invalid)?; - if pubkey_vec.len() != mem::size_of::() { - Err(ParsePubkeyError::WrongSize) - } else { - Pubkey::try_from(pubkey_vec).map_err(|_| ParsePubkeyError::Invalid) - } - } -} - -impl From<[u8; 32]> for Pubkey { - #[inline] - fn from(from: [u8; 32]) -> Self { - Self(from) - } -} - -impl TryFrom<&[u8]> for Pubkey { - type Error = std::array::TryFromSliceError; - - #[inline] - fn try_from(pubkey: &[u8]) -> Result { - <[u8; 32]>::try_from(pubkey).map(Self::from) - } -} - -impl TryFrom> for Pubkey { - type Error = Vec; - - #[inline] - fn try_from(pubkey: Vec) -> Result { - <[u8; 32]>::try_from(pubkey).map(Self::from) - } -} - -impl TryFrom<&str> for Pubkey { - type Error = ParsePubkeyError; - fn try_from(s: &str) -> Result { - Pubkey::from_str(s) - } -} - -#[allow(clippy::used_underscore_binding)] -pub fn bytes_are_curve_point>(_bytes: T) -> bool { - #[cfg(not(target_os = "solana"))] - { - curve25519_dalek::edwards::CompressedEdwardsY::from_slice(_bytes.as_ref()) - .decompress() - .is_some() - } - #[cfg(target_os = "solana")] - unimplemented!(); -} - -impl Pubkey { - pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self { - Self(pubkey_array) - } - - /// unique Pubkey for tests and benchmarks. - pub fn new_unique() -> Self { - use solana_atomic_u64::AtomicU64; - static I: AtomicU64 = AtomicU64::new(1); - - let mut b = [0u8; 32]; - let i = I.fetch_add(1); - // use big endian representation to ensure that recent unique pubkeys - // are always greater than less recent unique pubkeys - b[0..8].copy_from_slice(&i.to_be_bytes()); - Self::from(b) - } - - pub fn create_with_seed( - base: &Pubkey, - seed: &str, - owner: &Pubkey, - ) -> Result { - if seed.len() > MAX_SEED_LEN { - return Err(PubkeyError::MaxSeedLengthExceeded); - } - - let owner = owner.as_ref(); - if owner.len() >= PDA_MARKER.len() { - let slice = &owner[owner.len() - PDA_MARKER.len()..]; - if slice == PDA_MARKER { - return Err(PubkeyError::IllegalOwner); - } - } - let hash = hashv(&[base.as_ref(), seed.as_ref(), owner]); - Ok(Pubkey::from(hash.to_bytes())) - } - - /// Find a valid [program derived address][pda] and its corresponding bump seed. - /// - /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses - /// - /// Program derived addresses (PDAs) are account keys that only the program, - /// `program_id`, has the authority to sign. The address is of the same form - /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519 - /// curve and thus have no associated private key. When performing - /// cross-program invocations the program can "sign" for the key by calling - /// [`invoke_signed`] and passing the same seeds used to generate the - /// address, along with the calculated _bump seed_, which this function - /// returns as the second tuple element. The runtime will verify that the - /// program associated with this address is the caller and thus authorized - /// to be the signer. - /// - /// [`invoke_signed`]: crate::program::invoke_signed - /// - /// The `seeds` are application-specific, and must be carefully selected to - /// uniquely derive accounts per application requirements. It is common to - /// use static strings and other pubkeys as seeds. - /// - /// Because the program address must not lie on the ed25519 curve, there may - /// be seed and program id combinations that are invalid. For this reason, - /// an extra seed (the bump seed) is calculated that results in a - /// point off the curve. The bump seed must be passed as an additional seed - /// when calling `invoke_signed`. - /// - /// The processes of finding a valid program address is by trial and error, - /// and even though it is deterministic given a set of inputs it can take a - /// variable amount of time to succeed across different inputs. This means - /// that when called from an on-chain program it may incur a variable amount - /// of the program's compute budget. Programs that are meant to be very - /// performant may not want to use this function because it could take a - /// considerable amount of time. Programs that are already at risk - /// of exceeding their compute budget should call this with care since - /// there is a chance that the program's budget may be occasionally - /// and unpredictably exceeded. - /// - /// As all account addresses accessed by an on-chain Solana program must be - /// explicitly passed to the program, it is typical for the PDAs to be - /// derived in off-chain client programs, avoiding the compute cost of - /// generating the address on-chain. The address may or may not then be - /// verified by re-deriving it on-chain, depending on the requirements of - /// the program. This verification may be performed without the overhead of - /// re-searching for the bump key by using the [`create_program_address`] - /// function. - /// - /// [`create_program_address`]: Pubkey::create_program_address - /// - /// **Warning**: Because of the way the seeds are hashed there is a potential - /// for program address collisions for the same program id. The seeds are - /// hashed sequentially which means that seeds {"abcdef"}, {"abc", "def"}, - /// and {"ab", "cd", "ef"} will all result in the same program address given - /// the same program id. Since the chance of collision is local to a given - /// program id, the developer of that program must take care to choose seeds - /// that do not collide with each other. For seed schemes that are susceptible - /// to this type of hash collision, a common remedy is to insert separators - /// between seeds, e.g. transforming {"abc", "def"} into {"abc", "-", "def"}. - /// - /// # Panics - /// - /// Panics in the statistically improbable event that a bump seed could not be - /// found. Use [`try_find_program_address`] to handle this case. - /// - /// [`try_find_program_address`]: Pubkey::try_find_program_address - /// - /// Panics if any of the following are true: - /// - /// - the number of provided seeds is greater than, _or equal to_, [`MAX_SEEDS`], - /// - any individual seed's length is greater than [`MAX_SEED_LEN`]. - /// - /// # Examples - /// - /// This example illustrates a simple case of creating a "vault" account - /// which is derived from the payer account, but owned by an on-chain - /// program. The program derived address is derived in an off-chain client - /// program, which invokes an on-chain Solana program that uses the address - /// to create a new account owned and controlled by the program itself. - /// - /// By convention, the on-chain program will be compiled for use in two - /// different contexts: both on-chain, to interpret a custom program - /// instruction as a Solana transaction; and off-chain, as a library, so - /// that clients can share the instruction data structure, constructors, and - /// other common code. - /// - /// First the on-chain Solana program: - /// - /// ``` - /// # use borsh::{BorshSerialize, BorshDeserialize}; - /// # use solana_program::{ - /// # pubkey::Pubkey, - /// # entrypoint::ProgramResult, - /// # program::invoke_signed, - /// # system_instruction, - /// # account_info::{ - /// # AccountInfo, - /// # next_account_info, - /// # }, - /// # }; - /// // The custom instruction processed by our program. It includes the - /// // PDA's bump seed, which is derived by the client program. This - /// // definition is also imported into the off-chain client program. - /// // The computed address of the PDA will be passed to this program via - /// // the `accounts` vector of the `Instruction` type. - /// #[derive(BorshSerialize, BorshDeserialize, Debug)] - /// # #[borsh(crate = "borsh")] - /// pub struct InstructionData { - /// pub vault_bump_seed: u8, - /// pub lamports: u64, - /// } - /// - /// // The size in bytes of a vault account. The client program needs - /// // this information to calculate the quantity of lamports necessary - /// // to pay for the account's rent. - /// pub static VAULT_ACCOUNT_SIZE: u64 = 1024; - /// - /// // The entrypoint of the on-chain program, as provided to the - /// // `entrypoint!` macro. - /// fn process_instruction( - /// program_id: &Pubkey, - /// accounts: &[AccountInfo], - /// instruction_data: &[u8], - /// ) -> ProgramResult { - /// let account_info_iter = &mut accounts.iter(); - /// let payer = next_account_info(account_info_iter)?; - /// // The vault PDA, derived from the payer's address - /// let vault = next_account_info(account_info_iter)?; - /// - /// let mut instruction_data = instruction_data; - /// let instr = InstructionData::deserialize(&mut instruction_data)?; - /// let vault_bump_seed = instr.vault_bump_seed; - /// let lamports = instr.lamports; - /// let vault_size = VAULT_ACCOUNT_SIZE; - /// - /// // Invoke the system program to create an account while virtually - /// // signing with the vault PDA, which is owned by this caller program. - /// invoke_signed( - /// &system_instruction::create_account( - /// &payer.key, - /// &vault.key, - /// lamports, - /// vault_size, - /// &program_id, - /// ), - /// &[ - /// payer.clone(), - /// vault.clone(), - /// ], - /// // A slice of seed slices, each seed slice being the set - /// // of seeds used to generate one of the PDAs required by the - /// // callee program, the final seed being a single-element slice - /// // containing the `u8` bump seed. - /// &[ - /// &[ - /// b"vault", - /// payer.key.as_ref(), - /// &[vault_bump_seed], - /// ], - /// ] - /// )?; - /// - /// Ok(()) - /// } - /// ``` - /// - /// The client program: - /// - /// ``` - /// # use borsh::{BorshSerialize, BorshDeserialize}; - /// # use solana_program::example_mocks::{solana_sdk, solana_rpc_client}; - /// # use solana_program::{ - /// # pubkey::Pubkey, - /// # instruction::Instruction, - /// # hash::Hash, - /// # instruction::AccountMeta, - /// # system_program, - /// # }; - /// # use solana_sdk::{ - /// # signature::Keypair, - /// # signature::{Signer, Signature}, - /// # transaction::Transaction, - /// # }; - /// # use solana_rpc_client::rpc_client::RpcClient; - /// # use std::convert::TryFrom; - /// # use anyhow::Result; - /// # - /// # #[derive(BorshSerialize, BorshDeserialize, Debug)] - /// # #[borsh(crate = "borsh")] - /// # struct InstructionData { - /// # pub vault_bump_seed: u8, - /// # pub lamports: u64, - /// # } - /// # - /// # pub static VAULT_ACCOUNT_SIZE: u64 = 1024; - /// # - /// fn create_vault_account( - /// client: &RpcClient, - /// program_id: Pubkey, - /// payer: &Keypair, - /// ) -> Result<()> { - /// // Derive the PDA from the payer account, a string representing the unique - /// // purpose of the account ("vault"), and the address of our on-chain program. - /// let (vault_pubkey, vault_bump_seed) = Pubkey::find_program_address( - /// &[b"vault", payer.pubkey().as_ref()], - /// &program_id - /// ); - /// - /// // Get the amount of lamports needed to pay for the vault's rent - /// let vault_account_size = usize::try_from(VAULT_ACCOUNT_SIZE)?; - /// let lamports = client.get_minimum_balance_for_rent_exemption(vault_account_size)?; - /// - /// // The on-chain program's instruction data, imported from that program's crate. - /// let instr_data = InstructionData { - /// vault_bump_seed, - /// lamports, - /// }; - /// - /// // The accounts required by both our on-chain program and the system program's - /// // `create_account` instruction, including the vault's address. - /// let accounts = vec![ - /// AccountMeta::new(payer.pubkey(), true), - /// AccountMeta::new(vault_pubkey, false), - /// AccountMeta::new(system_program::ID, false), - /// ]; - /// - /// // Create the instruction by serializing our instruction data via borsh - /// let instruction = Instruction::new_with_borsh( - /// program_id, - /// &instr_data, - /// accounts, - /// ); - /// - /// let blockhash = client.get_latest_blockhash()?; - /// - /// let transaction = Transaction::new_signed_with_payer( - /// &[instruction], - /// Some(&payer.pubkey()), - /// &[payer], - /// blockhash, - /// ); - /// - /// client.send_and_confirm_transaction(&transaction)?; - /// - /// Ok(()) - /// } - /// # let program_id = Pubkey::new_unique(); - /// # let payer = Keypair::new(); - /// # let client = RpcClient::new(String::new()); - /// # - /// # create_vault_account(&client, program_id, &payer)?; - /// # - /// # Ok::<(), anyhow::Error>(()) - /// ``` - pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) { - Self::try_find_program_address(seeds, program_id) - .unwrap_or_else(|| panic!("Unable to find a viable program address bump seed")) - } - - /// Find a valid [program derived address][pda] and its corresponding bump seed. - /// - /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses - /// - /// The only difference between this method and [`find_program_address`] - /// is that this one returns `None` in the statistically improbable event - /// that a bump seed cannot be found; or if any of `find_program_address`'s - /// preconditions are violated. - /// - /// See the documentation for [`find_program_address`] for a full description. - /// - /// [`find_program_address`]: Pubkey::find_program_address - #[allow(clippy::same_item_push)] - pub fn try_find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> Option<(Pubkey, u8)> { - // Perform the calculation inline, calling this from within a program is - // not supported - #[cfg(not(target_os = "solana"))] - { - let mut bump_seed = [u8::MAX]; - for _ in 0..u8::MAX { - { - let mut seeds_with_bump = seeds.to_vec(); - seeds_with_bump.push(&bump_seed); - match Self::create_program_address(&seeds_with_bump, program_id) { - Ok(address) => return Some((address, bump_seed[0])), - Err(PubkeyError::InvalidSeeds) => (), - _ => break, - } - } - bump_seed[0] -= 1; - } - None - } - // Call via a system call to perform the calculation - #[cfg(target_os = "solana")] - { - let mut bytes = [0; 32]; - let mut bump_seed = u8::MAX; - let result = unsafe { - crate::syscalls::sol_try_find_program_address( - seeds as *const _ as *const u8, - seeds.len() as u64, - program_id as *const _ as *const u8, - &mut bytes as *mut _ as *mut u8, - &mut bump_seed as *mut _ as *mut u8, - ) - }; - match result { - crate::entrypoint::SUCCESS => Some((Pubkey::from(bytes), bump_seed)), - _ => None, - } - } - } - - /// Create a valid [program derived address][pda] without searching for a bump seed. - /// - /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses - /// - /// Because this function does not create a bump seed, it may unpredictably - /// return an error for any given set of seeds and is not generally suitable - /// for creating program derived addresses. - /// - /// However, it can be used for efficiently verifying that a set of seeds plus - /// bump seed generated by [`find_program_address`] derives a particular - /// address as expected. See the example for details. - /// - /// See the documentation for [`find_program_address`] for a full description - /// of program derived addresses and bump seeds. - /// - /// [`find_program_address`]: Pubkey::find_program_address - /// - /// # Examples - /// - /// Creating a program derived address involves iteratively searching for a - /// bump seed for which the derived [`Pubkey`] does not lie on the ed25519 - /// curve. This search process is generally performed off-chain, with the - /// [`find_program_address`] function, after which the client passes the - /// bump seed to the program as instruction data. - /// - /// Depending on the application requirements, a program may wish to verify - /// that the set of seeds, plus the bump seed, do correctly generate an - /// expected address. - /// - /// The verification is performed by appending to the other seeds one - /// additional seed slice that contains the single `u8` bump seed, calling - /// `create_program_address`, checking that the return value is `Ok`, and - /// that the returned `Pubkey` has the expected value. - /// - /// ``` - /// # use solana_program::pubkey::Pubkey; - /// # let program_id = Pubkey::new_unique(); - /// let (expected_pda, bump_seed) = Pubkey::find_program_address(&[b"vault"], &program_id); - /// let actual_pda = Pubkey::create_program_address(&[b"vault", &[bump_seed]], &program_id)?; - /// assert_eq!(expected_pda, actual_pda); - /// # Ok::<(), anyhow::Error>(()) - /// ``` - pub fn create_program_address( - seeds: &[&[u8]], - program_id: &Pubkey, - ) -> Result { - if seeds.len() > MAX_SEEDS { - return Err(PubkeyError::MaxSeedLengthExceeded); - } - for seed in seeds.iter() { - if seed.len() > MAX_SEED_LEN { - return Err(PubkeyError::MaxSeedLengthExceeded); - } - } - - // Perform the calculation inline, calling this from within a program is - // not supported - #[cfg(not(target_os = "solana"))] - { - let mut hasher = crate::hash::Hasher::default(); - for seed in seeds.iter() { - hasher.hash(seed); - } - hasher.hashv(&[program_id.as_ref(), PDA_MARKER]); - let hash = hasher.result(); - - if bytes_are_curve_point(hash) { - return Err(PubkeyError::InvalidSeeds); - } - - Ok(Pubkey::from(hash.to_bytes())) - } - // Call via a system call to perform the calculation - #[cfg(target_os = "solana")] - { - let mut bytes = [0; 32]; - let result = unsafe { - crate::syscalls::sol_create_program_address( - seeds as *const _ as *const u8, - seeds.len() as u64, - program_id as *const _ as *const u8, - &mut bytes as *mut _ as *mut u8, - ) - }; - match result { - crate::entrypoint::SUCCESS => Ok(Pubkey::from(bytes)), - _ => Err(result.into()), - } - } - } - - pub const fn to_bytes(self) -> [u8; 32] { - self.0 - } - - pub fn is_on_curve(&self) -> bool { - bytes_are_curve_point(self) - } - - /// Log a `Pubkey` from a program - pub fn log(&self) { - #[cfg(target_os = "solana")] - unsafe { - crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8) - }; - - #[cfg(not(target_os = "solana"))] - crate::program_stubs::sol_log(&self.to_string()); - } -} - -impl AsRef<[u8]> for Pubkey { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Pubkey { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl fmt::Debug for Pubkey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -impl fmt::Display for Pubkey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -#[cfg(feature = "borsh")] -impl borsh0_10::de::BorshDeserialize for Pubkey { - fn deserialize_reader( - reader: &mut R, - ) -> ::core::result::Result { - Ok(Self(borsh0_10::BorshDeserialize::deserialize_reader( - reader, - )?)) - } -} - -#[cfg(feature = "borsh")] -macro_rules! impl_borsh_schema { - ($borsh:ident) => { - impl $borsh::BorshSchema for Pubkey - where - [u8; 32]: $borsh::BorshSchema, - { - fn declaration() -> $borsh::schema::Declaration { - "Pubkey".to_string() - } - fn add_definitions_recursively( - definitions: &mut $borsh::maybestd::collections::HashMap< - $borsh::schema::Declaration, - $borsh::schema::Definition, - >, - ) { - let fields = $borsh::schema::Fields::UnnamedFields(<[_]>::into_vec( - $borsh::maybestd::boxed::Box::new([ - <[u8; 32] as $borsh::BorshSchema>::declaration(), - ]), - )); - let definition = $borsh::schema::Definition::Struct { fields }; - ::add_definition( - ::declaration(), - definition, - definitions, - ); - <[u8; 32] as $borsh::BorshSchema>::add_definitions_recursively(definitions); - } - } - }; -} -#[cfg(feature = "borsh")] -impl_borsh_schema!(borsh0_10); - -#[cfg(feature = "borsh")] -macro_rules! impl_borsh_serialize { - ($borsh:ident) => { - impl $borsh::ser::BorshSerialize for Pubkey { - fn serialize( - &self, - writer: &mut W, - ) -> ::core::result::Result<(), $borsh::maybestd::io::Error> { - $borsh::BorshSerialize::serialize(&self.0, writer)?; - Ok(()) - } - } - }; -} -#[cfg(feature = "borsh")] -impl_borsh_serialize!(borsh0_10); - -#[cfg(test)] -mod tests { - use {super::*, std::str::from_utf8}; - - #[test] - fn test_new_unique() { - assert!(Pubkey::new_unique() != Pubkey::new_unique()); - } - - #[test] - fn pubkey_fromstr() { - let pubkey = Pubkey::new_unique(); - let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); - - assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); - - pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string()); - assert_eq!( - pubkey_base58_str.parse::(), - Err(ParsePubkeyError::WrongSize) - ); - - pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); - assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); - - pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); - assert_eq!( - pubkey_base58_str.parse::(), - Err(ParsePubkeyError::WrongSize) - ); - - let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); - assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); - - // throw some non-base58 stuff in there - pubkey_base58_str.replace_range(..1, "I"); - assert_eq!( - pubkey_base58_str.parse::(), - Err(ParsePubkeyError::Invalid) - ); - - // too long input string - // longest valid encoding - let mut too_long = bs58::encode(&[255u8; PUBKEY_BYTES]).into_string(); - // and one to grow on - too_long.push('1'); - assert_eq!(too_long.parse::(), Err(ParsePubkeyError::WrongSize)); - } - - #[test] - fn test_create_with_seed() { - assert!( - Pubkey::create_with_seed(&Pubkey::new_unique(), "☉", &Pubkey::new_unique()).is_ok() - ); - assert_eq!( - Pubkey::create_with_seed( - &Pubkey::new_unique(), - from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(), - &Pubkey::new_unique() - ), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - assert!(Pubkey::create_with_seed( - &Pubkey::new_unique(), - "\ - \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ - ", - &Pubkey::new_unique() - ) - .is_ok()); - // utf-8 abuse ;) - assert_eq!( - Pubkey::create_with_seed( - &Pubkey::new_unique(), - "\ - x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ - ", - &Pubkey::new_unique() - ), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - - assert!(Pubkey::create_with_seed( - &Pubkey::new_unique(), - std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(), - &Pubkey::new_unique(), - ) - .is_ok()); - - assert!( - Pubkey::create_with_seed(&Pubkey::new_unique(), "", &Pubkey::new_unique(),).is_ok() - ); - - assert_eq!( - Pubkey::create_with_seed( - &Pubkey::default(), - "limber chicken: 4/45", - &Pubkey::default(), - ), - Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq" - .parse() - .unwrap()) - ); - } - - #[test] - fn test_create_program_address() { - let exceeded_seed = &[127; MAX_SEED_LEN + 1]; - let max_seed = &[0; MAX_SEED_LEN]; - let exceeded_seeds: &[&[u8]] = &[ - &[1], - &[2], - &[3], - &[4], - &[5], - &[6], - &[7], - &[8], - &[9], - &[10], - &[11], - &[12], - &[13], - &[14], - &[15], - &[16], - &[17], - ]; - let max_seeds: &[&[u8]] = &[ - &[1], - &[2], - &[3], - &[4], - &[5], - &[6], - &[7], - &[8], - &[9], - &[10], - &[11], - &[12], - &[13], - &[14], - &[15], - &[16], - ]; - let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap(); - let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap(); - - assert_eq!( - Pubkey::create_program_address(&[exceeded_seed], &program_id), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - assert_eq!( - Pubkey::create_program_address(&[b"short_seed", exceeded_seed], &program_id), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok()); - assert_eq!( - Pubkey::create_program_address(exceeded_seeds, &program_id), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - assert!(Pubkey::create_program_address(max_seeds, &program_id).is_ok()); - assert_eq!( - Pubkey::create_program_address(&[b"", &[1]], &program_id), - Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id), - Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id), - Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id), - Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL" - .parse() - .unwrap()) - ); - assert_ne!( - Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id).unwrap(), - Pubkey::create_program_address(&[b"Talking"], &program_id).unwrap(), - ); - } - - #[test] - fn test_pubkey_off_curve() { - // try a bunch of random input, all successful generated program - // addresses must land off the curve and be unique - let mut addresses = vec![]; - for _ in 0..1_000 { - let program_id = Pubkey::new_unique(); - let bytes1 = rand::random::<[u8; 10]>(); - let bytes2 = rand::random::<[u8; 32]>(); - if let Ok(program_address) = - Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id) - { - let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice( - &program_address.to_bytes(), - ) - .decompress() - .is_some(); - assert!(!is_on_curve); - assert!(!addresses.contains(&program_address)); - addresses.push(program_address); - } - } - } - - #[test] - fn test_find_program_address() { - for _ in 0..1_000 { - let program_id = Pubkey::new_unique(); - let (address, bump_seed) = - Pubkey::find_program_address(&[b"Lil'", b"Bits"], &program_id); - assert_eq!( - address, - Pubkey::create_program_address(&[b"Lil'", b"Bits", &[bump_seed]], &program_id) - .unwrap() - ); - } - } - - fn pubkey_from_seed_by_marker(marker: &[u8]) -> Result { - let key = Pubkey::new_unique(); - let owner = Pubkey::default(); - - let mut to_fake = owner.to_bytes().to_vec(); - to_fake.extend_from_slice(marker); - - let seed = &String::from_utf8(to_fake[..to_fake.len() - 32].to_vec()).expect("not utf8"); - let base = &Pubkey::try_from(&to_fake[to_fake.len() - 32..]).unwrap(); - - Pubkey::create_with_seed(&key, seed, base) - } - - #[test] - fn test_create_with_seed_rejects_illegal_owner() { - assert_eq!( - pubkey_from_seed_by_marker(PDA_MARKER), - Err(PubkeyError::IllegalOwner) - ); - assert!(pubkey_from_seed_by_marker(&PDA_MARKER[1..]).is_ok()); - } -} +pub use solana_program_core::pubkey::*; diff --git a/sdk/program/src/serialize_utils/mod.rs b/sdk/program/src/serialize_utils/mod.rs index 6b1396f3481d5c..027dce57a19e0d 100644 --- a/sdk/program/src/serialize_utils/mod.rs +++ b/sdk/program/src/serialize_utils/mod.rs @@ -1,70 +1,2 @@ -//! Helpers for reading and writing bytes. - -#![allow(clippy::arithmetic_side_effects)] -use {crate::pubkey::Pubkey, solana_sanitize::SanitizeError}; - pub mod cursor; - -pub fn append_u16(buf: &mut Vec, data: u16) { - let start = buf.len(); - buf.resize(buf.len() + 2, 0); - let end = buf.len(); - buf[start..end].copy_from_slice(&data.to_le_bytes()); -} - -pub fn append_u8(buf: &mut Vec, data: u8) { - let start = buf.len(); - buf.resize(buf.len() + 1, 0); - buf[start] = data; -} - -pub fn append_slice(buf: &mut Vec, data: &[u8]) { - let start = buf.len(); - buf.resize(buf.len() + data.len(), 0); - let end = buf.len(); - buf[start..end].copy_from_slice(data); -} - -pub fn read_u8(current: &mut usize, data: &[u8]) -> Result { - if data.len() < *current + 1 { - return Err(SanitizeError::IndexOutOfBounds); - } - let e = data[*current]; - *current += 1; - Ok(e) -} - -pub fn read_pubkey(current: &mut usize, data: &[u8]) -> Result { - let len = std::mem::size_of::(); - if data.len() < *current + len { - return Err(SanitizeError::IndexOutOfBounds); - } - let e = Pubkey::try_from(&data[*current..*current + len]) - .map_err(|_| SanitizeError::ValueOutOfBounds)?; - *current += len; - Ok(e) -} - -pub fn read_u16(current: &mut usize, data: &[u8]) -> Result { - if data.len() < *current + 2 { - return Err(SanitizeError::IndexOutOfBounds); - } - let mut fixed_data = [0u8; 2]; - fixed_data.copy_from_slice(&data[*current..*current + 2]); - let e = u16::from_le_bytes(fixed_data); - *current += 2; - Ok(e) -} - -pub fn read_slice( - current: &mut usize, - data: &[u8], - data_len: usize, -) -> Result, SanitizeError> { - if data.len() < *current + data_len { - return Err(SanitizeError::IndexOutOfBounds); - } - let e = data[*current..*current + data_len].to_vec(); - *current += data_len; - Ok(e) -} +pub use solana_program_core::serialize_utils::*; diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index c021789b507da8..3127675f0d550b 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -113,6 +113,23 @@ pub mod wasm; pub use solana_bn254 as alt_bn128; #[deprecated(since = "2.1.0", note = "Use `solana-decode-error` crate instead")] pub use solana_decode_error as decode_error; +/// Convenience macro to define a static public key. +/// +/// Input: a single literal base58 string representation of a Pubkey +/// +/// # Example +/// +/// ``` +/// use std::str::FromStr; +/// use solana_program::{pubkey, pubkey::Pubkey}; +/// +/// static ID: Pubkey = pubkey!("My11111111111111111111111111111111111111111"); +/// +/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); +/// assert_eq!(ID, my_id); +/// ``` +#[allow(deprecated)] +pub use solana_program_core::const_pubkey as pubkey; #[deprecated(since = "2.1.0", note = "Use `solana-program-memory` crate instead")] pub use solana_program_memory as program_memory; #[deprecated(since = "2.1.0", note = "Use `solana-sanitize` crate instead")] @@ -141,22 +158,6 @@ pub use solana_sdk_macro::declare_deprecated_id; /// assert_eq!(id(), my_id); /// ``` pub use solana_sdk_macro::declare_id; -/// Convenience macro to define a static public key. -/// -/// Input: a single literal base58 string representation of a Pubkey -/// -/// # Example -/// -/// ``` -/// use std::str::FromStr; -/// use solana_program::{pubkey, pubkey::Pubkey}; -/// -/// static ID: Pubkey = pubkey!("My11111111111111111111111111111111111111111"); -/// -/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); -/// assert_eq!(ID, my_id); -/// ``` -pub use solana_sdk_macro::pubkey; /// Convenience macro to define multiple static public keys. pub use solana_sdk_macro::pubkeys; #[deprecated(since = "2.1.0", note = "Use `solana-secp256k1-recover` crate instead")] diff --git a/turbine/benches/cluster_info.rs b/turbine/benches/cluster_info.rs index 1f15137175acdb..fa7e8a96e6db31 100644 --- a/turbine/benches/cluster_info.rs +++ b/turbine/benches/cluster_info.rs @@ -2,6 +2,8 @@ extern crate test; +#[allow(deprecated)] +use solana_sdk::pubkey; use { rand::{thread_rng, Rng}, solana_gossip::{ @@ -14,7 +16,6 @@ use { }, solana_runtime::{bank::Bank, bank_forks::BankForks}, solana_sdk::{ - pubkey, signature::{Keypair, Signer}, timing::{timestamp, AtomicInterval}, },