From 63db3242043602c7b9765a066b6f8d01ef20454e Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 19 Oct 2020 13:19:24 -0700 Subject: [PATCH] Initial population of solana-program-sdk --- Cargo.lock | 32 +- ci/nits.sh | 4 +- ci/test-stable.sh | 3 + programs/bpf/Cargo.lock | 29 +- sdk/Cargo.toml | 25 +- sdk/Xargo.toml | 2 + sdk/macro/src/lib.rs | 127 ++++-- sdk/program/Cargo.toml | 29 ++ sdk/program/Xargo.toml | 2 + sdk/program/build.rs | 1 + sdk/{ => program}/src/account.rs | 2 +- sdk/{ => program}/src/account_info.rs | 0 sdk/{ => program}/src/account_utils.rs | 0 sdk/{ => program}/src/bpf_loader.rs | 0 .../src/bpf_loader_deprecated.rs | 0 sdk/{ => program}/src/clock.rs | 0 sdk/{ => program}/src/decode_error.rs | 0 sdk/program/src/entrypoint.rs | 257 +++++++++++ sdk/program/src/entrypoint_deprecated.rs | 137 ++++++ sdk/{ => program}/src/epoch_schedule.rs | 0 sdk/{ => program}/src/fee_calculator.rs | 0 sdk/program/src/hash.rs | 186 ++++++++ sdk/{ => program}/src/incinerator.rs | 0 sdk/{ => program}/src/instruction.rs | 0 sdk/program/src/lib.rs | 69 +++ sdk/{ => program}/src/loader_instruction.rs | 0 sdk/program/src/log.rs | 98 ++++ sdk/{ => program}/src/message.rs | 70 ++- sdk/{ => program}/src/native_token.rs | 0 sdk/{ => program}/src/nonce/account.rs | 3 +- sdk/{ => program}/src/nonce/mod.rs | 0 sdk/{ => program}/src/nonce/state/current.rs | 0 sdk/{ => program}/src/nonce/state/mod.rs | 0 sdk/{ => program}/src/nonce/utils.rs | 4 +- sdk/{ => program}/src/program.rs | 2 - sdk/{ => program}/src/program_error.rs | 6 +- sdk/{ => program}/src/program_option.rs | 0 sdk/{ => program}/src/program_pack.rs | 0 sdk/{ => program}/src/program_stubs.rs | 9 +- sdk/program/src/pubkey.rs | 426 ++++++++++++++++++ sdk/{ => program}/src/rent.rs | 3 +- sdk/{ => program}/src/sanitize.rs | 0 sdk/program/src/secp256k1_program.rs | 1 + sdk/{ => program}/src/serialize_utils.rs | 0 sdk/{ => program}/src/short_vec.rs | 0 sdk/{ => program}/src/slot_hashes.rs | 0 sdk/{ => program}/src/slot_history.rs | 0 sdk/{ => program}/src/stake_history.rs | 0 sdk/{ => program}/src/system_instruction.rs | 10 +- sdk/{ => program}/src/system_program.rs | 0 sdk/{ => program}/src/sysvar/clock.rs | 0 .../src/sysvar/epoch_schedule.rs | 0 sdk/{ => program}/src/sysvar/fees.rs | 0 sdk/{ => program}/src/sysvar/instructions.rs | 7 +- sdk/{ => program}/src/sysvar/mod.rs | 0 .../src/sysvar/recent_blockhashes.rs | 0 sdk/{ => program}/src/sysvar/rent.rs | 0 sdk/{ => program}/src/sysvar/rewards.rs | 0 sdk/{ => program}/src/sysvar/slot_hashes.rs | 0 sdk/{ => program}/src/sysvar/slot_history.rs | 0 sdk/{ => program}/src/sysvar/stake_history.rs | 0 sdk/src/.lib.rs.swo | Bin 0 -> 16384 bytes sdk/src/client.rs | 2 + sdk/src/entrypoint.rs | 239 +--------- sdk/src/entrypoint_deprecated.rs | 126 +----- sdk/src/genesis_config.rs | 2 + sdk/src/hard_forks.rs | 2 + sdk/src/hash.rs | 183 +------- sdk/src/lib.rs | 83 +--- sdk/src/log.rs | 86 +--- sdk/src/pubkey.rs | 417 +---------------- sdk/src/secp256k1.rs | 2 + sdk/src/secp256k1_program.rs | 1 - sdk/src/shred_version.rs | 2 + sdk/src/signature.rs | 1 + sdk/src/signers.rs | 1 + sdk/src/system_transaction.rs | 1 + sdk/src/transaction.rs | 2 + sdk/src/transport.rs | 2 + 79 files changed, 1475 insertions(+), 1221 deletions(-) create mode 100644 sdk/Xargo.toml create mode 100644 sdk/program/Xargo.toml create mode 120000 sdk/program/build.rs rename sdk/{ => program}/src/account.rs (99%) rename sdk/{ => program}/src/account_info.rs (100%) rename sdk/{ => program}/src/account_utils.rs (100%) rename sdk/{ => program}/src/bpf_loader.rs (100%) rename sdk/{ => program}/src/bpf_loader_deprecated.rs (100%) rename sdk/{ => program}/src/clock.rs (100%) rename sdk/{ => program}/src/decode_error.rs (100%) create mode 100644 sdk/program/src/entrypoint.rs create mode 100644 sdk/program/src/entrypoint_deprecated.rs rename sdk/{ => program}/src/epoch_schedule.rs (100%) rename sdk/{ => program}/src/fee_calculator.rs (100%) create mode 100644 sdk/program/src/hash.rs rename sdk/{ => program}/src/incinerator.rs (100%) rename sdk/{ => program}/src/instruction.rs (100%) rename sdk/{ => program}/src/loader_instruction.rs (100%) create mode 100644 sdk/program/src/log.rs rename sdk/{ => program}/src/message.rs (93%) rename sdk/{ => program}/src/native_token.rs (100%) rename sdk/{ => program}/src/nonce/account.rs (99%) rename sdk/{ => program}/src/nonce/mod.rs (100%) rename sdk/{ => program}/src/nonce/state/current.rs (100%) rename sdk/{ => program}/src/nonce/state/mod.rs (100%) rename sdk/{ => program}/src/nonce/utils.rs (98%) rename sdk/{ => program}/src/program.rs (98%) rename sdk/{ => program}/src/program_error.rs (98%) rename sdk/{ => program}/src/program_option.rs (100%) rename sdk/{ => program}/src/program_pack.rs (100%) rename sdk/{ => program}/src/program_stubs.rs (89%) create mode 100644 sdk/program/src/pubkey.rs rename sdk/{ => program}/src/rent.rs (97%) rename sdk/{ => program}/src/sanitize.rs (100%) create mode 100644 sdk/program/src/secp256k1_program.rs rename sdk/{ => program}/src/serialize_utils.rs (100%) rename sdk/{ => program}/src/short_vec.rs (100%) rename sdk/{ => program}/src/slot_hashes.rs (100%) rename sdk/{ => program}/src/slot_history.rs (100%) rename sdk/{ => program}/src/stake_history.rs (100%) rename sdk/{ => program}/src/system_instruction.rs (98%) rename sdk/{ => program}/src/system_program.rs (100%) rename sdk/{ => program}/src/sysvar/clock.rs (100%) rename sdk/{ => program}/src/sysvar/epoch_schedule.rs (100%) rename sdk/{ => program}/src/sysvar/fees.rs (100%) rename sdk/{ => program}/src/sysvar/instructions.rs (85%) rename sdk/{ => program}/src/sysvar/mod.rs (100%) rename sdk/{ => program}/src/sysvar/recent_blockhashes.rs (100%) rename sdk/{ => program}/src/sysvar/rent.rs (100%) rename sdk/{ => program}/src/sysvar/rewards.rs (100%) rename sdk/{ => program}/src/sysvar/slot_hashes.rs (100%) rename sdk/{ => program}/src/sysvar/slot_history.rs (100%) rename sdk/{ => program}/src/sysvar/stake_history.rs (100%) create mode 100644 sdk/src/.lib.rs.swo delete mode 100644 sdk/src/secp256k1_program.rs diff --git a/Cargo.lock b/Cargo.lock index 1a1214610b5bf9..560922aa2823f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4379,6 +4379,36 @@ dependencies = [ "solana-version", ] +[[package]] +name = "solana-program-sdk" +version = "1.5.0" +dependencies = [ + "assert_matches", + "bincode", + "bs58", + "bv", + "curve25519-dalek 2.1.0", + "hex", + "itertools 0.9.0", + "lazy_static", + "log 0.4.8", + "num-derive", + "num-traits", + "rand 0.7.3", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger 1.5.0", + "solana-sdk-macro 1.5.0", + "thiserror", +] + [[package]] name = "solana-ramp-tps" version = "1.5.0" @@ -4545,7 +4575,6 @@ dependencies = [ "hex", "hmac", "itertools 0.9.0", - "lazy_static", "libsecp256k1", "log 0.4.8", "memmap", @@ -4566,6 +4595,7 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger 1.5.0", + "solana-program-sdk", "solana-sdk-macro 1.5.0", "thiserror", "tiny-bip39", diff --git a/ci/nits.sh b/ci/nits.sh index 191b06c29a017f..2162d8fae0460f 100755 --- a/ci/nits.sh +++ b/ci/nits.sh @@ -27,8 +27,8 @@ declare print_free_tree=( ':sdk/bpf/rust/rust-utils/**.rs' ':sdk/**.rs' ':^sdk/cargo-build-bpf/**.rs' - ':^sdk/src/program_option.rs' - ':^sdk/src/program_stubs.rs' + ':^sdk/program/src/program_option.rs' + ':^sdk/program/src/program_stubs.rs' ':programs/**.rs' ':^**bin**.rs' ':^**bench**.rs' diff --git a/ci/test-stable.sh b/ci/test-stable.sh index 8b7edfabf34ac1..bd50c27d6273ec 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -40,6 +40,9 @@ test-stable) _ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture ;; test-stable-perf) + # BPF solana-sdk legacy compile test + ./cargo-build-bpf --manifest-path sdk/Cargo.toml --no-default-features --features program + # BPF program tests _ make -C programs/bpf/c tests _ cargo +"$rust_stable" test \ diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index e3899495032b3e..78c21e4d29d371 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2110,6 +2110,32 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-program-sdk" +version = "1.5.0" +dependencies = [ + "bincode", + "bs58", + "curve25519-dalek 2.1.0", + "hex", + "lazy_static", + "log", + "num-derive 0.3.0", + "num-traits", + "rand", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "sha2", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-sdk-macro", + "thiserror", +] + [[package]] name = "solana-rayon-threadlimit" version = "1.5.0" @@ -2177,14 +2203,12 @@ dependencies = [ "bv", "byteorder 1.3.4", "chrono", - "curve25519-dalek 2.1.0", "digest 0.9.0", "ed25519-dalek", "generic-array 0.14.3", "hex", "hmac", "itertools", - "lazy_static", "libsecp256k1", "log", "memmap", @@ -2205,6 +2229,7 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", + "solana-program-sdk", "solana-sdk-macro", "thiserror", ] diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index c5922ce623b7f5..fe9be5b634a573 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -9,9 +9,12 @@ license = "Apache-2.0" edition = "2018" [features] -# On-chain program specific dependencies +# "program" feature is a legacy feature retained to support v1.3 and older +# programs. New development should not use this feature. Instead use the +# solana-program-sdk crate program = [] -# Everything includes functionality that is not compatible or needed for on-chain programs + +# "everything" includes functionality that is not compatible or needed for on-chain programs default = [ "everything" ] @@ -43,12 +46,11 @@ curve25519-dalek = { version = "2.1.0", optional = true } generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true } hex = "0.4.2" hmac = "0.7.0" -itertools = { version = "0.9.0" } -lazy_static = "1.4.0" -log = { version = "0.4.8" } +itertools = "0.9.0" +log = "0.4.8" memmap = { version = "0.7.0", optional = true } -num-derive = { version = "0.3" } -num-traits = { version = "0.2" } +num-derive = "0.3" +num-traits = "0.2" pbkdf2 = { version = "0.3.0", default-features = false } rand = { version = "0.7.0", optional = true } rand_chacha = { version = "0.2.2", optional = true } @@ -70,15 +72,12 @@ libsecp256k1 = { version = "0.3.5", optional = true } sha3 = { version = "0.9.1", optional = true } digest = { version = "0.9.0", optional = true } -[target.'cfg(not(target_arch = "bpf"))'.dependencies] -curve25519-dalek = { version = "2.1.0" } - [dev-dependencies] curve25519-dalek = "2.1.0" tiny-bip39 = "0.7.0" -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - [build-dependencies] rustc_version = "0.2" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/Xargo.toml b/sdk/Xargo.toml new file mode 100644 index 00000000000000..475fb71ed15ac2 --- /dev/null +++ b/sdk/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/sdk/macro/src/lib.rs b/sdk/macro/src/lib.rs index dc5ae2724b4292..0bf1502f29ff81 100644 --- a/sdk/macro/src/lib.rs +++ b/sdk/macro/src/lib.rs @@ -17,49 +17,81 @@ use syn::{ Expr, Ident, LitByte, LitStr, Path, Token, }; -struct Id(proc_macro2::TokenStream); -impl Parse for Id { - fn parse(input: ParseStream) -> Result { - let token_stream = if input.peek(syn::LitStr) { - let id_literal: LitStr = input.parse()?; - parse_pubkey(&id_literal)? - } else { - let expr: Expr = input.parse()?; - quote! { #expr } - }; +fn parse_id( + input: ParseStream, + pubkey_type: proc_macro2::TokenStream, +) -> Result { + let id = if input.peek(syn::LitStr) { + let id_literal: LitStr = input.parse()?; + parse_pubkey(&id_literal, &pubkey_type)? + } else { + let expr: Expr = input.parse()?; + quote! { #expr } + }; - if !input.is_empty() { - let stream: proc_macro2::TokenStream = input.parse()?; - return Err(syn::Error::new_spanned(stream, "unexpected token")); + if !input.is_empty() { + let stream: proc_macro2::TokenStream = input.parse()?; + return Err(syn::Error::new_spanned(stream, "unexpected token")); + } + Ok(id) +} + +fn id_to_tokens( + id: &proc_macro2::TokenStream, + pubkey_type: proc_macro2::TokenStream, + tokens: &mut proc_macro2::TokenStream, +) { + tokens.extend(quote! { + /// The static program ID + pub static ID: #pubkey_type = #id; + + /// Confirms that a given pubkey is equivalent to the program ID + pub fn check_id(id: &#pubkey_type) -> bool { + id == &ID + } + + /// Returns the program ID + pub fn id() -> #pubkey_type { + ID + } + + #[cfg(test)] + #[test] + fn test_id() { + assert!(check_id(&id())); } + }); +} - Ok(Id(token_stream)) +struct Id(proc_macro2::TokenStream); + +impl Parse for Id { + fn parse(input: ParseStream) -> Result { + parse_id(input, quote! { ::solana_sdk::pubkey::Pubkey }).map(Self) } } impl ToTokens for Id { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let id = &self.0; - tokens.extend(quote! { - /// The static program ID - pub static ID: ::solana_sdk::pubkey::Pubkey = #id; - - /// Confirms that a given pubkey is equivalent to the program ID - pub fn check_id(id: &::solana_sdk::pubkey::Pubkey) -> bool { - id == &ID - } + id_to_tokens(&self.0, quote! { ::solana_sdk::pubkey::Pubkey }, tokens) + } +} - /// Returns the program ID - pub fn id() -> ::solana_sdk::pubkey::Pubkey { - ID - } +struct ProgramSdkId(proc_macro2::TokenStream); - #[cfg(test)] - #[test] - fn test_id() { - assert!(check_id(&id())); - } - }); +impl Parse for ProgramSdkId { + fn parse(input: ParseStream) -> Result { + parse_id(input, quote! { ::solana_program_sdk::pubkey::Pubkey }).map(Self) + } +} + +impl ToTokens for ProgramSdkId { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + id_to_tokens( + &self.0, + quote! { ::solana_program_sdk::pubkey::Pubkey }, + tokens, + ) } } @@ -135,7 +167,16 @@ pub fn declare_id(input: TokenStream) -> TokenStream { TokenStream::from(quote! {#id}) } -fn parse_pubkey(id_literal: &LitStr) -> Result { +#[proc_macro] +pub fn program_sdk_declare_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as ProgramSdkId); + TokenStream::from(quote! {#id}) +} + +fn parse_pubkey( + id_literal: &LitStr, + pubkey_type: &proc_macro2::TokenStream, +) -> Result { let id_vec = bs58::decode(id_literal.value()) .into_vec() .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?; @@ -147,7 +188,7 @@ fn parse_pubkey(id_literal: &LitStr) -> Result { })?; let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site())); Ok(quote! { - ::solana_sdk::pubkey::Pubkey::new_from_array( + #pubkey_type::new_from_array( [#(#bytes,)*] ) }) @@ -160,11 +201,15 @@ struct Pubkeys { } impl Parse for Pubkeys { fn parse(input: ParseStream) -> Result { + let pubkey_type = quote! { + ::solana_sdk::pubkey::Pubkey + }; + let method = input.parse()?; let _comma: Token![,] = input.parse()?; let (num, pubkeys) = if input.peek(syn::LitStr) { let id_literal: LitStr = input.parse()?; - (1, parse_pubkey(&id_literal)?) + (1, parse_pubkey(&id_literal, &pubkey_type)?) } else if input.peek(Bracket) { let pubkey_strings; bracketed!(pubkey_strings in input); @@ -172,7 +217,7 @@ impl Parse for Pubkeys { Punctuated::parse_terminated(&pubkey_strings)?; let mut pubkeys: Punctuated = Punctuated::new(); for string in punctuated.iter() { - pubkeys.push(parse_pubkey(string)?); + pubkeys.push(parse_pubkey(string, &pubkey_type)?); } (pubkeys.len(), quote! {#pubkeys}) } else { @@ -195,15 +240,19 @@ impl ToTokens for Pubkeys { num, pubkeys, } = self; + + let pubkey_type = quote! { + ::solana_sdk::pubkey::Pubkey + }; if *num == 1 { tokens.extend(quote! { - pub fn #method() -> ::solana_sdk::pubkey::Pubkey { + pub fn #method() -> #pubkey_type { #pubkeys } }); } else { tokens.extend(quote! { - pub fn #method() -> ::std::vec::Vec<::solana_sdk::pubkey::Pubkey> { + pub fn #method() -> ::std::vec::Vec<#pubkey_type> { vec![#pubkeys] } }); diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 2d6d478112bee4..a96f727c818549 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -9,8 +9,37 @@ license = "Apache-2.0" edition = "2018" [dependencies] +bincode = "1.3.1" +bs58 = "0.3.1" +bv = { version = "0.11.1", features = ["serde"] } +hex = "0.4.2" +itertools = "0.9.0" +lazy_static = "1.4.0" +log = "0.4.8" +num-derive = "0.3" +num-traits = "0.2" +rustversion = "1.0.3" serde = "1.0.112" +serde_bytes = "0.11" serde_derive = "1.0.103" +sha2 = "0.8.2" +solana-frozen-abi = { path = "../../frozen-abi", version = "1.5.0" } +solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "1.5.0" } +solana-sdk-macro = { path = "../macro", version = "1.5.0" } +thiserror = "1.0" + +[target.'cfg(not(target_arch = "bpf"))'.dependencies] +curve25519-dalek = { version = "2.1.0" } +rand = "0.7.0" +solana-logger = { path = "../../logger", version = "1.5.0" } + +[dev-dependencies] +assert_matches = "1.3.0" +bincode = "1.3.1" +serde_json = "1.0.56" + +[build-dependencies] +rustc_version = "0.2" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/program/Xargo.toml b/sdk/program/Xargo.toml new file mode 100644 index 00000000000000..475fb71ed15ac2 --- /dev/null +++ b/sdk/program/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/sdk/program/build.rs b/sdk/program/build.rs new file mode 120000 index 00000000000000..84539eddaa6ded --- /dev/null +++ b/sdk/program/build.rs @@ -0,0 +1 @@ +../../frozen-abi/build.rs \ No newline at end of file diff --git a/sdk/src/account.rs b/sdk/program/src/account.rs similarity index 99% rename from sdk/src/account.rs rename to sdk/program/src/account.rs index 0e0d2710c75d73..a5ec2c9deb64c5 100644 --- a/sdk/src/account.rs +++ b/sdk/program/src/account.rs @@ -8,7 +8,7 @@ use std::{ /// An Account with data that is stored on chain #[repr(C)] -#[frozen_abi(digest = "By9FhuLAM947tkLxbTVQru9ZKTrRQuvCR5W387nPSLNu")] +#[frozen_abi(digest = "AVG6bXqVUippoYRAE8e1ewHeZi1UvFx5r5gKspFSEbxM")] #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)] #[serde(rename_all = "camelCase")] pub struct Account { diff --git a/sdk/src/account_info.rs b/sdk/program/src/account_info.rs similarity index 100% rename from sdk/src/account_info.rs rename to sdk/program/src/account_info.rs diff --git a/sdk/src/account_utils.rs b/sdk/program/src/account_utils.rs similarity index 100% rename from sdk/src/account_utils.rs rename to sdk/program/src/account_utils.rs diff --git a/sdk/src/bpf_loader.rs b/sdk/program/src/bpf_loader.rs similarity index 100% rename from sdk/src/bpf_loader.rs rename to sdk/program/src/bpf_loader.rs diff --git a/sdk/src/bpf_loader_deprecated.rs b/sdk/program/src/bpf_loader_deprecated.rs similarity index 100% rename from sdk/src/bpf_loader_deprecated.rs rename to sdk/program/src/bpf_loader_deprecated.rs diff --git a/sdk/src/clock.rs b/sdk/program/src/clock.rs similarity index 100% rename from sdk/src/clock.rs rename to sdk/program/src/clock.rs diff --git a/sdk/src/decode_error.rs b/sdk/program/src/decode_error.rs similarity index 100% rename from sdk/src/decode_error.rs rename to sdk/program/src/decode_error.rs diff --git a/sdk/program/src/entrypoint.rs b/sdk/program/src/entrypoint.rs new file mode 100644 index 00000000000000..5d979db3486035 --- /dev/null +++ b/sdk/program/src/entrypoint.rs @@ -0,0 +1,257 @@ +//! @brief Solana Rust-based BPF program entry point supported by the latest +//! BPFLoader. For more information see './bpf_loader.rs' + +extern crate alloc; +use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +use alloc::vec::Vec; +use std::{ + alloc::Layout, + cell::RefCell, + mem::{align_of, size_of}, + ptr::null_mut, + rc::Rc, + // Hide Result from bindgen gets confused about generics in non-generic type declarations + result::Result as ResultGeneric, + slice::{from_raw_parts, from_raw_parts_mut}, +}; + +pub type ProgramResult = ResultGeneric<(), ProgramError>; + +/// User implemented function to process an instruction +/// +/// program_id: Program ID of the currently executing program accounts: Accounts +/// passed as part of the instruction instruction_data: Instruction data +pub type ProcessInstruction = + fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult; + +/// Programs indicate success with a return value of 0 +pub const SUCCESS: u64 = 0; + +/// Start address of the memory region used for program heap. +pub const HEAP_START_ADDRESS: usize = 0x300000000; +/// Length of the heap memory region used for program heap. +pub const HEAP_LENGTH: usize = 32 * 1024; + +/// Declare the entry point of the program and use the default local heap +/// implementation +/// +/// Deserialize the program input arguments and call the user defined +/// `process_instruction` function. Users must call this macro otherwise an +/// entry point for their program will not be created. +/// +/// If the program defines the feature `custom-heap` then the default heap +/// implementation will not be included and the program is free to implement +/// their own `#[global_allocator]` +#[macro_export] +macro_rules! entrypoint { + ($process_instruction:ident) => { + #[cfg(all(not(feature = "custom-heap"), not(test)))] + #[global_allocator] + static A: $crate::entrypoint::BumpAllocator = $crate::entrypoint::BumpAllocator { + start: $crate::entrypoint::HEAP_START_ADDRESS, + len: $crate::entrypoint::HEAP_LENGTH, + }; + + /// # Safety + #[no_mangle] + pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { + let (program_id, accounts, instruction_data) = + unsafe { $crate::entrypoint::deserialize(input) }; + match $process_instruction(&program_id, &accounts, &instruction_data) { + Ok(()) => $crate::entrypoint::SUCCESS, + Err(error) => error.into(), + } + } + }; +} + +/// The bump allocator used as the default rust heap when running programs. +pub struct BumpAllocator { + pub start: usize, + pub len: usize, +} +unsafe impl std::alloc::GlobalAlloc for BumpAllocator { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let pos_ptr = self.start as *mut usize; + + let mut pos = *pos_ptr; + if pos == 0 { + // First time, set starting position + pos = self.start + self.len; + } + pos = pos.saturating_sub(layout.size()); + pos &= !(layout.align().wrapping_sub(1)); + if pos < self.start + size_of::<*mut u8>() { + return null_mut(); + } + *pos_ptr = pos; + pos as *mut u8 + } + #[inline] + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + // I'm a bump allocator, I don't free + } +} + +/// Maximum number of bytes a program may add to an account during a single realloc +pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10; + +/// Deserialize the input arguments +/// +/// # Safety +#[allow(clippy::type_complexity)] +pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec>, &'a [u8]) { + let mut offset: usize = 0; + + // Number of accounts present + + #[allow(clippy::cast_ptr_alignment)] + let num_accounts = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + // Account Infos + + let mut accounts = Vec::with_capacity(num_accounts); + for _ in 0..num_accounts { + let dup_info = *(input.add(offset) as *const u8); + offset += size_of::(); + if dup_info == std::u8::MAX { + #[allow(clippy::cast_ptr_alignment)] + let is_signer = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let is_writable = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let executable = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + offset += size_of::(); // padding to u64 + + let key: &Pubkey = &*(input.add(offset) as *const Pubkey); + offset += size_of::(); + + let owner: &Pubkey = &*(input.add(offset) as *const Pubkey); + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64))); + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let data_len = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + let data = Rc::new(RefCell::new({ + from_raw_parts_mut(input.add(offset), data_len) + })); + offset += data_len + MAX_PERMITTED_DATA_INCREASE; + offset += (offset as *const u8).align_offset(align_of::()); // padding + + #[allow(clippy::cast_ptr_alignment)] + let rent_epoch = *(input.add(offset) as *const u64); + offset += size_of::(); + + accounts.push(AccountInfo { + is_signer, + is_writable, + key, + lamports, + data, + owner, + executable, + rent_epoch, + }); + } else { + offset += 7; // padding + + // Duplicate account, clone the original + accounts.push(accounts[dup_info as usize].clone()); + } + } + + // Instruction data + + #[allow(clippy::cast_ptr_alignment)] + let instruction_data_len = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) }; + offset += instruction_data_len; + + // Program Id + + let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); + + (program_id, accounts, instruction_data) +} + +#[cfg(test)] +mod test { + use super::*; + use std::alloc::GlobalAlloc; + + #[test] + fn test_bump_allocator() { + // alloc the entire + { + let heap = vec![0u8; 128]; + let allocator = BumpAllocator { + start: heap.as_ptr() as *const _ as usize, + len: heap.len(), + }; + for i in 0..128 - size_of::<*mut u8>() { + let ptr = unsafe { + allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) + }; + assert_eq!( + ptr as *const _ as usize, + heap.as_ptr() as *const _ as usize + heap.len() - 1 - i + ); + } + assert_eq!(null_mut(), unsafe { + allocator.alloc(Layout::from_size_align(1, 1).unwrap()) + }); + } + // check alignment + { + let heap = vec![0u8; 128]; + let allocator = BumpAllocator { + start: heap.as_ptr() as *const _ as usize, + len: heap.len(), + }; + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; + assert_eq!(0, ptr.align_offset(size_of::())); + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; + assert_eq!(0, ptr.align_offset(size_of::())); + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; + assert_eq!(0, ptr.align_offset(size_of::())); + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; + assert_eq!(0, ptr.align_offset(size_of::())); + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; + assert_eq!(0, ptr.align_offset(size_of::())); + let ptr = unsafe { allocator.alloc(Layout::from_size_align(1, 64).unwrap()) }; + assert_eq!(0, ptr.align_offset(64)); + } + // alloc entire block (minus the pos ptr) + { + let heap = vec![0u8; 128]; + let allocator = BumpAllocator { + start: heap.as_ptr() as *const _ as usize, + len: heap.len(), + }; + let ptr = + unsafe { allocator.alloc(Layout::from_size_align(120, size_of::()).unwrap()) }; + assert_ne!(ptr, null_mut()); + assert_eq!(0, ptr.align_offset(size_of::())); + } + } +} diff --git a/sdk/program/src/entrypoint_deprecated.rs b/sdk/program/src/entrypoint_deprecated.rs new file mode 100644 index 00000000000000..278bb75dcb901b --- /dev/null +++ b/sdk/program/src/entrypoint_deprecated.rs @@ -0,0 +1,137 @@ +//! @brief Solana Rust-based BPF program entry point supported by the original +//! and now deprecated BPFLoader. For more information see +//! './bpf_loader_deprecated.rs' + +extern crate alloc; +use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +use alloc::vec::Vec; +use std::{ + cell::RefCell, + mem::size_of, + rc::Rc, + // Hide Result from bindgen gets confused about generics in non-generic type declarations + result::Result as ResultGeneric, + slice::{from_raw_parts, from_raw_parts_mut}, +}; + +pub type ProgramResult = ResultGeneric<(), ProgramError>; + +/// User implemented function to process an instruction +/// +/// program_id: Program ID of the currently executing program +/// accounts: Accounts passed as part of the instruction +/// instruction_data: Instruction data +pub type ProcessInstruction = + fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult; + +/// Programs indicate success with a return value of 0 +pub const SUCCESS: u64 = 0; + +/// Declare the entry point of the program. +/// +/// Deserialize the program input arguments and call +/// the user defined `process_instruction` function. +/// Users must call this macro otherwise an entry point for +/// their program will not be created. +#[macro_export] +macro_rules! entrypoint_deprecated { + ($process_instruction:ident) => { + /// # Safety + #[no_mangle] + pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { + let (program_id, accounts, instruction_data) = + unsafe { $crate::entrypoint_deprecated::deserialize(input) }; + match $process_instruction(&program_id, &accounts, &instruction_data) { + Ok(()) => $crate::entrypoint_deprecated::SUCCESS, + Err(error) => error.into(), + } + } + }; +} + +/// Deserialize the input arguments +/// +/// # Safety +#[allow(clippy::type_complexity)] +pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec>, &'a [u8]) { + let mut offset: usize = 0; + + // Number of accounts present + + #[allow(clippy::cast_ptr_alignment)] + let num_accounts = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + // Account Infos + + let mut accounts = Vec::with_capacity(num_accounts); + for _ in 0..num_accounts { + let dup_info = *(input.add(offset) as *const u8); + offset += size_of::(); + if dup_info == std::u8::MAX { + #[allow(clippy::cast_ptr_alignment)] + let is_signer = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let is_writable = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + let key: &Pubkey = &*(input.add(offset) as *const Pubkey); + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64))); + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let data_len = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + let data = Rc::new(RefCell::new({ + from_raw_parts_mut(input.add(offset), data_len) + })); + offset += data_len; + + let owner: &Pubkey = &*(input.add(offset) as *const Pubkey); + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let executable = *(input.add(offset) as *const u8) != 0; + offset += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let rent_epoch = *(input.add(offset) as *const u64); + offset += size_of::(); + + accounts.push(AccountInfo { + is_signer, + is_writable, + key, + lamports, + data, + owner, + executable, + rent_epoch, + }); + } else { + // Duplicate account, clone the original + accounts.push(accounts[dup_info as usize].clone()); + } + } + + // Instruction data + + #[allow(clippy::cast_ptr_alignment)] + let instruction_data_len = *(input.add(offset) as *const u64) as usize; + offset += size_of::(); + + let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) }; + offset += instruction_data_len; + + // Program Id + + let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); + + (program_id, accounts, instruction_data) +} diff --git a/sdk/src/epoch_schedule.rs b/sdk/program/src/epoch_schedule.rs similarity index 100% rename from sdk/src/epoch_schedule.rs rename to sdk/program/src/epoch_schedule.rs diff --git a/sdk/src/fee_calculator.rs b/sdk/program/src/fee_calculator.rs similarity index 100% rename from sdk/src/fee_calculator.rs rename to sdk/program/src/fee_calculator.rs diff --git a/sdk/program/src/hash.rs b/sdk/program/src/hash.rs new file mode 100644 index 00000000000000..3677fe37459529 --- /dev/null +++ b/sdk/program/src/hash.rs @@ -0,0 +1,186 @@ +//! The `hash` module provides functions for creating SHA-256 hashes. + +use crate::sanitize::Sanitize; +use sha2::{Digest, Sha256}; +use std::{convert::TryFrom, fmt, mem, str::FromStr}; +use thiserror::Error; + +pub const HASH_BYTES: usize = 32; +#[derive( + Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample, +)] +#[repr(transparent)] +pub struct Hash(pub [u8; HASH_BYTES]); + +#[derive(Clone, Default)] +pub struct Hasher { + hasher: Sha256, +} + +impl Hasher { + pub fn hash(&mut self, val: &[u8]) { + self.hasher.input(val); + } + pub fn hashv(&mut self, vals: &[&[u8]]) { + for val in vals { + self.hash(val); + } + } + pub fn result(self) -> Hash { + // At the time of this writing, the sha2 library is stuck on an old version + // of generic_array (0.9.0). Decouple ourselves with a clone to our version. + Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.result().as_slice()).unwrap()) + } +} + +impl Sanitize for Hash {} + +impl AsRef<[u8]> for Hash { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl fmt::Debug for Hash { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", bs58::encode(self.0).into_string()) + } +} + +impl fmt::Display for Hash { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", bs58::encode(self.0).into_string()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum ParseHashError { + #[error("string decoded to wrong size for hash")] + WrongSize, + #[error("failed to decoded string to hash")] + Invalid, +} + +impl FromStr for Hash { + type Err = ParseHashError; + + fn from_str(s: &str) -> Result { + let bytes = bs58::decode(s) + .into_vec() + .map_err(|_| ParseHashError::Invalid)?; + if bytes.len() != mem::size_of::() { + Err(ParseHashError::WrongSize) + } else { + Ok(Hash::new(&bytes)) + } + } +} + +impl Hash { + pub fn new(hash_slice: &[u8]) -> Self { + Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap()) + } + + pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self { + Self(hash_array) + } + + /// unique Hash for tests and benchmarks. + pub fn new_unique() -> Self { + use std::sync::atomic::{AtomicU64, Ordering}; + static I: AtomicU64 = AtomicU64::new(1); + + let mut b = [0u8; HASH_BYTES]; + let i = I.fetch_add(1, Ordering::Relaxed); + b[0..8].copy_from_slice(&i.to_le_bytes()); + Self::new(&b) + } + + pub fn to_bytes(self) -> [u8; HASH_BYTES] { + self.0 + } +} + +/// 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 + // not supported + #[cfg(not(target_arch = "bpf"))] + { + let mut hasher = Hasher::default(); + hasher.hashv(vals); + hasher.result() + } + // Call via a system call to perform the calculation + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64; + }; + let mut hash_result = [0; HASH_BYTES]; + unsafe { + sol_sha256( + vals as *const _ as *const u8, + vals.len() as u64, + &mut hash_result as *mut _ as *mut u8, + ); + } + Hash::new_from_array(hash_result) + } +} + +/// Return a Sha256 hash for the given data. +pub fn hash(val: &[u8]) -> Hash { + hashv(&[val]) +} + +/// 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(); + hash_data.extend_from_slice(val); + hash(&hash_data) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new_unique() { + assert!(Hash::new_unique() != Hash::new_unique()); + } + + #[test] + fn test_hash_fromstr() { + let hash = hash(&[1u8]); + + let mut hash_base58_str = bs58::encode(hash).into_string(); + + assert_eq!(hash_base58_str.parse::(), Ok(hash)); + + hash_base58_str.push_str(&bs58::encode(hash.0).into_string()); + assert_eq!( + hash_base58_str.parse::(), + Err(ParseHashError::WrongSize) + ); + + hash_base58_str.truncate(hash_base58_str.len() / 2); + assert_eq!(hash_base58_str.parse::(), Ok(hash)); + + hash_base58_str.truncate(hash_base58_str.len() / 2); + assert_eq!( + hash_base58_str.parse::(), + Err(ParseHashError::WrongSize) + ); + + let mut hash_base58_str = bs58::encode(hash.0).into_string(); + assert_eq!(hash_base58_str.parse::(), Ok(hash)); + + // throw some non-base58 stuff in there + hash_base58_str.replace_range(..1, "I"); + assert_eq!( + hash_base58_str.parse::(), + Err(ParseHashError::Invalid) + ); + } +} diff --git a/sdk/src/incinerator.rs b/sdk/program/src/incinerator.rs similarity index 100% rename from sdk/src/incinerator.rs rename to sdk/program/src/incinerator.rs diff --git a/sdk/src/instruction.rs b/sdk/program/src/instruction.rs similarity index 100% rename from sdk/src/instruction.rs rename to sdk/program/src/instruction.rs diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 3f0f839fe671e9..69575a2017cb5f 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -1,2 +1,71 @@ +#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))] +#![cfg_attr(RUSTC_NEEDS_PROC_MACRO_HYGIENE, feature(proc_macro_hygiene))] + +// Allows macro expansion of `use ::solana_program_sdk::*` to work within this crate +extern crate self as solana_program_sdk; + +pub mod account; +pub mod account_info; +pub mod account_utils; +pub mod bpf_loader; +pub mod bpf_loader_deprecated; +pub mod clock; +pub mod decode_error; +pub mod entrypoint; +pub mod entrypoint_deprecated; +pub mod epoch_schedule; +pub mod fee_calculator; +pub mod hash; +pub mod incinerator; +pub mod instruction; +pub mod loader_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 pubkey; +pub mod rent; +pub mod sanitize; +pub mod secp256k1_program; +pub mod serialize_utils; +pub mod short_vec; +pub mod slot_hashes; +pub mod slot_history; +pub mod stake_history; +pub mod system_instruction; +pub mod system_program; +pub mod sysvar; + +/// 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_sdk::{declare_id, pubkey::Pubkey}; +/// +/// # mod item_wrapper { +/// # use solana_program_sdk::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_sdk_declare_id as declare_id; + #[macro_use] extern crate serde_derive; + +#[macro_use] +extern crate solana_frozen_abi_macro; diff --git a/sdk/src/loader_instruction.rs b/sdk/program/src/loader_instruction.rs similarity index 100% rename from sdk/src/loader_instruction.rs rename to sdk/program/src/loader_instruction.rs diff --git a/sdk/program/src/log.rs b/sdk/program/src/log.rs new file mode 100644 index 00000000000000..a94db5179aa458 --- /dev/null +++ b/sdk/program/src/log.rs @@ -0,0 +1,98 @@ +//! @brief Solana Rust-based BPF program logging + +use crate::account_info::AccountInfo; + +/// Prints a string +/// There are two forms and are fast +/// 1. Single string +/// 2. 5 integers +#[macro_export] +macro_rules! info { + ($msg:expr) => { + $crate::log::sol_log($msg) + }; + ($arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => { + $crate::log::sol_log_64( + $arg1 as u64, + $arg2 as u64, + $arg3 as u64, + $arg4 as u64, + $arg5 as u64, + ) + }; // `format!()` is not supported yet, Issue #3099 + // `format!()` incurs a very large runtime overhead so it should be used with care + // ($($arg:tt)*) => ($crate::log::sol_log(&format!($($arg)*))); +} + +/// Prints a string to stdout +/// +/// @param message - Message to print +#[inline] +pub fn sol_log(message: &str) { + #[cfg(target_arch = "bpf")] + unsafe { + sol_log_(message.as_ptr(), message.len() as u64); + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_log(message); +} + +#[cfg(target_arch = "bpf")] +extern "C" { + fn sol_log_(message: *const u8, len: u64); +} + +/// Prints 64 bit values represented as hexadecimal to stdout +/// +/// @param argx - integer arguments to print + +#[inline] +pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { + #[cfg(target_arch = "bpf")] + unsafe { + sol_log_64_(arg1, arg2, arg3, arg4, arg5); + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5); +} + +#[cfg(target_arch = "bpf")] +extern "C" { + fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); +} + +/// Prints the hexadecimal representation of a slice +/// +/// @param slice - The array to print +#[allow(dead_code)] +pub fn sol_log_slice(slice: &[u8]) { + for (i, s) in slice.iter().enumerate() { + info!(0, 0, 0, i, *s); + } +} + +/// Prints the hexadecimal representation of the program's input parameters +/// +/// @param ka - A pointer to an array of `AccountInfo` to print +/// @param data - A pointer to the instruction data to print +#[allow(dead_code)] +pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) { + for (i, account) in accounts.iter().enumerate() { + info!("AccountInfo"); + info!(0, 0, 0, 0, i); + info!("- Is signer"); + info!(0, 0, 0, 0, account.is_signer); + info!("- Key"); + account.key.log(); + info!("- Lamports"); + info!(0, 0, 0, 0, account.lamports()); + info!("- Account data length"); + info!(0, 0, 0, 0, account.data_len()); + info!("- Owner"); + account.owner.log(); + } + info!("Instruction data"); + sol_log_slice(data); +} diff --git a/sdk/src/message.rs b/sdk/program/src/message.rs similarity index 93% rename from sdk/src/message.rs rename to sdk/program/src/message.rs index a7df70470819ea..300b5b4e30cc2a 100644 --- a/sdk/src/message.rs +++ b/sdk/program/src/message.rs @@ -426,10 +426,7 @@ impl Message { #[cfg(test)] mod tests { use super::*; - use crate::{ - instruction::AccountMeta, - signature::{Keypair, Signer}, - }; + use crate::instruction::AccountMeta; #[test] fn test_message_unique_program_ids() { @@ -444,7 +441,7 @@ mod tests { #[test] fn test_message_unique_program_ids_not_adjacent() { let program_id0 = Pubkey::default(); - let program_id1 = solana_sdk::pubkey::new_rand(); + let program_id1 = Pubkey::new_unique(); let program_ids = get_program_ids(&[ Instruction::new(program_id0, &0, vec![]), Instruction::new(program_id1, &0, vec![]), @@ -455,7 +452,7 @@ mod tests { #[test] fn test_message_unique_program_ids_order_preserved() { - let program_id0 = solana_sdk::pubkey::new_rand(); + let program_id0 = Pubkey::new_unique(); let program_id1 = Pubkey::default(); // Key less than program_id0 let program_ids = get_program_ids(&[ Instruction::new(program_id0, &0, vec![]), @@ -558,7 +555,7 @@ mod tests { #[test] fn test_message_unique_keys_order_preserved() { let program_id = Pubkey::default(); - let id0 = solana_sdk::pubkey::new_rand(); + let id0 = Pubkey::new_unique(); let id1 = Pubkey::default(); // Key less than id0 let keys = get_keys( &[ @@ -574,7 +571,7 @@ mod tests { fn test_message_unique_keys_not_adjacent() { let program_id = Pubkey::default(); let id0 = Pubkey::default(); - let id1 = solana_sdk::pubkey::new_rand(); + let id1 = Pubkey::new_unique(); let keys = get_keys( &[ Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]), @@ -590,7 +587,7 @@ mod tests { fn test_message_signed_keys_first() { let program_id = Pubkey::default(); let id0 = Pubkey::default(); - let id1 = solana_sdk::pubkey::new_rand(); + let id1 = Pubkey::new_unique(); let keys = get_keys( &[ Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]), @@ -619,9 +616,9 @@ mod tests { fn test_message_readonly_keys_last() { let program_id = Pubkey::default(); let id0 = Pubkey::default(); // Identical key/program_id should be de-duped - let id1 = solana_sdk::pubkey::new_rand(); - let id2 = solana_sdk::pubkey::new_rand(); - let id3 = solana_sdk::pubkey::new_rand(); + let id1 = Pubkey::new_unique(); + let id2 = Pubkey::new_unique(); + let id3 = Pubkey::new_unique(); let keys = get_keys( &[ Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]), @@ -639,11 +636,10 @@ mod tests { #[test] fn test_message_kitchen_sink() { - let program_id0 = solana_sdk::pubkey::new_rand(); - let program_id1 = solana_sdk::pubkey::new_rand(); + let program_id0 = Pubkey::new_unique(); + let program_id1 = Pubkey::new_unique(); let id0 = Pubkey::default(); - let keypair1 = Keypair::new(); - let id1 = keypair1.pubkey(); + let id1 = Pubkey::new_unique(); let message = Message::new( &[ Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]), @@ -669,7 +665,7 @@ mod tests { #[test] fn test_message_payer_first() { let program_id = Pubkey::default(); - let payer = solana_sdk::pubkey::new_rand(); + let payer = Pubkey::new_unique(); let id0 = Pubkey::default(); let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]); @@ -692,8 +688,8 @@ mod tests { #[test] fn test_message_program_last() { let program_id = Pubkey::default(); - let id0 = solana_sdk::pubkey::new_rand(); - let id1 = solana_sdk::pubkey::new_rand(); + let id0 = Pubkey::new_unique(); + let id1 = Pubkey::new_unique(); let keys = get_keys( &[ Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]), @@ -710,8 +706,8 @@ mod tests { #[test] fn test_program_position() { let program_id0 = Pubkey::default(); - let program_id1 = solana_sdk::pubkey::new_rand(); - let id = solana_sdk::pubkey::new_rand(); + let program_id1 = Pubkey::new_unique(); + let id = Pubkey::new_unique(); let message = Message::new( &[ Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]), @@ -726,12 +722,12 @@ mod tests { #[test] fn test_is_writable() { - let key0 = solana_sdk::pubkey::new_rand(); - let key1 = solana_sdk::pubkey::new_rand(); - let key2 = solana_sdk::pubkey::new_rand(); - let key3 = solana_sdk::pubkey::new_rand(); - let key4 = solana_sdk::pubkey::new_rand(); - let key5 = solana_sdk::pubkey::new_rand(); + let key0 = Pubkey::new_unique(); + let key1 = Pubkey::new_unique(); + let key2 = Pubkey::new_unique(); + let key3 = Pubkey::new_unique(); + let key4 = Pubkey::new_unique(); + let key5 = Pubkey::new_unique(); let message = Message { header: MessageHeader { @@ -754,10 +750,10 @@ mod tests { #[test] fn test_get_account_keys_by_lock_type() { let program_id = Pubkey::default(); - let id0 = solana_sdk::pubkey::new_rand(); - let id1 = solana_sdk::pubkey::new_rand(); - let id2 = solana_sdk::pubkey::new_rand(); - let id3 = solana_sdk::pubkey::new_rand(); + let id0 = Pubkey::new_unique(); + let id1 = Pubkey::new_unique(); + let id2 = Pubkey::new_unique(); + let id3 = Pubkey::new_unique(); let message = Message::new( &[ Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]), @@ -776,12 +772,12 @@ mod tests { #[test] fn test_decompile_instructions() { solana_logger::setup(); - let program_id0 = solana_sdk::pubkey::new_rand(); - let program_id1 = solana_sdk::pubkey::new_rand(); - let id0 = solana_sdk::pubkey::new_rand(); - let id1 = solana_sdk::pubkey::new_rand(); - let id2 = solana_sdk::pubkey::new_rand(); - let id3 = solana_sdk::pubkey::new_rand(); + let program_id0 = Pubkey::new_unique(); + let program_id1 = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let id1 = Pubkey::new_unique(); + let id2 = Pubkey::new_unique(); + let id3 = Pubkey::new_unique(); let instructions = vec![ Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]), Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, true)]), diff --git a/sdk/src/native_token.rs b/sdk/program/src/native_token.rs similarity index 100% rename from sdk/src/native_token.rs rename to sdk/program/src/native_token.rs diff --git a/sdk/src/nonce/account.rs b/sdk/program/src/nonce/account.rs similarity index 99% rename from sdk/src/nonce/account.rs rename to sdk/program/src/nonce/account.rs index 7323603cc126a2..6c73a110ebcb1b 100644 --- a/sdk/src/nonce/account.rs +++ b/sdk/program/src/nonce/account.rs @@ -170,12 +170,11 @@ pub fn create_account(lamports: u64) -> RefCell { } /// Convenience function for working with keyed accounts in tests -#[cfg(feature = "everything")] pub fn with_test_keyed_account(lamports: u64, signer: bool, f: F) where F: Fn(&KeyedAccount), { - let pubkey = solana_sdk::pubkey::new_rand(); + let pubkey = Pubkey::new_unique(); let account = create_account(lamports); let keyed_account = KeyedAccount::new(&pubkey, signer, &account); f(&keyed_account) diff --git a/sdk/src/nonce/mod.rs b/sdk/program/src/nonce/mod.rs similarity index 100% rename from sdk/src/nonce/mod.rs rename to sdk/program/src/nonce/mod.rs diff --git a/sdk/src/nonce/state/current.rs b/sdk/program/src/nonce/state/current.rs similarity index 100% rename from sdk/src/nonce/state/current.rs rename to sdk/program/src/nonce/state/current.rs diff --git a/sdk/src/nonce/state/mod.rs b/sdk/program/src/nonce/state/mod.rs similarity index 100% rename from sdk/src/nonce/state/mod.rs rename to sdk/program/src/nonce/state/mod.rs diff --git a/sdk/src/nonce/utils.rs b/sdk/program/src/nonce/utils.rs similarity index 98% rename from sdk/src/nonce/utils.rs rename to sdk/program/src/nonce/utils.rs index 9c6ccfc2d3095e..e97ef1692987aa 100644 --- a/sdk/src/nonce/utils.rs +++ b/sdk/program/src/nonce/utils.rs @@ -1,4 +1,4 @@ -use solana_sdk::{ +use crate::{ account::Account, account_utils::StateMut, fee_calculator::FeeCalculator, @@ -26,7 +26,7 @@ pub fn fee_calculator_of(account: &Account) -> Option { #[cfg(test)] mod tests { use super::*; - use solana_sdk::{ + use crate::{ account_utils::State as AccountUtilsState, hash::Hash, nonce::{account::with_test_keyed_account, Account as NonceAccount, State}, diff --git a/sdk/src/program.rs b/sdk/program/src/program.rs similarity index 98% rename from sdk/src/program.rs rename to sdk/program/src/program.rs index c6b4cf34976747..af6b5467fdf739 100644 --- a/sdk/src/program.rs +++ b/sdk/program/src/program.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "program")] - use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction}; /// Invoke a cross-program instruction diff --git a/sdk/src/program_error.rs b/sdk/program/src/program_error.rs similarity index 98% rename from sdk/src/program_error.rs rename to sdk/program/src/program_error.rs index 708257318cbedc..d94a238ffe70bc 100644 --- a/sdk/src/program_error.rs +++ b/sdk/program/src/program_error.rs @@ -1,13 +1,9 @@ +use crate::info; use crate::{decode_error::DecodeError, instruction::InstructionError, pubkey::PubkeyError}; use num_traits::{FromPrimitive, ToPrimitive}; use std::convert::TryFrom; use thiserror::Error; -#[cfg(feature = "program")] -use crate::info; -#[cfg(all(feature = "everything", not(feature = "program")))] -use log::info; - /// Reasons the program may fail #[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)] pub enum ProgramError { diff --git a/sdk/src/program_option.rs b/sdk/program/src/program_option.rs similarity index 100% rename from sdk/src/program_option.rs rename to sdk/program/src/program_option.rs diff --git a/sdk/src/program_pack.rs b/sdk/program/src/program_pack.rs similarity index 100% rename from sdk/src/program_pack.rs rename to sdk/program/src/program_pack.rs diff --git a/sdk/src/program_stubs.rs b/sdk/program/src/program_stubs.rs similarity index 89% rename from sdk/src/program_stubs.rs rename to sdk/program/src/program_stubs.rs index 809c70d8de44bc..2a858582cdda98 100644 --- a/sdk/src/program_stubs.rs +++ b/sdk/program/src/program_stubs.rs @@ -1,9 +1,8 @@ //! @brief Syscall stubs when building for programs for non-BPF targets -use crate::{ - account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, - program_error::ProgramError, -}; +#![cfg(not(target_arch = "bpf"))] + +use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction}; use std::sync::{Arc, RwLock}; lazy_static::lazy_static! { @@ -27,7 +26,7 @@ pub trait SyscallStubs: Sync + Send { _signers_seeds: &[&[&[u8]]], ) -> ProgramResult { sol_log("SyscallStubs: sol_invoke_signed() not available"); - Err(ProgramError::InvalidArgument) + Ok(()) } } diff --git a/sdk/program/src/pubkey.rs b/sdk/program/src/pubkey.rs new file mode 100644 index 00000000000000..af7c73903678d0 --- /dev/null +++ b/sdk/program/src/pubkey.rs @@ -0,0 +1,426 @@ +use crate::{decode_error::DecodeError, hash::hashv}; +use num_derive::{FromPrimitive, ToPrimitive}; +use std::{convert::TryFrom, fmt, mem, str::FromStr}; +use thiserror::Error; + +/// maximum length of derived pubkey seed +pub const MAX_SEED_LEN: usize = 32; + +#[derive(Error, Debug, Serialize, Clone, PartialEq, 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, +} +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"), + } + } +} + +#[repr(transparent)] +#[derive( + Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample, +)] +pub struct Pubkey([u8; 32]); + +impl crate::sanitize::Sanitize for Pubkey {} + +#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)] +pub enum ParsePubkeyError { + #[error("String is the wrong size")] + WrongSize, + #[error("Invalid Base58 string")] + Invalid, +} +impl DecodeError for ParsePubkeyError { + fn type_of() -> &'static str { + "ParsePubkeyError" + } +} + +impl FromStr for Pubkey { + type Err = ParsePubkeyError; + + fn from_str(s: &str) -> Result { + let pubkey_vec = bs58::decode(s) + .into_vec() + .map_err(|_| ParsePubkeyError::Invalid)?; + if pubkey_vec.len() != mem::size_of::() { + Err(ParsePubkeyError::WrongSize) + } else { + Ok(Pubkey::new(&pubkey_vec)) + } + } +} + +impl Pubkey { + pub fn new(pubkey_vec: &[u8]) -> Self { + Self( + <[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec)) + .expect("Slice must be the same length as a Pubkey"), + ) + } + + pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self { + Self(pubkey_array) + } + + #[deprecated(since = "1.3.9", note = "Please use 'Pubkey::new_unique' instead")] + #[cfg(not(target_arch = "bpf"))] + pub fn new_rand() -> Self { + // Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe + Pubkey::new(&rand::random::<[u8; 32]>()) + } + + /// unique Pubkey for tests and benchmarks. + pub fn new_unique() -> Self { + use std::sync::atomic::{AtomicU64, Ordering}; + static I: AtomicU64 = AtomicU64::new(1); + + let mut b = [0u8; 32]; + let i = I.fetch_add(1, Ordering::Relaxed); + b[0..8].copy_from_slice(&i.to_le_bytes()); + Self::new(&b) + } + + pub fn create_with_seed( + base: &Pubkey, + seed: &str, + owner: &Pubkey, + ) -> Result { + if seed.len() > MAX_SEED_LEN { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + + Ok(Pubkey::new( + hashv(&[base.as_ref(), seed.as_ref(), owner.as_ref()]).as_ref(), + )) + } + + /// Create a program address + /// + /// Program addresses are account keys that only the program 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. + /// The runtime will check that indeed the program associated with this + /// address is the caller and thus authorized to be the signer. + /// + /// Because the program address cannot lie on the ed25519 curve there may be + /// seed and program id combinations that are invalid. In these cases an + /// extra seed (bump seed) can be calculated that results in a point off the + /// curve. Use `find_program_address` to calculate that bump seed. + /// + /// 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 change 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 themselves. + pub fn create_program_address( + seeds: &[&[u8]], + program_id: &Pubkey, + ) -> Result { + // Perform the calculation inline, calling this from within a program is + // not supported + #[cfg(not(target_arch = "bpf"))] + { + let mut hasher = crate::hash::Hasher::default(); + for seed in seeds.iter() { + if seed.len() > MAX_SEED_LEN { + return Err(PubkeyError::MaxSeedLengthExceeded); + } + hasher.hash(seed); + } + hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]); + let hash = hasher.result(); + + if curve25519_dalek::edwards::CompressedEdwardsY::from_slice(hash.as_ref()) + .decompress() + .is_some() + { + return Err(PubkeyError::InvalidSeeds); + } + + Ok(Pubkey::new(hash.as_ref())) + } + // Call via a system call to perform the calculation + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_create_program_address( + seeds_addr: *const u8, + seeds_len: u64, + program_id_addr: *const u8, + address_bytes_addr: *const u8, + ) -> u64; + }; + let mut bytes = [0; 32]; + let result = unsafe { + 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::new(&bytes)), + _ => Err(result.into()), + } + } + } + + /// Find a valid program address and its corresponding bump seed which must be passed + /// as an additional seed when calling `invoke_signed` + #[allow(clippy::same_item_push)] + pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) { + let mut bump_seed = [std::u8::MAX]; + for _ in 0..std::u8::MAX { + { + let mut seeds_with_bump = seeds.to_vec(); + seeds_with_bump.push(&bump_seed); + if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) { + return (address, bump_seed[0]); + } + } + bump_seed[0] -= 1; + } + panic!("Unable to find a viable program address bump seed"); + } + + pub fn to_bytes(self) -> [u8; 32] { + self.0 + } + + /// Log a `Pubkey` from a program + pub fn log(&self) { + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_log_pubkey(pubkey_addr: *const u8); + }; + unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_log(&self.to_string()); + } +} + +impl AsRef<[u8]> for Pubkey { + fn as_ref(&self) -> &[u8] { + &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(test)] +mod tests { + use super::*; + use 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) + ); + } + + #[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 program_id = Pubkey::from_str("BPFLoader1111111111111111111111111111111111").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(&[b"", &[1]], &program_id), + Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&["☉".as_ref()], &program_id), + Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id), + Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds" + .parse() + .unwrap()) + ); + assert_eq!( + Pubkey::create_program_address(&[public_key.as_ref()], &program_id), + Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K" + .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() + ); + } + } +} diff --git a/sdk/src/rent.rs b/sdk/program/src/rent.rs similarity index 97% rename from sdk/src/rent.rs rename to sdk/program/src/rent.rs index be05654179f9f7..2a0f698beff2e1 100644 --- a/sdk/src/rent.rs +++ b/sdk/program/src/rent.rs @@ -133,8 +133,9 @@ mod tests { #[test] #[should_panic] fn show_rent_model() { - use crate::{clock::*, sysvar::Sysvar, timing::*}; + use crate::{clock::*, sysvar::Sysvar}; + const SECONDS_PER_YEAR: f64 = 365.242_199 * 24.0 * 60.0 * 60.0; const SLOTS_PER_YEAR: f64 = SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64); diff --git a/sdk/src/sanitize.rs b/sdk/program/src/sanitize.rs similarity index 100% rename from sdk/src/sanitize.rs rename to sdk/program/src/sanitize.rs diff --git a/sdk/program/src/secp256k1_program.rs b/sdk/program/src/secp256k1_program.rs new file mode 100644 index 00000000000000..32860e81c6f37d --- /dev/null +++ b/sdk/program/src/secp256k1_program.rs @@ -0,0 +1 @@ +crate::declare_id!("KeccakSecp256k11111111111111111111111111111"); diff --git a/sdk/src/serialize_utils.rs b/sdk/program/src/serialize_utils.rs similarity index 100% rename from sdk/src/serialize_utils.rs rename to sdk/program/src/serialize_utils.rs diff --git a/sdk/src/short_vec.rs b/sdk/program/src/short_vec.rs similarity index 100% rename from sdk/src/short_vec.rs rename to sdk/program/src/short_vec.rs diff --git a/sdk/src/slot_hashes.rs b/sdk/program/src/slot_hashes.rs similarity index 100% rename from sdk/src/slot_hashes.rs rename to sdk/program/src/slot_hashes.rs diff --git a/sdk/src/slot_history.rs b/sdk/program/src/slot_history.rs similarity index 100% rename from sdk/src/slot_history.rs rename to sdk/program/src/slot_history.rs diff --git a/sdk/src/stake_history.rs b/sdk/program/src/stake_history.rs similarity index 100% rename from sdk/src/stake_history.rs rename to sdk/program/src/stake_history.rs diff --git a/sdk/src/system_instruction.rs b/sdk/program/src/system_instruction.rs similarity index 98% rename from sdk/src/system_instruction.rs rename to sdk/program/src/system_instruction.rs index ee843a41a8eaa4..4fdad5082dc5ab 100644 --- a/sdk/src/system_instruction.rs +++ b/sdk/program/src/system_instruction.rs @@ -491,9 +491,9 @@ mod tests { #[test] fn test_move_many() { - let alice_pubkey = solana_sdk::pubkey::new_rand(); - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let carol_pubkey = solana_sdk::pubkey::new_rand(); + let alice_pubkey = Pubkey::new_unique(); + let bob_pubkey = Pubkey::new_unique(); + let carol_pubkey = Pubkey::new_unique(); let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)]; let instructions = transfer_many(&alice_pubkey, &to_lamports); @@ -504,8 +504,8 @@ mod tests { #[test] fn test_create_nonce_account() { - let from_pubkey = solana_sdk::pubkey::new_rand(); - let nonce_pubkey = solana_sdk::pubkey::new_rand(); + let from_pubkey = Pubkey::new_unique(); + let nonce_pubkey = Pubkey::new_unique(); let authorized = nonce_pubkey; let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42); assert_eq!(ixs.len(), 2); diff --git a/sdk/src/system_program.rs b/sdk/program/src/system_program.rs similarity index 100% rename from sdk/src/system_program.rs rename to sdk/program/src/system_program.rs diff --git a/sdk/src/sysvar/clock.rs b/sdk/program/src/sysvar/clock.rs similarity index 100% rename from sdk/src/sysvar/clock.rs rename to sdk/program/src/sysvar/clock.rs diff --git a/sdk/src/sysvar/epoch_schedule.rs b/sdk/program/src/sysvar/epoch_schedule.rs similarity index 100% rename from sdk/src/sysvar/epoch_schedule.rs rename to sdk/program/src/sysvar/epoch_schedule.rs diff --git a/sdk/src/sysvar/fees.rs b/sdk/program/src/sysvar/fees.rs similarity index 100% rename from sdk/src/sysvar/fees.rs rename to sdk/program/src/sysvar/fees.rs diff --git a/sdk/src/sysvar/instructions.rs b/sdk/program/src/sysvar/instructions.rs similarity index 85% rename from sdk/src/sysvar/instructions.rs rename to sdk/program/src/sysvar/instructions.rs index e4efc75cf39a8e..7aad1843bdfb08 100644 --- a/sdk/src/sysvar/instructions.rs +++ b/sdk/program/src/sysvar/instructions.rs @@ -1,9 +1,6 @@ //! This account contains the serialized transaction instructions -//! -use crate::instruction::Instruction; -use crate::sanitize::SanitizeError; -use crate::sysvar::Sysvar; +use crate::{instruction::Instruction, sanitize::SanitizeError, sysvar::Sysvar}; pub type Instructions = Vec; @@ -24,7 +21,7 @@ pub fn store_current_index(data: &mut [u8], instruction_index: u16) { } pub fn load_instruction_at(index: usize, data: &[u8]) -> Result { - solana_sdk::message::Message::deserialize_instruction(index, data) + crate::message::Message::deserialize_instruction(index, data) } #[cfg(test)] diff --git a/sdk/src/sysvar/mod.rs b/sdk/program/src/sysvar/mod.rs similarity index 100% rename from sdk/src/sysvar/mod.rs rename to sdk/program/src/sysvar/mod.rs diff --git a/sdk/src/sysvar/recent_blockhashes.rs b/sdk/program/src/sysvar/recent_blockhashes.rs similarity index 100% rename from sdk/src/sysvar/recent_blockhashes.rs rename to sdk/program/src/sysvar/recent_blockhashes.rs diff --git a/sdk/src/sysvar/rent.rs b/sdk/program/src/sysvar/rent.rs similarity index 100% rename from sdk/src/sysvar/rent.rs rename to sdk/program/src/sysvar/rent.rs diff --git a/sdk/src/sysvar/rewards.rs b/sdk/program/src/sysvar/rewards.rs similarity index 100% rename from sdk/src/sysvar/rewards.rs rename to sdk/program/src/sysvar/rewards.rs diff --git a/sdk/src/sysvar/slot_hashes.rs b/sdk/program/src/sysvar/slot_hashes.rs similarity index 100% rename from sdk/src/sysvar/slot_hashes.rs rename to sdk/program/src/sysvar/slot_hashes.rs diff --git a/sdk/src/sysvar/slot_history.rs b/sdk/program/src/sysvar/slot_history.rs similarity index 100% rename from sdk/src/sysvar/slot_history.rs rename to sdk/program/src/sysvar/slot_history.rs diff --git a/sdk/src/sysvar/stake_history.rs b/sdk/program/src/sysvar/stake_history.rs similarity index 100% rename from sdk/src/sysvar/stake_history.rs rename to sdk/program/src/sysvar/stake_history.rs diff --git a/sdk/src/.lib.rs.swo b/sdk/src/.lib.rs.swo new file mode 100644 index 0000000000000000000000000000000000000000..deb979918242c3626c85ef7ca95d63000321c584 GIT binary patch literal 16384 zcmeI2TWlOx8Gwg`^g;+JEh<8l(D9lk>)_aNZi?1~COA%GS|?GiZD}-~ojJQZ^6bnq zXLjvPaG(OAN~j4GQ7Rs)mI@>;ssy5jR#btKKJZW>l9p0cqDX}Y5&;q=FD+8}&dlt^ zmn4q*z(upt=hd9~&+R|||DSXIb#cRyv2n3&V57n3Cd1fVJY~N8_SqFPpD>JS!}X=o zFRuJifww-ce`PV_WYXZ^lp0h+gUXp4RG~fSxy6BS3B6pU@$7Y`flLFJ*FZfgt>3oF z*tBtIgBI$|^@v+;K74uk>`kVDOaqw)G7V%J$TW~?Ak#pmflLGccN&Ol%Z$%6t7YlT zZcM-5Gw*wO`aF|~Z^(?F(yOaqw)G7V%J$TW~?Ak#pmflLFL2L1~* zU^#{{`2oZD0qEcVTlfDns|}+L5v+ylU5Dv!1p-phC#Ru{3-~^q zh6pBL4{U%Otb(^lB>WkE4L^e?;4APs@ZcWU0qfuvSPqv~G7k7H%)*m!3Z}t=JD?X< z!rSjNjEisqz7D5h7wmwIFaT@dB_{q=sKW@{0c&6xJb!~>JPVJ(w_peK!fkNrdc!ye zKY@qgYv92V_!RWQt*`?AcAa7T6@Cnl!Z+c57=>N%(h9>k3$yS9+y_UY0R6BI`rsy5 z4S#yCVY~oO!y|AKOt=gB;Un-CiJLdzEc^%_h6kVsyJ0Qd3~!PE`Xjs!=isOCID84d z2o1O!?u8?;*hfxBGW3NVT9Fj|Q;6kTCjaGORxLXW%u*N}m%eEg-C?8mGf7!p$zz5w zr=Ay-g{1^vWf=||wR%y^d0$jpwhhm%qGaeu(~+Uukn`Kt^{as+qR{eHEeInmV3*4I zlC+|FD8+V>lMNX*ql)X7bA89+9@{l6J8}bm$#qmyMY3wfon32Hx9rPMExjw`;~6Zy zMkSPv$z0VnO-b6dja#-(4lTK#iWNF$DF`Q*KAE!QAB|jP+JRqk%S-Ob_FU;-eMJWc z#dzS4+EngJz z8>=L}rU+#gWs_ppy^Ibx#Y zg@wYLLG#NsMf5=ExMi?HN})0RYYTGVAH^K zYtCIto6|#U6)n%}cD7U|*RPe6kF2mPBhw1)iuS}}t(5EQLwR!@+_R21(H+CE-(9P% z)ol$_hGCEf8SV0EaT0ZO0a2*)fT@MeTHyK-v8JW{#a?&HEZYw1$h&?i7}o2uZ=S^o zRg<<`a%~-?v?(PP$l@&spoKyxRn3ZnQCL?|>d1vcY#n(_W9z_%G=42!pV?2!Cbq2A zu(*gLZO;m&={kD2gJNQ0BL21%4iTC1vpsp&$@le(Y7?2hL{{EaUX>O(Z0%( zh56igb7-lbT;D+5p9-y7K504bx;GxGtK+JERg+<%&>CK^n2BXN!|u7p4wD)5;;VBN zQYduVY{_Vg=pF}tj*j91%&&Z+Q@dXzdM^|b-4HsoX5$)>!}3K;XgBIQ7-5R~Rj=R= zn`lLbBB~G;h)_HT?E$ii8H!qtOG5-ntC zc?1$Aw`^nRA>~9h{6;Z@w966|Qm0!>*G{{+k-%?ApXg33N=6z5BGswxB#s4Iu^z62 zQO6OqRQKcUS_y=4QwCs1Nyu`ePH2zZDr4z5DVuh~>WTvCCcYpTq~)&9Wa{a;fDy#U z3kEbP%_-@YD}-c|E+j?OT30WzM8#E65H`Dty&y6#Q);P-Oubc`o~jVt*}sW_?QUC} zv$=4zmXC=X=RnjF4r{iVoPgRT@$T7r;u}qN>3*(=Q&Tf{=)pq3n%+R{O# zy{os&s43aDThD&LGIu><91Uqk!K5VEwS|&WFR(c1b&AomvdUex69FRbBCB+n!jc!8 zW2aHJTQ)>IuzPc;upAwcl9Z-xd3K$;-BPj^*cDUR6{*8{M?NV}_JP)vx;7M!a`EA{ z^~%9Z<{dyfuk3*1?!2vwZMy14Iyfv)x9tVpD^;w!UW6t(V`JAfQ?)I&v2|W|sW!y+F zx32eY>X!3Y8r9s>hLRp}Np)?&IxLHwI=reh8gbO(J`sn_l91u?CN{V^HW-qF!r2hIHl_Kle1J4X)eGxywk=h*1p(SDKM zQ}TGDo!+D>%`%Zu>T}YQ!87m;I1J-3 z1nc2E_x)LT3LXRrcf#%PF?feN|6kx`n1%1bqwoNH0Y>5Dunzvto&Wdn3Y6g}jKOBm zd4LP>BAkKe;OFopd>f|V9@qnY@Im+ocm6-X8TcVQ1P#zRf?aSsd; - -/// User implemented function to process an instruction -/// -/// program_id: Program ID of the currently executing program accounts: Accounts -/// passed as part of the instruction instruction_data: Instruction data -pub type ProcessInstruction = - fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult; - -/// Programs indicate success with a return value of 0 -pub const SUCCESS: u64 = 0; - -/// Start address of the memory region used for program heap. -pub const HEAP_START_ADDRESS: usize = 0x300000000; -/// Length of the heap memory region used for program heap. -pub const HEAP_LENGTH: usize = 32 * 1024; - -/// Declare the entry point of the program and use the default local heap -/// implementation -/// -/// Deserialize the program input arguments and call the user defined -/// `process_instruction` function. Users must call this macro otherwise an -/// entry point for their program will not be created. -/// -/// If the program defines the feature `custom-heap` then the default heap -/// implementation will not be included and the program is free to implement -/// their own `#[global_allocator]` #[macro_export] +#[deprecated( + since = "1.4.3", + note = "use solana_program_sdk::entrypoint::entrypoint instead" +)] macro_rules! entrypoint { ($process_instruction:ident) => { #[cfg(all(not(feature = "custom-heap"), not(test)))] @@ -64,194 +26,3 @@ macro_rules! entrypoint { } }; } - -/// The bump allocator used as the default rust heap when running programs. -pub struct BumpAllocator { - pub start: usize, - pub len: usize, -} -unsafe impl std::alloc::GlobalAlloc for BumpAllocator { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let pos_ptr = self.start as *mut usize; - - let mut pos = *pos_ptr; - if pos == 0 { - // First time, set starting position - pos = self.start + self.len; - } - pos = pos.saturating_sub(layout.size()); - pos &= !(layout.align().wrapping_sub(1)); - if pos < self.start + size_of::<*mut u8>() { - return null_mut(); - } - *pos_ptr = pos; - pos as *mut u8 - } - #[inline] - unsafe fn dealloc(&self, _: *mut u8, _: Layout) { - // I'm a bump allocator, I don't free - } -} - -/// Maximum number of bytes a program may add to an account during a single realloc -pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10; - -/// Deserialize the input arguments -/// -/// # Safety -#[allow(clippy::type_complexity)] -pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec>, &'a [u8]) { - let mut offset: usize = 0; - - // Number of accounts present - - #[allow(clippy::cast_ptr_alignment)] - let num_accounts = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - // Account Infos - - let mut accounts = Vec::with_capacity(num_accounts); - for _ in 0..num_accounts { - let dup_info = *(input.add(offset) as *const u8); - offset += size_of::(); - if dup_info == std::u8::MAX { - #[allow(clippy::cast_ptr_alignment)] - let is_signer = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let is_writable = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let executable = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - offset += size_of::(); // padding to u64 - - let key: &Pubkey = &*(input.add(offset) as *const Pubkey); - offset += size_of::(); - - let owner: &Pubkey = &*(input.add(offset) as *const Pubkey); - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64))); - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let data_len = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - let data = Rc::new(RefCell::new({ - from_raw_parts_mut(input.add(offset), data_len) - })); - offset += data_len + MAX_PERMITTED_DATA_INCREASE; - offset += (offset as *const u8).align_offset(align_of::()); // padding - - #[allow(clippy::cast_ptr_alignment)] - let rent_epoch = *(input.add(offset) as *const u64); - offset += size_of::(); - - accounts.push(AccountInfo { - is_signer, - is_writable, - key, - lamports, - data, - owner, - executable, - rent_epoch, - }); - } else { - offset += 7; // padding - - // Duplicate account, clone the original - accounts.push(accounts[dup_info as usize].clone()); - } - } - - // Instruction data - - #[allow(clippy::cast_ptr_alignment)] - let instruction_data_len = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) }; - offset += instruction_data_len; - - // Program Id - - let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); - - (program_id, accounts, instruction_data) -} - -#[cfg(test)] -mod test { - use super::*; - use std::alloc::GlobalAlloc; - - #[test] - fn test_bump_allocator() { - // alloc the entire - { - let heap = vec![0u8; 128]; - let allocator = BumpAllocator { - start: heap.as_ptr() as *const _ as usize, - len: heap.len(), - }; - for i in 0..128 - size_of::<*mut u8>() { - let ptr = unsafe { - allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) - }; - assert_eq!( - ptr as *const _ as usize, - heap.as_ptr() as *const _ as usize + heap.len() - 1 - i - ); - } - assert_eq!(null_mut(), unsafe { - allocator.alloc(Layout::from_size_align(1, 1).unwrap()) - }); - } - // check alignment - { - let heap = vec![0u8; 128]; - let allocator = BumpAllocator { - start: heap.as_ptr() as *const _ as usize, - len: heap.len(), - }; - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; - assert_eq!(0, ptr.align_offset(size_of::())); - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; - assert_eq!(0, ptr.align_offset(size_of::())); - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; - assert_eq!(0, ptr.align_offset(size_of::())); - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; - assert_eq!(0, ptr.align_offset(size_of::())); - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(1, size_of::()).unwrap()) }; - assert_eq!(0, ptr.align_offset(size_of::())); - let ptr = unsafe { allocator.alloc(Layout::from_size_align(1, 64).unwrap()) }; - assert_eq!(0, ptr.align_offset(64)); - } - // alloc entire block (minus the pos ptr) - { - let heap = vec![0u8; 128]; - let allocator = BumpAllocator { - start: heap.as_ptr() as *const _ as usize, - len: heap.len(), - }; - let ptr = - unsafe { allocator.alloc(Layout::from_size_align(120, size_of::()).unwrap()) }; - assert_ne!(ptr, null_mut()); - assert_eq!(0, ptr.align_offset(size_of::())); - } - } -} diff --git a/sdk/src/entrypoint_deprecated.rs b/sdk/src/entrypoint_deprecated.rs index 278bb75dcb901b..25e38251f6a645 100644 --- a/sdk/src/entrypoint_deprecated.rs +++ b/sdk/src/entrypoint_deprecated.rs @@ -1,39 +1,10 @@ -//! @brief Solana Rust-based BPF program entry point supported by the original -//! and now deprecated BPFLoader. For more information see -//! './bpf_loader_deprecated.rs' +pub use solana_program_sdk::entrypoint_deprecated::*; -extern crate alloc; -use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; -use alloc::vec::Vec; -use std::{ - cell::RefCell, - mem::size_of, - rc::Rc, - // Hide Result from bindgen gets confused about generics in non-generic type declarations - result::Result as ResultGeneric, - slice::{from_raw_parts, from_raw_parts_mut}, -}; - -pub type ProgramResult = ResultGeneric<(), ProgramError>; - -/// User implemented function to process an instruction -/// -/// program_id: Program ID of the currently executing program -/// accounts: Accounts passed as part of the instruction -/// instruction_data: Instruction data -pub type ProcessInstruction = - fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult; - -/// Programs indicate success with a return value of 0 -pub const SUCCESS: u64 = 0; - -/// Declare the entry point of the program. -/// -/// Deserialize the program input arguments and call -/// the user defined `process_instruction` function. -/// Users must call this macro otherwise an entry point for -/// their program will not be created. #[macro_export] +#[deprecated( + since = "1.4.3", + note = "use solana_program_sdk::entrypoint::entrypoint instead" +)] macro_rules! entrypoint_deprecated { ($process_instruction:ident) => { /// # Safety @@ -48,90 +19,3 @@ macro_rules! entrypoint_deprecated { } }; } - -/// Deserialize the input arguments -/// -/// # Safety -#[allow(clippy::type_complexity)] -pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec>, &'a [u8]) { - let mut offset: usize = 0; - - // Number of accounts present - - #[allow(clippy::cast_ptr_alignment)] - let num_accounts = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - // Account Infos - - let mut accounts = Vec::with_capacity(num_accounts); - for _ in 0..num_accounts { - let dup_info = *(input.add(offset) as *const u8); - offset += size_of::(); - if dup_info == std::u8::MAX { - #[allow(clippy::cast_ptr_alignment)] - let is_signer = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let is_writable = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - let key: &Pubkey = &*(input.add(offset) as *const Pubkey); - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64))); - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let data_len = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - let data = Rc::new(RefCell::new({ - from_raw_parts_mut(input.add(offset), data_len) - })); - offset += data_len; - - let owner: &Pubkey = &*(input.add(offset) as *const Pubkey); - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let executable = *(input.add(offset) as *const u8) != 0; - offset += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let rent_epoch = *(input.add(offset) as *const u64); - offset += size_of::(); - - accounts.push(AccountInfo { - is_signer, - is_writable, - key, - lamports, - data, - owner, - executable, - rent_epoch, - }); - } else { - // Duplicate account, clone the original - accounts.push(accounts[dup_info as usize].clone()); - } - } - - // Instruction data - - #[allow(clippy::cast_ptr_alignment)] - let instruction_data_len = *(input.add(offset) as *const u64) as usize; - offset += size_of::(); - - let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) }; - offset += instruction_data_len; - - // Program Id - - let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey); - - (program_id, accounts, instruction_data) -} diff --git a/sdk/src/genesis_config.rs b/sdk/src/genesis_config.rs index 58631b408ff76f..42ebdf1ab7654a 100644 --- a/sdk/src/genesis_config.rs +++ b/sdk/src/genesis_config.rs @@ -1,5 +1,7 @@ //! The `genesis_config` module is a library for generating the chain's genesis config. +#![cfg(feature = "everything")] + use crate::{ account::Account, clock::{UnixTimestamp, DEFAULT_TICKS_PER_SLOT}, diff --git a/sdk/src/hard_forks.rs b/sdk/src/hard_forks.rs index 2b65419db196c3..07784f43c6d21e 100644 --- a/sdk/src/hard_forks.rs +++ b/sdk/src/hard_forks.rs @@ -1,6 +1,8 @@ //! The `hard_forks` module is used to maintain the list of slot boundaries for when a hard fork //! should occur. +#![cfg(feature = "everything")] + use byteorder::{ByteOrder, LittleEndian}; use solana_sdk::clock::Slot; diff --git a/sdk/src/hash.rs b/sdk/src/hash.rs index 92c4808f35bec7..576d2b21d2b37e 100644 --- a/sdk/src/hash.rs +++ b/sdk/src/hash.rs @@ -1,82 +1,6 @@ -//! The `hash` module provides functions for creating SHA-256 hashes. +pub use solana_program_sdk::hash::*; -use crate::sanitize::Sanitize; -use sha2::{Digest, Sha256}; -use std::{convert::TryFrom, fmt, mem, str::FromStr}; -use thiserror::Error; - -pub const HASH_BYTES: usize = 32; -#[derive( - Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample, -)] -#[repr(transparent)] -pub struct Hash(pub [u8; HASH_BYTES]); - -#[derive(Clone, Default)] -pub struct Hasher { - hasher: Sha256, -} - -impl Hasher { - pub fn hash(&mut self, val: &[u8]) { - self.hasher.input(val); - } - pub fn hashv(&mut self, vals: &[&[u8]]) { - for val in vals { - self.hash(val); - } - } - pub fn result(self) -> Hash { - // At the time of this writing, the sha2 library is stuck on an old version - // of generic_array (0.9.0). Decouple ourselves with a clone to our version. - Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.result().as_slice()).unwrap()) - } -} - -impl Sanitize for Hash {} - -impl AsRef<[u8]> for Hash { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl fmt::Debug for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -impl fmt::Display for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum ParseHashError { - #[error("string decoded to wrong size for hash")] - WrongSize, - #[error("failed to decoded string to hash")] - Invalid, -} - -impl FromStr for Hash { - type Err = ParseHashError; - - fn from_str(s: &str) -> Result { - let bytes = bs58::decode(s) - .into_vec() - .map_err(|_| ParseHashError::Invalid)?; - if bytes.len() != mem::size_of::() { - Err(ParseHashError::WrongSize) - } else { - Ok(Hash::new(&bytes)) - } - } -} - -/// New random hash value for tests and benchmarks. +/// random hash value for tests and benchmarks. #[cfg(feature = "everything")] pub fn new_rand(rng: &mut R) -> Hash where @@ -86,106 +10,3 @@ where rng.fill(&mut buf); Hash::new(&buf) } - -impl Hash { - pub fn new(hash_slice: &[u8]) -> Self { - Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap()) - } - - pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self { - Self(hash_array) - } - - pub fn to_bytes(self) -> [u8; HASH_BYTES] { - self.0 - } - - /// New random hash value for tests and benchmarks. - #[cfg(all(feature = "everything", not(target_arch = "bpf")))] - #[deprecated(since = "1.3.9", note = "Please use 'hash::new_rand' instead")] - pub fn new_rand(rng: &mut R) -> Self - where - R: rand::Rng, - { - new_rand(rng) - } -} - -/// 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 - // not supported - #[cfg(not(all(feature = "program", target_arch = "bpf")))] - { - let mut hasher = Hasher::default(); - hasher.hashv(vals); - hasher.result() - } - // Call via a system call to perform the calculation - #[cfg(all(feature = "program", target_arch = "bpf"))] - { - extern "C" { - fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64; - }; - let mut hash_result = [0; HASH_BYTES]; - unsafe { - sol_sha256( - vals as *const _ as *const u8, - vals.len() as u64, - &mut hash_result as *mut _ as *mut u8, - ); - } - Hash::new_from_array(hash_result) - } -} - -/// Return a Sha256 hash for the given data. -pub fn hash(val: &[u8]) -> Hash { - hashv(&[val]) -} - -/// 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(); - hash_data.extend_from_slice(val); - hash(&hash_data) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hash_fromstr() { - let hash = hash(&[1u8]); - - let mut hash_base58_str = bs58::encode(hash).into_string(); - - assert_eq!(hash_base58_str.parse::(), Ok(hash)); - - hash_base58_str.push_str(&bs58::encode(hash.0).into_string()); - assert_eq!( - hash_base58_str.parse::(), - Err(ParseHashError::WrongSize) - ); - - hash_base58_str.truncate(hash_base58_str.len() / 2); - assert_eq!(hash_base58_str.parse::(), Ok(hash)); - - hash_base58_str.truncate(hash_base58_str.len() / 2); - assert_eq!( - hash_base58_str.parse::(), - Err(ParseHashError::WrongSize) - ); - - let mut hash_base58_str = bs58::encode(hash.0).into_string(); - assert_eq!(hash_base58_str.parse::(), Ok(hash)); - - // throw some non-base58 stuff in there - hash_base58_str.replace_range(..1, "I"); - assert_eq!( - hash_base58_str.parse::(), - Err(ParseHashError::Invalid) - ); - } -} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 6b9d6da1e3f785..668b73deded198 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -4,47 +4,36 @@ // Allows macro expansion of `use ::solana_sdk::*` to work within this crate extern crate self as solana_sdk; -pub mod account; -pub mod account_utils; -pub mod bpf_loader; -pub mod bpf_loader_deprecated; +pub use solana_program_sdk::*; + pub mod builtins; -pub mod clock; +pub mod client; pub mod commitment_config; -pub mod decode_error; pub mod deserialize_utils; +pub mod entrypoint; +pub mod entrypoint_deprecated; pub mod entrypoint_native; pub mod epoch_info; -pub mod epoch_schedule; -pub mod fee_calculator; +pub mod genesis_config; +pub mod hard_forks; pub mod hash; -pub mod incinerator; pub mod inflation; -pub mod instruction; -pub mod loader_instruction; -pub mod message; +pub mod log; pub mod native_loader; -pub mod native_token; -pub mod nonce; pub mod packet; pub mod poh_config; -pub mod program_option; -pub mod program_pack; pub mod program_utils; pub mod pubkey; -pub mod rent; pub mod rpc_port; -pub mod sanitize; -pub mod secp256k1_program; -pub mod short_vec; -pub mod slot_hashes; -pub mod slot_history; -pub mod stake_history; +pub mod secp256k1; +pub mod shred_version; +pub mod signature; +pub mod signers; pub mod stake_weighted_timestamp; -pub mod system_instruction; -pub mod system_program; -pub mod sysvar; +pub mod system_transaction; pub mod timing; +pub mod transaction; +pub mod transport; /// Convenience macro to declare a static public key and functions to interact with it /// @@ -72,54 +61,16 @@ pub use solana_sdk_macro::pubkeys; #[rustversion::since(1.46.0)] pub use solana_sdk_macro::respan; -// On-chain program specific modules -pub mod account_info; -pub mod entrypoint; -pub mod entrypoint_deprecated; -pub mod log; -pub mod program; -pub mod program_error; - -#[cfg(all(feature = "program", not(target_arch = "bpf")))] -extern crate lazy_static; - -#[cfg(all(feature = "program", not(target_arch = "bpf")))] -pub mod program_stubs; - -// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with v1.3.x programs +// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with older programs #[macro_export] #[deprecated( - since = "1.4.2", + since = "1.4.3", note = "program_stubs macro is obsolete and can be safely removed" )] macro_rules! program_stubs { () => {}; } -pub mod serialize_utils; - -// Modules not usable by on-chain programs -#[cfg(feature = "everything")] -pub mod client; -#[cfg(feature = "everything")] -pub mod genesis_config; -#[cfg(feature = "everything")] -pub mod hard_forks; -#[cfg(feature = "everything")] -pub mod secp256k1; -#[cfg(feature = "everything")] -pub mod shred_version; -#[cfg(feature = "everything")] -pub mod signature; -#[cfg(feature = "everything")] -pub mod signers; -#[cfg(feature = "everything")] -pub mod system_transaction; -#[cfg(feature = "everything")] -pub mod transaction; -#[cfg(feature = "everything")] -pub mod transport; - #[macro_use] extern crate serde_derive; pub extern crate bs58; diff --git a/sdk/src/log.rs b/sdk/src/log.rs index 1c6d5f0ea3ebc9..e2d2fb966835a2 100644 --- a/sdk/src/log.rs +++ b/sdk/src/log.rs @@ -1,14 +1,9 @@ -//! @brief Solana Rust-based BPF program logging - #![cfg(feature = "program")] -use crate::account_info::AccountInfo; +pub use solana_program_sdk::log::*; -/// Prints a string -/// There are two forms and are fast -/// 1. Single string -/// 2. 5 integers #[macro_export] +#[deprecated(since = "1.4.3", note = "solana_program_sdk::log::info instead")] macro_rules! info { ($msg:expr) => { $crate::log::sol_log($msg) @@ -21,80 +16,5 @@ macro_rules! info { $arg4 as u64, $arg5 as u64, ) - }; // `format!()` is not supported yet, Issue #3099 - // `format!()` incurs a very large runtime overhead so it should be used with care - // ($($arg:tt)*) => ($crate::log::sol_log(&format!($($arg)*))); -} - -/// Prints a string to stdout -/// -/// @param message - Message to print -#[inline] -pub fn sol_log(message: &str) { - #[cfg(target_arch = "bpf")] - unsafe { - sol_log_(message.as_ptr(), message.len() as u64); - } - - #[cfg(not(target_arch = "bpf"))] - crate::program_stubs::sol_log(message); -} - -#[cfg(target_arch = "bpf")] -extern "C" { - fn sol_log_(message: *const u8, len: u64); -} - -/// Prints 64 bit values represented as hexadecimal to stdout -/// -/// @param argx - integer arguments to print - -#[inline] -pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { - #[cfg(target_arch = "bpf")] - unsafe { - sol_log_64_(arg1, arg2, arg3, arg4, arg5); - } - - #[cfg(not(target_arch = "bpf"))] - crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5); -} - -#[cfg(target_arch = "bpf")] -extern "C" { - fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); -} - -/// Prints the hexadecimal representation of a slice -/// -/// @param slice - The array to print -#[allow(dead_code)] -pub fn sol_log_slice(slice: &[u8]) { - for (i, s) in slice.iter().enumerate() { - info!(0, 0, 0, i, *s); - } -} - -/// Prints the hexadecimal representation of the program's input parameters -/// -/// @param ka - A pointer to an array of `AccountInfo` to print -/// @param data - A pointer to the instruction data to print -#[allow(dead_code)] -pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) { - for (i, account) in accounts.iter().enumerate() { - info!("AccountInfo"); - info!(0, 0, 0, 0, i); - info!("- Is signer"); - info!(0, 0, 0, 0, account.is_signer); - info!("- Key"); - account.key.log(); - info!("- Lamports"); - info!(0, 0, 0, 0, account.lamports()); - info!("- Account data length"); - info!(0, 0, 0, 0, account.data_len()); - info!("- Owner"); - account.owner.log(); - } - info!("Instruction data"); - sol_log_slice(data); + }; } diff --git a/sdk/src/pubkey.rs b/sdk/src/pubkey.rs index c05e044a36c83c..275de86e6066d9 100644 --- a/sdk/src/pubkey.rs +++ b/sdk/src/pubkey.rs @@ -1,71 +1,4 @@ -use crate::{decode_error::DecodeError, hash::hashv}; -use num_derive::{FromPrimitive, ToPrimitive}; -use std::{convert::TryFrom, fmt, mem, str::FromStr}; -use thiserror::Error; - -pub use bs58; - -/// maximum length of derived pubkey seed -pub const MAX_SEED_LEN: usize = 32; - -#[derive(Error, Debug, Serialize, Clone, PartialEq, 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, -} -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"), - } - } -} - -#[repr(transparent)] -#[derive( - Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample, -)] -pub struct Pubkey([u8; 32]); - -impl crate::sanitize::Sanitize for Pubkey {} - -#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)] -pub enum ParsePubkeyError { - #[error("String is the wrong size")] - WrongSize, - #[error("Invalid Base58 string")] - Invalid, -} -impl DecodeError for ParsePubkeyError { - fn type_of() -> &'static str { - "ParsePubkeyError" - } -} - -impl FromStr for Pubkey { - type Err = ParsePubkeyError; - - fn from_str(s: &str) -> Result { - let pubkey_vec = bs58::decode(s) - .into_vec() - .map_err(|_| ParsePubkeyError::Invalid)?; - if pubkey_vec.len() != mem::size_of::() { - Err(ParsePubkeyError::WrongSize) - } else { - Ok(Pubkey::new(&pubkey_vec)) - } - } -} +pub use solana_program_sdk::pubkey::*; /// New random Pubkey for tests and benchmarks. #[cfg(feature = "everything")] @@ -73,171 +6,6 @@ pub fn new_rand() -> Pubkey { Pubkey::new(&rand::random::<[u8; 32]>()) } -impl Pubkey { - pub fn new(pubkey_vec: &[u8]) -> Self { - Self( - <[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec)) - .expect("Slice must be the same length as a Pubkey"), - ) - } - - pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self { - Self(pubkey_array) - } - - pub fn create_with_seed( - base: &Pubkey, - seed: &str, - owner: &Pubkey, - ) -> Result { - if seed.len() > MAX_SEED_LEN { - return Err(PubkeyError::MaxSeedLengthExceeded); - } - - Ok(Pubkey::new( - hashv(&[base.as_ref(), seed.as_ref(), owner.as_ref()]).as_ref(), - )) - } - - /// Create a program address - /// - /// Program addresses are account keys that only the program 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. - /// The runtime will check that indeed the program associated with this - /// address is the caller and thus authorized to be the signer. - /// - /// Because the program address cannot lie on the ed25519 curve there may be - /// seed and program id combinations that are invalid. In these cases an - /// extra seed (bump seed) can be calculated that results in a point off the - /// curve. Use `find_program_address` to calculate that bump seed. - /// - /// 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 change 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 themselves. - pub fn create_program_address( - seeds: &[&[u8]], - program_id: &Pubkey, - ) -> Result { - // Perform the calculation inline, calling this from within a program is - // not supported - #[cfg(not(all(feature = "program", target_arch = "bpf")))] - { - let mut hasher = crate::hash::Hasher::default(); - for seed in seeds.iter() { - if seed.len() > MAX_SEED_LEN { - return Err(PubkeyError::MaxSeedLengthExceeded); - } - hasher.hash(seed); - } - hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]); - let hash = hasher.result(); - - if curve25519_dalek::edwards::CompressedEdwardsY::from_slice(hash.as_ref()) - .decompress() - .is_some() - { - return Err(PubkeyError::InvalidSeeds); - } - - Ok(Pubkey::new(hash.as_ref())) - } - // Call via a system call to perform the calculation - #[cfg(all(feature = "program", target_arch = "bpf"))] - { - extern "C" { - fn sol_create_program_address( - seeds_addr: *const u8, - seeds_len: u64, - program_id_addr: *const u8, - address_bytes_addr: *const u8, - ) -> u64; - }; - let mut bytes = [0; 32]; - let result = unsafe { - 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::new(&bytes)), - _ => Err(result.into()), - } - } - } - - /// Find a valid program address and its corresponding bump seed which must be passed - /// as an additional seed when calling `invoke_signed` - #[allow(clippy::same_item_push)] - pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) { - let mut bump_seed = [std::u8::MAX]; - for _ in 0..std::u8::MAX { - { - let mut seeds_with_bump = seeds.to_vec(); - seeds_with_bump.push(&bump_seed); - if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) { - return (address, bump_seed[0]); - } - } - bump_seed[0] -= 1; - } - panic!("Unable to find a viable program address bump seed"); - } - - #[cfg(all(feature = "everything", not(target_arch = "bpf")))] - #[deprecated(since = "1.3.9", note = "Please use 'pubkey::new_rand' instead")] - pub fn new_rand() -> Self { - // Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe - new_rand() - } - - pub fn to_bytes(self) -> [u8; 32] { - self.0 - } - - /// Log a `Pubkey` from a program - pub fn log(&self) { - #[cfg(all(feature = "program", target_arch = "bpf"))] - { - extern "C" { - fn sol_log_pubkey(pubkey_addr: *const u8); - }; - unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; - } - - #[cfg(all(feature = "program", not(target_arch = "bpf")))] - crate::program_stubs::sol_log(&self.to_string()); - } -} - -impl AsRef<[u8]> for Pubkey { - fn as_ref(&self) -> &[u8] { - &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 = "everything")] pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box> { use std::io::Write; @@ -258,192 +26,15 @@ pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box Result> { let f = std::fs::File::open(infile.to_string())?; let printable: String = serde_json::from_reader(f)?; + + use std::str::FromStr; Ok(Pubkey::from_str(&printable)?) } #[cfg(test)] mod tests { use super::*; - use std::{fs::remove_file, str::from_utf8}; - - #[test] - fn pubkey_fromstr() { - let pubkey = solana_sdk::pubkey::new_rand(); - 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) - ); - } - - #[test] - fn test_create_with_seed() { - assert!(Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - "☉", - &solana_sdk::pubkey::new_rand() - ) - .is_ok()); - assert_eq!( - Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - from_utf8(&[127; MAX_SEED_LEN + 1]).unwrap(), - &solana_sdk::pubkey::new_rand() - ), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - assert!(Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - "\ - \u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ - ", - &solana_sdk::pubkey::new_rand() - ) - .is_ok()); - // utf-8 abuse ;) - assert_eq!( - Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - "\ - x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\ - ", - &solana_sdk::pubkey::new_rand() - ), - Err(PubkeyError::MaxSeedLengthExceeded) - ); - - assert!(Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - std::str::from_utf8(&[0; MAX_SEED_LEN]).unwrap(), - &solana_sdk::pubkey::new_rand(), - ) - .is_ok()); - - assert!(Pubkey::create_with_seed( - &solana_sdk::pubkey::new_rand(), - "", - &solana_sdk::pubkey::new_rand(), - ) - .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 program_id = Pubkey::from_str("BPFLoader1111111111111111111111111111111111").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(&[b"", &[1]], &program_id), - Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&["☉".as_ref()], &program_id), - Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id), - Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds" - .parse() - .unwrap()) - ); - assert_eq!( - Pubkey::create_program_address(&[public_key.as_ref()], &program_id), - Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K" - .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 = solana_sdk::pubkey::new_rand(); - 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 = solana_sdk::pubkey::new_rand(); - 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() - ); - } - } + use std::fs::remove_file; #[test] fn test_read_write_pubkey() -> Result<(), Box> { diff --git a/sdk/src/secp256k1.rs b/sdk/src/secp256k1.rs index 4caec54b221186..b5f30be8bae626 100644 --- a/sdk/src/secp256k1.rs +++ b/sdk/src/secp256k1.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "everything")] + use digest::Digest; use serde_derive::{Deserialize, Serialize}; diff --git a/sdk/src/secp256k1_program.rs b/sdk/src/secp256k1_program.rs deleted file mode 100644 index ebce5835b9c82a..00000000000000 --- a/sdk/src/secp256k1_program.rs +++ /dev/null @@ -1 +0,0 @@ -solana_sdk::declare_id!("KeccakSecp256k11111111111111111111111111111"); diff --git a/sdk/src/shred_version.rs b/sdk/src/shred_version.rs index 5406fd57c358e6..f06b66f374dff6 100644 --- a/sdk/src/shred_version.rs +++ b/sdk/src/shred_version.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "everything")] + use solana_sdk::{ hard_forks::HardForks, hash::{extend_and_hash, Hash}, diff --git a/sdk/src/signature.rs b/sdk/src/signature.rs index 0a8985a23e01a6..a45a4f966fe1de 100644 --- a/sdk/src/signature.rs +++ b/sdk/src/signature.rs @@ -1,4 +1,5 @@ //! The `signature` module provides functionality for public, and private keys. +#![cfg(feature = "everything")] use crate::{pubkey::Pubkey, transaction::TransactionError}; use ed25519_dalek::Signer as DalekSigner; diff --git a/sdk/src/signers.rs b/sdk/src/signers.rs index e99f925ed48024..1c7b9a5502df08 100644 --- a/sdk/src/signers.rs +++ b/sdk/src/signers.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "everything")] use crate::{ pubkey::Pubkey, signature::{Signature, Signer, SignerError}, diff --git a/sdk/src/system_transaction.rs b/sdk/src/system_transaction.rs index af3b8ce3fc09c6..cd23335c6c5a3f 100644 --- a/sdk/src/system_transaction.rs +++ b/sdk/src/system_transaction.rs @@ -1,4 +1,5 @@ //! The `system_transaction` module provides functionality for creating system transactions. +#![cfg(feature = "everything")] use crate::{ hash::Hash, diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index 66d6c89665bdd0..0b49eef82921fe 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -1,5 +1,7 @@ //! Defines a Transaction type to package an atomic sequence of instructions. +#![cfg(feature = "everything")] + use crate::sanitize::{Sanitize, SanitizeError}; use crate::secp256k1::verify_eth_addresses; use crate::{ diff --git a/sdk/src/transport.rs b/sdk/src/transport.rs index 84c19807facb3f..287047e7696c3c 100644 --- a/sdk/src/transport.rs +++ b/sdk/src/transport.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "everything")] + use crate::transaction::TransactionError; use std::io; use thiserror::Error;