Initial population of solana-program-sdk
This commit is contained in:
parent
3718771ffb
commit
63db324204
|
@ -4379,6 +4379,36 @@ dependencies = [
|
||||||
"solana-version",
|
"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]]
|
[[package]]
|
||||||
name = "solana-ramp-tps"
|
name = "solana-ramp-tps"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -4545,7 +4575,6 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
"itertools 0.9.0",
|
"itertools 0.9.0",
|
||||||
"lazy_static",
|
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
"log 0.4.8",
|
"log 0.4.8",
|
||||||
"memmap",
|
"memmap",
|
||||||
|
@ -4566,6 +4595,7 @@ dependencies = [
|
||||||
"solana-frozen-abi",
|
"solana-frozen-abi",
|
||||||
"solana-frozen-abi-macro",
|
"solana-frozen-abi-macro",
|
||||||
"solana-logger 1.5.0",
|
"solana-logger 1.5.0",
|
||||||
|
"solana-program-sdk",
|
||||||
"solana-sdk-macro 1.5.0",
|
"solana-sdk-macro 1.5.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tiny-bip39",
|
"tiny-bip39",
|
||||||
|
|
|
@ -27,8 +27,8 @@ declare print_free_tree=(
|
||||||
':sdk/bpf/rust/rust-utils/**.rs'
|
':sdk/bpf/rust/rust-utils/**.rs'
|
||||||
':sdk/**.rs'
|
':sdk/**.rs'
|
||||||
':^sdk/cargo-build-bpf/**.rs'
|
':^sdk/cargo-build-bpf/**.rs'
|
||||||
':^sdk/src/program_option.rs'
|
':^sdk/program/src/program_option.rs'
|
||||||
':^sdk/src/program_stubs.rs'
|
':^sdk/program/src/program_stubs.rs'
|
||||||
':programs/**.rs'
|
':programs/**.rs'
|
||||||
':^**bin**.rs'
|
':^**bin**.rs'
|
||||||
':^**bench**.rs'
|
':^**bench**.rs'
|
||||||
|
|
|
@ -40,6 +40,9 @@ test-stable)
|
||||||
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||||
;;
|
;;
|
||||||
test-stable-perf)
|
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
|
# BPF program tests
|
||||||
_ make -C programs/bpf/c tests
|
_ make -C programs/bpf/c tests
|
||||||
_ cargo +"$rust_stable" test \
|
_ cargo +"$rust_stable" test \
|
||||||
|
|
|
@ -2110,6 +2110,32 @@ dependencies = [
|
||||||
"solana-sdk",
|
"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]]
|
[[package]]
|
||||||
name = "solana-rayon-threadlimit"
|
name = "solana-rayon-threadlimit"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -2177,14 +2203,12 @@ dependencies = [
|
||||||
"bv",
|
"bv",
|
||||||
"byteorder 1.3.4",
|
"byteorder 1.3.4",
|
||||||
"chrono",
|
"chrono",
|
||||||
"curve25519-dalek 2.1.0",
|
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"generic-array 0.14.3",
|
"generic-array 0.14.3",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
"log",
|
"log",
|
||||||
"memmap",
|
"memmap",
|
||||||
|
@ -2205,6 +2229,7 @@ dependencies = [
|
||||||
"solana-frozen-abi",
|
"solana-frozen-abi",
|
||||||
"solana-frozen-abi-macro",
|
"solana-frozen-abi-macro",
|
||||||
"solana-logger",
|
"solana-logger",
|
||||||
|
"solana-program-sdk",
|
||||||
"solana-sdk-macro",
|
"solana-sdk-macro",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,9 +9,12 @@ license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[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 = []
|
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 = [
|
default = [
|
||||||
"everything"
|
"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 }
|
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
hmac = "0.7.0"
|
hmac = "0.7.0"
|
||||||
itertools = { version = "0.9.0" }
|
itertools = "0.9.0"
|
||||||
lazy_static = "1.4.0"
|
log = "0.4.8"
|
||||||
log = { version = "0.4.8" }
|
|
||||||
memmap = { version = "0.7.0", optional = true }
|
memmap = { version = "0.7.0", optional = true }
|
||||||
num-derive = { version = "0.3" }
|
num-derive = "0.3"
|
||||||
num-traits = { version = "0.2" }
|
num-traits = "0.2"
|
||||||
pbkdf2 = { version = "0.3.0", default-features = false }
|
pbkdf2 = { version = "0.3.0", default-features = false }
|
||||||
rand = { version = "0.7.0", optional = true }
|
rand = { version = "0.7.0", optional = true }
|
||||||
rand_chacha = { version = "0.2.2", 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 }
|
sha3 = { version = "0.9.1", optional = true }
|
||||||
digest = { version = "0.9.0", optional = true }
|
digest = { version = "0.9.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
|
|
||||||
curve25519-dalek = { version = "2.1.0" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
curve25519-dalek = "2.1.0"
|
curve25519-dalek = "2.1.0"
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rustc_version = "0.2"
|
rustc_version = "0.2"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -17,49 +17,81 @@ use syn::{
|
||||||
Expr, Ident, LitByte, LitStr, Path, Token,
|
Expr, Ident, LitByte, LitStr, Path, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Id(proc_macro2::TokenStream);
|
fn parse_id(
|
||||||
impl Parse for Id {
|
input: ParseStream,
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
pubkey_type: proc_macro2::TokenStream,
|
||||||
let token_stream = if input.peek(syn::LitStr) {
|
) -> Result<proc_macro2::TokenStream> {
|
||||||
let id_literal: LitStr = input.parse()?;
|
let id = if input.peek(syn::LitStr) {
|
||||||
parse_pubkey(&id_literal)?
|
let id_literal: LitStr = input.parse()?;
|
||||||
} else {
|
parse_pubkey(&id_literal, &pubkey_type)?
|
||||||
let expr: Expr = input.parse()?;
|
} else {
|
||||||
quote! { #expr }
|
let expr: Expr = input.parse()?;
|
||||||
};
|
quote! { #expr }
|
||||||
|
};
|
||||||
|
|
||||||
if !input.is_empty() {
|
if !input.is_empty() {
|
||||||
let stream: proc_macro2::TokenStream = input.parse()?;
|
let stream: proc_macro2::TokenStream = input.parse()?;
|
||||||
return Err(syn::Error::new_spanned(stream, "unexpected token"));
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Id(token_stream))
|
/// Returns the program ID
|
||||||
|
pub fn id() -> #pubkey_type {
|
||||||
|
ID
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
|
assert!(check_id(&id()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Id(proc_macro2::TokenStream);
|
||||||
|
|
||||||
|
impl Parse for Id {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
parse_id(input, quote! { ::solana_sdk::pubkey::Pubkey }).map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for Id {
|
impl ToTokens for Id {
|
||||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
let id = &self.0;
|
id_to_tokens(&self.0, quote! { ::solana_sdk::pubkey::Pubkey }, tokens)
|
||||||
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
|
struct ProgramSdkId(proc_macro2::TokenStream);
|
||||||
pub fn check_id(id: &::solana_sdk::pubkey::Pubkey) -> bool {
|
|
||||||
id == &ID
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the program ID
|
impl Parse for ProgramSdkId {
|
||||||
pub fn id() -> ::solana_sdk::pubkey::Pubkey {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
ID
|
parse_id(input, quote! { ::solana_program_sdk::pubkey::Pubkey }).map(Self)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
impl ToTokens for ProgramSdkId {
|
||||||
#[test]
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
fn test_id() {
|
id_to_tokens(
|
||||||
assert!(check_id(&id()));
|
&self.0,
|
||||||
}
|
quote! { ::solana_program_sdk::pubkey::Pubkey },
|
||||||
});
|
tokens,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +167,16 @@ pub fn declare_id(input: TokenStream) -> TokenStream {
|
||||||
TokenStream::from(quote! {#id})
|
TokenStream::from(quote! {#id})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pubkey(id_literal: &LitStr) -> Result<proc_macro2::TokenStream> {
|
#[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<proc_macro2::TokenStream> {
|
||||||
let id_vec = bs58::decode(id_literal.value())
|
let id_vec = bs58::decode(id_literal.value())
|
||||||
.into_vec()
|
.into_vec()
|
||||||
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
|
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
|
||||||
|
@ -147,7 +188,7 @@ fn parse_pubkey(id_literal: &LitStr) -> Result<proc_macro2::TokenStream> {
|
||||||
})?;
|
})?;
|
||||||
let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
|
let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
::solana_sdk::pubkey::Pubkey::new_from_array(
|
#pubkey_type::new_from_array(
|
||||||
[#(#bytes,)*]
|
[#(#bytes,)*]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -160,11 +201,15 @@ struct Pubkeys {
|
||||||
}
|
}
|
||||||
impl Parse for Pubkeys {
|
impl Parse for Pubkeys {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let pubkey_type = quote! {
|
||||||
|
::solana_sdk::pubkey::Pubkey
|
||||||
|
};
|
||||||
|
|
||||||
let method = input.parse()?;
|
let method = input.parse()?;
|
||||||
let _comma: Token![,] = input.parse()?;
|
let _comma: Token![,] = input.parse()?;
|
||||||
let (num, pubkeys) = if input.peek(syn::LitStr) {
|
let (num, pubkeys) = if input.peek(syn::LitStr) {
|
||||||
let id_literal: LitStr = input.parse()?;
|
let id_literal: LitStr = input.parse()?;
|
||||||
(1, parse_pubkey(&id_literal)?)
|
(1, parse_pubkey(&id_literal, &pubkey_type)?)
|
||||||
} else if input.peek(Bracket) {
|
} else if input.peek(Bracket) {
|
||||||
let pubkey_strings;
|
let pubkey_strings;
|
||||||
bracketed!(pubkey_strings in input);
|
bracketed!(pubkey_strings in input);
|
||||||
|
@ -172,7 +217,7 @@ impl Parse for Pubkeys {
|
||||||
Punctuated::parse_terminated(&pubkey_strings)?;
|
Punctuated::parse_terminated(&pubkey_strings)?;
|
||||||
let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
|
let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
|
||||||
for string in punctuated.iter() {
|
for string in punctuated.iter() {
|
||||||
pubkeys.push(parse_pubkey(string)?);
|
pubkeys.push(parse_pubkey(string, &pubkey_type)?);
|
||||||
}
|
}
|
||||||
(pubkeys.len(), quote! {#pubkeys})
|
(pubkeys.len(), quote! {#pubkeys})
|
||||||
} else {
|
} else {
|
||||||
|
@ -195,15 +240,19 @@ impl ToTokens for Pubkeys {
|
||||||
num,
|
num,
|
||||||
pubkeys,
|
pubkeys,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
let pubkey_type = quote! {
|
||||||
|
::solana_sdk::pubkey::Pubkey
|
||||||
|
};
|
||||||
if *num == 1 {
|
if *num == 1 {
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
pub fn #method() -> ::solana_sdk::pubkey::Pubkey {
|
pub fn #method() -> #pubkey_type {
|
||||||
#pubkeys
|
#pubkeys
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
pub fn #method() -> ::std::vec::Vec<::solana_sdk::pubkey::Pubkey> {
|
pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
|
||||||
vec![#pubkeys]
|
vec![#pubkeys]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,8 +9,37 @@ license = "Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[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 = "1.0.112"
|
||||||
|
serde_bytes = "0.11"
|
||||||
serde_derive = "1.0.103"
|
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]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1 @@
|
||||||
|
../../frozen-abi/build.rs
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
|
|
||||||
/// An Account with data that is stored on chain
|
/// An Account with data that is stored on chain
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[frozen_abi(digest = "By9FhuLAM947tkLxbTVQru9ZKTrRQuvCR5W387nPSLNu")]
|
#[frozen_abi(digest = "AVG6bXqVUippoYRAE8e1ewHeZi1UvFx5r5gKspFSEbxM")]
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Account {
|
pub struct Account {
|
|
@ -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<AccountInfo<'a>>, &'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::<u64>();
|
||||||
|
|
||||||
|
// 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::<u8>();
|
||||||
|
if dup_info == std::u8::MAX {
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let is_signer = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let is_writable = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let executable = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
offset += size_of::<u32>(); // padding to u64
|
||||||
|
|
||||||
|
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||||
|
offset += size_of::<Pubkey>();
|
||||||
|
|
||||||
|
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||||
|
offset += size_of::<Pubkey>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let data_len = *(input.add(offset) as *const u64) as usize;
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
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::<u128>()); // padding
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let rent_epoch = *(input.add(offset) as *const u64);
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
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::<u64>();
|
||||||
|
|
||||||
|
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::<u8>()).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::<u8>()).unwrap()) };
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u8>()));
|
||||||
|
let ptr =
|
||||||
|
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap()) };
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u16>()));
|
||||||
|
let ptr =
|
||||||
|
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap()) };
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u32>()));
|
||||||
|
let ptr =
|
||||||
|
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap()) };
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u64>()));
|
||||||
|
let ptr =
|
||||||
|
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap()) };
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u128>()));
|
||||||
|
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::<u8>()).unwrap()) };
|
||||||
|
assert_ne!(ptr, null_mut());
|
||||||
|
assert_eq!(0, ptr.align_offset(size_of::<u64>()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<AccountInfo<'a>>, &'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::<u64>();
|
||||||
|
|
||||||
|
// 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::<u8>();
|
||||||
|
if dup_info == std::u8::MAX {
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let is_signer = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let is_writable = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||||
|
offset += size_of::<Pubkey>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let data_len = *(input.add(offset) as *const u64) as usize;
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
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::<Pubkey>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let executable = *(input.add(offset) as *const u8) != 0;
|
||||||
|
offset += size_of::<u8>();
|
||||||
|
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
let rent_epoch = *(input.add(offset) as *const u64);
|
||||||
|
offset += size_of::<u64>();
|
||||||
|
|
||||||
|
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::<u64>();
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
|
@ -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<Self, Self::Err> {
|
||||||
|
let bytes = bs58::decode(s)
|
||||||
|
.into_vec()
|
||||||
|
.map_err(|_| ParseHashError::Invalid)?;
|
||||||
|
if bytes.len() != mem::size_of::<Hash>() {
|
||||||
|
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::<Hash>(), Ok(hash));
|
||||||
|
|
||||||
|
hash_base58_str.push_str(&bs58::encode(hash.0).into_string());
|
||||||
|
assert_eq!(
|
||||||
|
hash_base58_str.parse::<Hash>(),
|
||||||
|
Err(ParseHashError::WrongSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
hash_base58_str.truncate(hash_base58_str.len() / 2);
|
||||||
|
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
|
||||||
|
|
||||||
|
hash_base58_str.truncate(hash_base58_str.len() / 2);
|
||||||
|
assert_eq!(
|
||||||
|
hash_base58_str.parse::<Hash>(),
|
||||||
|
Err(ParseHashError::WrongSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut hash_base58_str = bs58::encode(hash.0).into_string();
|
||||||
|
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
|
||||||
|
|
||||||
|
// throw some non-base58 stuff in there
|
||||||
|
hash_base58_str.replace_range(..1, "I");
|
||||||
|
assert_eq!(
|
||||||
|
hash_base58_str.parse::<Hash>(),
|
||||||
|
Err(ParseHashError::Invalid)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate solana_frozen_abi_macro;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -426,10 +426,7 @@ impl Message {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::instruction::AccountMeta;
|
||||||
instruction::AccountMeta,
|
|
||||||
signature::{Keypair, Signer},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_unique_program_ids() {
|
fn test_message_unique_program_ids() {
|
||||||
|
@ -444,7 +441,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_unique_program_ids_not_adjacent() {
|
fn test_message_unique_program_ids_not_adjacent() {
|
||||||
let program_id0 = Pubkey::default();
|
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(&[
|
let program_ids = get_program_ids(&[
|
||||||
Instruction::new(program_id0, &0, vec![]),
|
Instruction::new(program_id0, &0, vec![]),
|
||||||
Instruction::new(program_id1, &0, vec![]),
|
Instruction::new(program_id1, &0, vec![]),
|
||||||
|
@ -455,7 +452,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_unique_program_ids_order_preserved() {
|
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_id1 = Pubkey::default(); // Key less than program_id0
|
||||||
let program_ids = get_program_ids(&[
|
let program_ids = get_program_ids(&[
|
||||||
Instruction::new(program_id0, &0, vec![]),
|
Instruction::new(program_id0, &0, vec![]),
|
||||||
|
@ -558,7 +555,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_unique_keys_order_preserved() {
|
fn test_message_unique_keys_order_preserved() {
|
||||||
let program_id = Pubkey::default();
|
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 id1 = Pubkey::default(); // Key less than id0
|
||||||
let keys = get_keys(
|
let keys = get_keys(
|
||||||
&[
|
&[
|
||||||
|
@ -574,7 +571,7 @@ mod tests {
|
||||||
fn test_message_unique_keys_not_adjacent() {
|
fn test_message_unique_keys_not_adjacent() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let id0 = Pubkey::default();
|
let id0 = Pubkey::default();
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let keys = get_keys(
|
let keys = get_keys(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||||
|
@ -590,7 +587,7 @@ mod tests {
|
||||||
fn test_message_signed_keys_first() {
|
fn test_message_signed_keys_first() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let id0 = Pubkey::default();
|
let id0 = Pubkey::default();
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let keys = get_keys(
|
let keys = get_keys(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||||
|
@ -619,9 +616,9 @@ mod tests {
|
||||||
fn test_message_readonly_keys_last() {
|
fn test_message_readonly_keys_last() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let id0 = Pubkey::default(); // Identical key/program_id should be de-duped
|
let id0 = Pubkey::default(); // Identical key/program_id should be de-duped
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let id2 = solana_sdk::pubkey::new_rand();
|
let id2 = Pubkey::new_unique();
|
||||||
let id3 = solana_sdk::pubkey::new_rand();
|
let id3 = Pubkey::new_unique();
|
||||||
let keys = get_keys(
|
let keys = get_keys(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
|
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
|
||||||
|
@ -639,11 +636,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_kitchen_sink() {
|
fn test_message_kitchen_sink() {
|
||||||
let program_id0 = solana_sdk::pubkey::new_rand();
|
let program_id0 = Pubkey::new_unique();
|
||||||
let program_id1 = solana_sdk::pubkey::new_rand();
|
let program_id1 = Pubkey::new_unique();
|
||||||
let id0 = Pubkey::default();
|
let id0 = Pubkey::default();
|
||||||
let keypair1 = Keypair::new();
|
let id1 = Pubkey::new_unique();
|
||||||
let id1 = keypair1.pubkey();
|
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
|
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
|
||||||
|
@ -669,7 +665,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_payer_first() {
|
fn test_message_payer_first() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let payer = solana_sdk::pubkey::new_rand();
|
let payer = Pubkey::new_unique();
|
||||||
let id0 = Pubkey::default();
|
let id0 = Pubkey::default();
|
||||||
|
|
||||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
|
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
|
||||||
|
@ -692,8 +688,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_program_last() {
|
fn test_message_program_last() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let id0 = solana_sdk::pubkey::new_rand();
|
let id0 = Pubkey::new_unique();
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let keys = get_keys(
|
let keys = get_keys(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
|
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
|
||||||
|
@ -710,8 +706,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_program_position() {
|
fn test_program_position() {
|
||||||
let program_id0 = Pubkey::default();
|
let program_id0 = Pubkey::default();
|
||||||
let program_id1 = solana_sdk::pubkey::new_rand();
|
let program_id1 = Pubkey::new_unique();
|
||||||
let id = solana_sdk::pubkey::new_rand();
|
let id = Pubkey::new_unique();
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
|
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
|
||||||
|
@ -726,12 +722,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_writable() {
|
fn test_is_writable() {
|
||||||
let key0 = solana_sdk::pubkey::new_rand();
|
let key0 = Pubkey::new_unique();
|
||||||
let key1 = solana_sdk::pubkey::new_rand();
|
let key1 = Pubkey::new_unique();
|
||||||
let key2 = solana_sdk::pubkey::new_rand();
|
let key2 = Pubkey::new_unique();
|
||||||
let key3 = solana_sdk::pubkey::new_rand();
|
let key3 = Pubkey::new_unique();
|
||||||
let key4 = solana_sdk::pubkey::new_rand();
|
let key4 = Pubkey::new_unique();
|
||||||
let key5 = solana_sdk::pubkey::new_rand();
|
let key5 = Pubkey::new_unique();
|
||||||
|
|
||||||
let message = Message {
|
let message = Message {
|
||||||
header: MessageHeader {
|
header: MessageHeader {
|
||||||
|
@ -754,10 +750,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_account_keys_by_lock_type() {
|
fn test_get_account_keys_by_lock_type() {
|
||||||
let program_id = Pubkey::default();
|
let program_id = Pubkey::default();
|
||||||
let id0 = solana_sdk::pubkey::new_rand();
|
let id0 = Pubkey::new_unique();
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let id2 = solana_sdk::pubkey::new_rand();
|
let id2 = Pubkey::new_unique();
|
||||||
let id3 = solana_sdk::pubkey::new_rand();
|
let id3 = Pubkey::new_unique();
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||||
|
@ -776,12 +772,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decompile_instructions() {
|
fn test_decompile_instructions() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let program_id0 = solana_sdk::pubkey::new_rand();
|
let program_id0 = Pubkey::new_unique();
|
||||||
let program_id1 = solana_sdk::pubkey::new_rand();
|
let program_id1 = Pubkey::new_unique();
|
||||||
let id0 = solana_sdk::pubkey::new_rand();
|
let id0 = Pubkey::new_unique();
|
||||||
let id1 = solana_sdk::pubkey::new_rand();
|
let id1 = Pubkey::new_unique();
|
||||||
let id2 = solana_sdk::pubkey::new_rand();
|
let id2 = Pubkey::new_unique();
|
||||||
let id3 = solana_sdk::pubkey::new_rand();
|
let id3 = Pubkey::new_unique();
|
||||||
let instructions = vec![
|
let instructions = vec![
|
||||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
|
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
|
||||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, true)]),
|
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, true)]),
|
|
@ -170,12 +170,11 @@ pub fn create_account(lamports: u64) -> RefCell<account::Account> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function for working with keyed accounts in tests
|
/// Convenience function for working with keyed accounts in tests
|
||||||
#[cfg(feature = "everything")]
|
|
||||||
pub fn with_test_keyed_account<F>(lamports: u64, signer: bool, f: F)
|
pub fn with_test_keyed_account<F>(lamports: u64, signer: bool, f: F)
|
||||||
where
|
where
|
||||||
F: Fn(&KeyedAccount),
|
F: Fn(&KeyedAccount),
|
||||||
{
|
{
|
||||||
let pubkey = solana_sdk::pubkey::new_rand();
|
let pubkey = Pubkey::new_unique();
|
||||||
let account = create_account(lamports);
|
let account = create_account(lamports);
|
||||||
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);
|
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);
|
||||||
f(&keyed_account)
|
f(&keyed_account)
|
|
@ -1,4 +1,4 @@
|
||||||
use solana_sdk::{
|
use crate::{
|
||||||
account::Account,
|
account::Account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
|
@ -26,7 +26,7 @@ pub fn fee_calculator_of(account: &Account) -> Option<FeeCalculator> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::{
|
use crate::{
|
||||||
account_utils::State as AccountUtilsState,
|
account_utils::State as AccountUtilsState,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
nonce::{account::with_test_keyed_account, Account as NonceAccount, State},
|
nonce::{account::with_test_keyed_account, Account as NonceAccount, State},
|
|
@ -1,5 +1,3 @@
|
||||||
#![cfg(feature = "program")]
|
|
||||||
|
|
||||||
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
|
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
|
||||||
|
|
||||||
/// Invoke a cross-program instruction
|
/// Invoke a cross-program instruction
|
|
@ -1,13 +1,9 @@
|
||||||
|
use crate::info;
|
||||||
use crate::{decode_error::DecodeError, instruction::InstructionError, pubkey::PubkeyError};
|
use crate::{decode_error::DecodeError, instruction::InstructionError, pubkey::PubkeyError};
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[cfg(feature = "program")]
|
|
||||||
use crate::info;
|
|
||||||
#[cfg(all(feature = "everything", not(feature = "program")))]
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
/// Reasons the program may fail
|
/// Reasons the program may fail
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
|
||||||
pub enum ProgramError {
|
pub enum ProgramError {
|
|
@ -1,9 +1,8 @@
|
||||||
//! @brief Syscall stubs when building for programs for non-BPF targets
|
//! @brief Syscall stubs when building for programs for non-BPF targets
|
||||||
|
|
||||||
use crate::{
|
#![cfg(not(target_arch = "bpf"))]
|
||||||
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
|
|
||||||
program_error::ProgramError,
|
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
|
||||||
};
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
|
@ -27,7 +26,7 @@ pub trait SyscallStubs: Sync + Send {
|
||||||
_signers_seeds: &[&[&[u8]]],
|
_signers_seeds: &[&[&[u8]]],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
||||||
Err(ProgramError::InvalidArgument)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<T> DecodeError<T> for PubkeyError {
|
||||||
|
fn type_of() -> &'static str {
|
||||||
|
"PubkeyError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u64> 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<T> DecodeError<T> for ParsePubkeyError {
|
||||||
|
fn type_of() -> &'static str {
|
||||||
|
"ParsePubkeyError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Pubkey {
|
||||||
|
type Err = ParsePubkeyError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let pubkey_vec = bs58::decode(s)
|
||||||
|
.into_vec()
|
||||||
|
.map_err(|_| ParsePubkeyError::Invalid)?;
|
||||||
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
||||||
|
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<Pubkey, PubkeyError> {
|
||||||
|
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<Pubkey, PubkeyError> {
|
||||||
|
// 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::<Pubkey>(), Ok(pubkey));
|
||||||
|
|
||||||
|
pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
|
||||||
|
assert_eq!(
|
||||||
|
pubkey_base58_str.parse::<Pubkey>(),
|
||||||
|
Err(ParsePubkeyError::WrongSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
||||||
|
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
||||||
|
|
||||||
|
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
||||||
|
assert_eq!(
|
||||||
|
pubkey_base58_str.parse::<Pubkey>(),
|
||||||
|
Err(ParsePubkeyError::WrongSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
|
||||||
|
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
||||||
|
|
||||||
|
// throw some non-base58 stuff in there
|
||||||
|
pubkey_base58_str.replace_range(..1, "I");
|
||||||
|
assert_eq!(
|
||||||
|
pubkey_base58_str.parse::<Pubkey>(),
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,8 +133,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn show_rent_model() {
|
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 =
|
const SLOTS_PER_YEAR: f64 =
|
||||||
SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64);
|
SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
crate::declare_id!("KeccakSecp256k11111111111111111111111111111");
|
|
@ -491,9 +491,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_move_many() {
|
fn test_move_many() {
|
||||||
let alice_pubkey = solana_sdk::pubkey::new_rand();
|
let alice_pubkey = Pubkey::new_unique();
|
||||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_unique();
|
||||||
let carol_pubkey = solana_sdk::pubkey::new_rand();
|
let carol_pubkey = Pubkey::new_unique();
|
||||||
let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)];
|
let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)];
|
||||||
|
|
||||||
let instructions = transfer_many(&alice_pubkey, &to_lamports);
|
let instructions = transfer_many(&alice_pubkey, &to_lamports);
|
||||||
|
@ -504,8 +504,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_nonce_account() {
|
fn test_create_nonce_account() {
|
||||||
let from_pubkey = solana_sdk::pubkey::new_rand();
|
let from_pubkey = Pubkey::new_unique();
|
||||||
let nonce_pubkey = solana_sdk::pubkey::new_rand();
|
let nonce_pubkey = Pubkey::new_unique();
|
||||||
let authorized = nonce_pubkey;
|
let authorized = nonce_pubkey;
|
||||||
let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42);
|
let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42);
|
||||||
assert_eq!(ixs.len(), 2);
|
assert_eq!(ixs.len(), 2);
|
|
@ -1,9 +1,6 @@
|
||||||
//! This account contains the serialized transaction instructions
|
//! This account contains the serialized transaction instructions
|
||||||
//!
|
|
||||||
|
|
||||||
use crate::instruction::Instruction;
|
use crate::{instruction::Instruction, sanitize::SanitizeError, sysvar::Sysvar};
|
||||||
use crate::sanitize::SanitizeError;
|
|
||||||
use crate::sysvar::Sysvar;
|
|
||||||
|
|
||||||
pub type Instructions = Vec<Instruction>;
|
pub type Instructions = Vec<Instruction>;
|
||||||
|
|
||||||
|
@ -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<Instruction, SanitizeError> {
|
pub fn load_instruction_at(index: usize, data: &[u8]) -> Result<Instruction, SanitizeError> {
|
||||||
solana_sdk::message::Message::deserialize_instruction(index, data)
|
crate::message::Message::deserialize_instruction(index, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
Binary file not shown.
|
@ -7,6 +7,8 @@
|
||||||
//! Asynchronous implementations are expected to create transactions, sign them, and send
|
//! Asynchronous implementations are expected to create transactions, sign them, and send
|
||||||
//! them but without waiting to see if the server accepted it.
|
//! them but without waiting to see if the server accepted it.
|
||||||
|
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account::Account,
|
account::Account,
|
||||||
clock::Slot,
|
clock::Slot,
|
||||||
|
|
|
@ -1,48 +1,10 @@
|
||||||
//! @brief Solana Rust-based BPF program entry point supported by the latest
|
pub use solana_program_sdk::entrypoint::*;
|
||||||
//! 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_export]
|
||||||
|
#[deprecated(
|
||||||
|
since = "1.4.3",
|
||||||
|
note = "use solana_program_sdk::entrypoint::entrypoint instead"
|
||||||
|
)]
|
||||||
macro_rules! entrypoint {
|
macro_rules! entrypoint {
|
||||||
($process_instruction:ident) => {
|
($process_instruction:ident) => {
|
||||||
#[cfg(all(not(feature = "custom-heap"), not(test)))]
|
#[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<AccountInfo<'a>>, &'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::<u64>();
|
|
||||||
|
|
||||||
// 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::<u8>();
|
|
||||||
if dup_info == std::u8::MAX {
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let is_signer = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let is_writable = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let executable = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
offset += size_of::<u32>(); // padding to u64
|
|
||||||
|
|
||||||
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
|
||||||
offset += size_of::<Pubkey>();
|
|
||||||
|
|
||||||
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
|
||||||
offset += size_of::<Pubkey>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let data_len = *(input.add(offset) as *const u64) as usize;
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
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::<u128>()); // padding
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let rent_epoch = *(input.add(offset) as *const u64);
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
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::<u64>();
|
|
||||||
|
|
||||||
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::<u8>()).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::<u8>()).unwrap()) };
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u8>()));
|
|
||||||
let ptr =
|
|
||||||
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap()) };
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u16>()));
|
|
||||||
let ptr =
|
|
||||||
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap()) };
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u32>()));
|
|
||||||
let ptr =
|
|
||||||
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap()) };
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u64>()));
|
|
||||||
let ptr =
|
|
||||||
unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap()) };
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u128>()));
|
|
||||||
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::<u8>()).unwrap()) };
|
|
||||||
assert_ne!(ptr, null_mut());
|
|
||||||
assert_eq!(0, ptr.align_offset(size_of::<u64>()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,39 +1,10 @@
|
||||||
//! @brief Solana Rust-based BPF program entry point supported by the original
|
pub use solana_program_sdk::entrypoint_deprecated::*;
|
||||||
//! 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_export]
|
||||||
|
#[deprecated(
|
||||||
|
since = "1.4.3",
|
||||||
|
note = "use solana_program_sdk::entrypoint::entrypoint instead"
|
||||||
|
)]
|
||||||
macro_rules! entrypoint_deprecated {
|
macro_rules! entrypoint_deprecated {
|
||||||
($process_instruction:ident) => {
|
($process_instruction:ident) => {
|
||||||
/// # Safety
|
/// # 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<AccountInfo<'a>>, &'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::<u64>();
|
|
||||||
|
|
||||||
// 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::<u8>();
|
|
||||||
if dup_info == std::u8::MAX {
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let is_signer = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let is_writable = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
|
||||||
offset += size_of::<Pubkey>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let data_len = *(input.add(offset) as *const u64) as usize;
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
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::<Pubkey>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let executable = *(input.add(offset) as *const u8) != 0;
|
|
||||||
offset += size_of::<u8>();
|
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let rent_epoch = *(input.add(offset) as *const u64);
|
|
||||||
offset += size_of::<u64>();
|
|
||||||
|
|
||||||
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::<u64>();
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! The `genesis_config` module is a library for generating the chain's genesis config.
|
//! The `genesis_config` module is a library for generating the chain's genesis config.
|
||||||
|
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account::Account,
|
account::Account,
|
||||||
clock::{UnixTimestamp, DEFAULT_TICKS_PER_SLOT},
|
clock::{UnixTimestamp, DEFAULT_TICKS_PER_SLOT},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//! The `hard_forks` module is used to maintain the list of slot boundaries for when a hard fork
|
//! The `hard_forks` module is used to maintain the list of slot boundaries for when a hard fork
|
||||||
//! should occur.
|
//! should occur.
|
||||||
|
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use solana_sdk::clock::Slot;
|
use solana_sdk::clock::Slot;
|
||||||
|
|
||||||
|
|
183
sdk/src/hash.rs
183
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;
|
/// random hash value for tests and benchmarks.
|
||||||
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<Self, Self::Err> {
|
|
||||||
let bytes = bs58::decode(s)
|
|
||||||
.into_vec()
|
|
||||||
.map_err(|_| ParseHashError::Invalid)?;
|
|
||||||
if bytes.len() != mem::size_of::<Hash>() {
|
|
||||||
Err(ParseHashError::WrongSize)
|
|
||||||
} else {
|
|
||||||
Ok(Hash::new(&bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New random hash value for tests and benchmarks.
|
|
||||||
#[cfg(feature = "everything")]
|
#[cfg(feature = "everything")]
|
||||||
pub fn new_rand<R: ?Sized>(rng: &mut R) -> Hash
|
pub fn new_rand<R: ?Sized>(rng: &mut R) -> Hash
|
||||||
where
|
where
|
||||||
|
@ -86,106 +10,3 @@ where
|
||||||
rng.fill(&mut buf);
|
rng.fill(&mut buf);
|
||||||
Hash::new(&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<R: ?Sized>(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::<Hash>(), Ok(hash));
|
|
||||||
|
|
||||||
hash_base58_str.push_str(&bs58::encode(hash.0).into_string());
|
|
||||||
assert_eq!(
|
|
||||||
hash_base58_str.parse::<Hash>(),
|
|
||||||
Err(ParseHashError::WrongSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
hash_base58_str.truncate(hash_base58_str.len() / 2);
|
|
||||||
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
|
|
||||||
|
|
||||||
hash_base58_str.truncate(hash_base58_str.len() / 2);
|
|
||||||
assert_eq!(
|
|
||||||
hash_base58_str.parse::<Hash>(),
|
|
||||||
Err(ParseHashError::WrongSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut hash_base58_str = bs58::encode(hash.0).into_string();
|
|
||||||
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
|
|
||||||
|
|
||||||
// throw some non-base58 stuff in there
|
|
||||||
hash_base58_str.replace_range(..1, "I");
|
|
||||||
assert_eq!(
|
|
||||||
hash_base58_str.parse::<Hash>(),
|
|
||||||
Err(ParseHashError::Invalid)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,47 +4,36 @@
|
||||||
// Allows macro expansion of `use ::solana_sdk::*` to work within this crate
|
// Allows macro expansion of `use ::solana_sdk::*` to work within this crate
|
||||||
extern crate self as solana_sdk;
|
extern crate self as solana_sdk;
|
||||||
|
|
||||||
pub mod account;
|
pub use solana_program_sdk::*;
|
||||||
pub mod account_utils;
|
|
||||||
pub mod bpf_loader;
|
|
||||||
pub mod bpf_loader_deprecated;
|
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
pub mod clock;
|
pub mod client;
|
||||||
pub mod commitment_config;
|
pub mod commitment_config;
|
||||||
pub mod decode_error;
|
|
||||||
pub mod deserialize_utils;
|
pub mod deserialize_utils;
|
||||||
|
pub mod entrypoint;
|
||||||
|
pub mod entrypoint_deprecated;
|
||||||
pub mod entrypoint_native;
|
pub mod entrypoint_native;
|
||||||
pub mod epoch_info;
|
pub mod epoch_info;
|
||||||
pub mod epoch_schedule;
|
pub mod genesis_config;
|
||||||
pub mod fee_calculator;
|
pub mod hard_forks;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod incinerator;
|
|
||||||
pub mod inflation;
|
pub mod inflation;
|
||||||
pub mod instruction;
|
pub mod log;
|
||||||
pub mod loader_instruction;
|
|
||||||
pub mod message;
|
|
||||||
pub mod native_loader;
|
pub mod native_loader;
|
||||||
pub mod native_token;
|
|
||||||
pub mod nonce;
|
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod poh_config;
|
pub mod poh_config;
|
||||||
pub mod program_option;
|
|
||||||
pub mod program_pack;
|
|
||||||
pub mod program_utils;
|
pub mod program_utils;
|
||||||
pub mod pubkey;
|
pub mod pubkey;
|
||||||
pub mod rent;
|
|
||||||
pub mod rpc_port;
|
pub mod rpc_port;
|
||||||
pub mod sanitize;
|
pub mod secp256k1;
|
||||||
pub mod secp256k1_program;
|
pub mod shred_version;
|
||||||
pub mod short_vec;
|
pub mod signature;
|
||||||
pub mod slot_hashes;
|
pub mod signers;
|
||||||
pub mod slot_history;
|
|
||||||
pub mod stake_history;
|
|
||||||
pub mod stake_weighted_timestamp;
|
pub mod stake_weighted_timestamp;
|
||||||
pub mod system_instruction;
|
pub mod system_transaction;
|
||||||
pub mod system_program;
|
|
||||||
pub mod sysvar;
|
|
||||||
pub mod timing;
|
pub mod timing;
|
||||||
|
pub mod transaction;
|
||||||
|
pub mod transport;
|
||||||
|
|
||||||
/// Convenience macro to declare a static public key and functions to interact with it
|
/// 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)]
|
#[rustversion::since(1.46.0)]
|
||||||
pub use solana_sdk_macro::respan;
|
pub use solana_sdk_macro::respan;
|
||||||
|
|
||||||
// On-chain program specific modules
|
// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with older programs
|
||||||
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
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "1.4.2",
|
since = "1.4.3",
|
||||||
note = "program_stubs macro is obsolete and can be safely removed"
|
note = "program_stubs macro is obsolete and can be safely removed"
|
||||||
)]
|
)]
|
||||||
macro_rules! program_stubs {
|
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]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
pub extern crate bs58;
|
pub extern crate bs58;
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
//! @brief Solana Rust-based BPF program logging
|
|
||||||
|
|
||||||
#![cfg(feature = "program")]
|
#![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]
|
#[macro_export]
|
||||||
|
#[deprecated(since = "1.4.3", note = "solana_program_sdk::log::info instead")]
|
||||||
macro_rules! info {
|
macro_rules! info {
|
||||||
($msg:expr) => {
|
($msg:expr) => {
|
||||||
$crate::log::sol_log($msg)
|
$crate::log::sol_log($msg)
|
||||||
|
@ -21,80 +16,5 @@ macro_rules! info {
|
||||||
$arg4 as u64,
|
$arg4 as u64,
|
||||||
$arg5 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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,4 @@
|
||||||
use crate::{decode_error::DecodeError, hash::hashv};
|
pub use solana_program_sdk::pubkey::*;
|
||||||
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<T> DecodeError<T> for PubkeyError {
|
|
||||||
fn type_of() -> &'static str {
|
|
||||||
"PubkeyError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<u64> 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<T> DecodeError<T> for ParsePubkeyError {
|
|
||||||
fn type_of() -> &'static str {
|
|
||||||
"ParsePubkeyError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Pubkey {
|
|
||||||
type Err = ParsePubkeyError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let pubkey_vec = bs58::decode(s)
|
|
||||||
.into_vec()
|
|
||||||
.map_err(|_| ParsePubkeyError::Invalid)?;
|
|
||||||
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
||||||
Err(ParsePubkeyError::WrongSize)
|
|
||||||
} else {
|
|
||||||
Ok(Pubkey::new(&pubkey_vec))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New random Pubkey for tests and benchmarks.
|
/// New random Pubkey for tests and benchmarks.
|
||||||
#[cfg(feature = "everything")]
|
#[cfg(feature = "everything")]
|
||||||
|
@ -73,171 +6,6 @@ pub fn new_rand() -> Pubkey {
|
||||||
Pubkey::new(&rand::random::<[u8; 32]>())
|
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<Pubkey, PubkeyError> {
|
|
||||||
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<Pubkey, PubkeyError> {
|
|
||||||
// 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")]
|
#[cfg(feature = "everything")]
|
||||||
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -258,192 +26,15 @@ pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn st
|
||||||
pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn std::error::Error>> {
|
pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn std::error::Error>> {
|
||||||
let f = std::fs::File::open(infile.to_string())?;
|
let f = std::fs::File::open(infile.to_string())?;
|
||||||
let printable: String = serde_json::from_reader(f)?;
|
let printable: String = serde_json::from_reader(f)?;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
Ok(Pubkey::from_str(&printable)?)
|
Ok(Pubkey::from_str(&printable)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::{fs::remove_file, str::from_utf8};
|
use std::fs::remove_file;
|
||||||
|
|
||||||
#[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::<Pubkey>(), Ok(pubkey));
|
|
||||||
|
|
||||||
pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string());
|
|
||||||
assert_eq!(
|
|
||||||
pubkey_base58_str.parse::<Pubkey>(),
|
|
||||||
Err(ParsePubkeyError::WrongSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
|
||||||
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
|
||||||
|
|
||||||
pubkey_base58_str.truncate(pubkey_base58_str.len() / 2);
|
|
||||||
assert_eq!(
|
|
||||||
pubkey_base58_str.parse::<Pubkey>(),
|
|
||||||
Err(ParsePubkeyError::WrongSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
|
|
||||||
assert_eq!(pubkey_base58_str.parse::<Pubkey>(), Ok(pubkey));
|
|
||||||
|
|
||||||
// throw some non-base58 stuff in there
|
|
||||||
pubkey_base58_str.replace_range(..1, "I");
|
|
||||||
assert_eq!(
|
|
||||||
pubkey_base58_str.parse::<Pubkey>(),
|
|
||||||
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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_write_pubkey() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_read_write_pubkey() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
solana_sdk::declare_id!("KeccakSecp256k11111111111111111111111111111");
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hard_forks::HardForks,
|
hard_forks::HardForks,
|
||||||
hash::{extend_and_hash, Hash},
|
hash::{extend_and_hash, Hash},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! The `signature` module provides functionality for public, and private keys.
|
//! The `signature` module provides functionality for public, and private keys.
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::{pubkey::Pubkey, transaction::TransactionError};
|
use crate::{pubkey::Pubkey, transaction::TransactionError};
|
||||||
use ed25519_dalek::Signer as DalekSigner;
|
use ed25519_dalek::Signer as DalekSigner;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
use crate::{
|
use crate::{
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Signature, Signer, SignerError},
|
signature::{Signature, Signer, SignerError},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! The `system_transaction` module provides functionality for creating system transactions.
|
//! The `system_transaction` module provides functionality for creating system transactions.
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Defines a Transaction type to package an atomic sequence of instructions.
|
//! Defines a Transaction type to package an atomic sequence of instructions.
|
||||||
|
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::sanitize::{Sanitize, SanitizeError};
|
use crate::sanitize::{Sanitize, SanitizeError};
|
||||||
use crate::secp256k1::verify_eth_addresses;
|
use crate::secp256k1::verify_eth_addresses;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![cfg(feature = "everything")]
|
||||||
|
|
||||||
use crate::transaction::TransactionError;
|
use crate::transaction::TransactionError;
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
Loading…
Reference in New Issue