Initial population of solana-program-sdk

This commit is contained in:
Michael Vines 2020-10-19 13:19:24 -07:00
parent 3718771ffb
commit 63db324204
79 changed files with 1474 additions and 1220 deletions

32
Cargo.lock generated
View File

@ -4379,6 +4379,36 @@ dependencies = [
"solana-version",
]
[[package]]
name = "solana-program-sdk"
version = "1.5.0"
dependencies = [
"assert_matches",
"bincode",
"bs58",
"bv",
"curve25519-dalek 2.1.0",
"hex",
"itertools 0.9.0",
"lazy_static",
"log 0.4.8",
"num-derive",
"num-traits",
"rand 0.7.3",
"rustc_version",
"rustversion",
"serde",
"serde_bytes",
"serde_derive",
"serde_json",
"sha2",
"solana-frozen-abi",
"solana-frozen-abi-macro",
"solana-logger 1.5.0",
"solana-sdk-macro 1.5.0",
"thiserror",
]
[[package]]
name = "solana-ramp-tps"
version = "1.5.0"
@ -4545,7 +4575,6 @@ dependencies = [
"hex",
"hmac",
"itertools 0.9.0",
"lazy_static",
"libsecp256k1",
"log 0.4.8",
"memmap",
@ -4566,6 +4595,7 @@ dependencies = [
"solana-frozen-abi",
"solana-frozen-abi-macro",
"solana-logger 1.5.0",
"solana-program-sdk",
"solana-sdk-macro 1.5.0",
"thiserror",
"tiny-bip39",

View File

@ -27,8 +27,8 @@ declare print_free_tree=(
':sdk/bpf/rust/rust-utils/**.rs'
':sdk/**.rs'
':^sdk/cargo-build-bpf/**.rs'
':^sdk/src/program_option.rs'
':^sdk/src/program_stubs.rs'
':^sdk/program/src/program_option.rs'
':^sdk/program/src/program_stubs.rs'
':programs/**.rs'
':^**bin**.rs'
':^**bench**.rs'

View File

@ -40,6 +40,9 @@ test-stable)
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
;;
test-stable-perf)
# BPF solana-sdk legacy compile test
./cargo-build-bpf --manifest-path sdk/Cargo.toml --no-default-features --features program
# BPF program tests
_ make -C programs/bpf/c tests
_ cargo +"$rust_stable" test \

View File

@ -2110,6 +2110,32 @@ dependencies = [
"solana-sdk",
]
[[package]]
name = "solana-program-sdk"
version = "1.5.0"
dependencies = [
"bincode",
"bs58",
"curve25519-dalek 2.1.0",
"hex",
"lazy_static",
"log",
"num-derive 0.3.0",
"num-traits",
"rand",
"rustc_version",
"rustversion",
"serde",
"serde_bytes",
"serde_derive",
"sha2",
"solana-frozen-abi",
"solana-frozen-abi-macro",
"solana-logger",
"solana-sdk-macro",
"thiserror",
]
[[package]]
name = "solana-rayon-threadlimit"
version = "1.5.0"
@ -2177,14 +2203,12 @@ dependencies = [
"bv",
"byteorder 1.3.4",
"chrono",
"curve25519-dalek 2.1.0",
"digest 0.9.0",
"ed25519-dalek",
"generic-array 0.14.3",
"hex",
"hmac",
"itertools",
"lazy_static",
"libsecp256k1",
"log",
"memmap",
@ -2205,6 +2229,7 @@ dependencies = [
"solana-frozen-abi",
"solana-frozen-abi-macro",
"solana-logger",
"solana-program-sdk",
"solana-sdk-macro",
"thiserror",
]

View File

@ -9,9 +9,12 @@ license = "Apache-2.0"
edition = "2018"
[features]
# On-chain program specific dependencies
# "program" feature is a legacy feature retained to support v1.3 and older
# programs. New development should not use this feature. Instead use the
# solana-program-sdk crate
program = []
# Everything includes functionality that is not compatible or needed for on-chain programs
# "everything" includes functionality that is not compatible or needed for on-chain programs
default = [
"everything"
]
@ -43,12 +46,11 @@ curve25519-dalek = { version = "2.1.0", optional = true }
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
hex = "0.4.2"
hmac = "0.7.0"
itertools = { version = "0.9.0" }
lazy_static = "1.4.0"
log = { version = "0.4.8" }
itertools = "0.9.0"
log = "0.4.8"
memmap = { version = "0.7.0", optional = true }
num-derive = { version = "0.3" }
num-traits = { version = "0.2" }
num-derive = "0.3"
num-traits = "0.2"
pbkdf2 = { version = "0.3.0", default-features = false }
rand = { version = "0.7.0", optional = true }
rand_chacha = { version = "0.2.2", optional = true }
@ -70,15 +72,12 @@ libsecp256k1 = { version = "0.3.5", optional = true }
sha3 = { version = "0.9.1", optional = true }
digest = { version = "0.9.0", optional = true }
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
curve25519-dalek = { version = "2.1.0" }
[dev-dependencies]
curve25519-dalek = "2.1.0"
tiny-bip39 = "0.7.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[build-dependencies]
rustc_version = "0.2"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

2
sdk/Xargo.toml Normal file
View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -17,49 +17,81 @@ use syn::{
Expr, Ident, LitByte, LitStr, Path, Token,
};
struct Id(proc_macro2::TokenStream);
impl Parse for Id {
fn parse(input: ParseStream) -> Result<Self> {
let token_stream = if input.peek(syn::LitStr) {
let id_literal: LitStr = input.parse()?;
parse_pubkey(&id_literal)?
} else {
let expr: Expr = input.parse()?;
quote! { #expr }
};
fn parse_id(
input: ParseStream,
pubkey_type: proc_macro2::TokenStream,
) -> Result<proc_macro2::TokenStream> {
let id = if input.peek(syn::LitStr) {
let id_literal: LitStr = input.parse()?;
parse_pubkey(&id_literal, &pubkey_type)?
} else {
let expr: Expr = input.parse()?;
quote! { #expr }
};
if !input.is_empty() {
let stream: proc_macro2::TokenStream = input.parse()?;
return Err(syn::Error::new_spanned(stream, "unexpected token"));
if !input.is_empty() {
let stream: proc_macro2::TokenStream = input.parse()?;
return Err(syn::Error::new_spanned(stream, "unexpected token"));
}
Ok(id)
}
fn id_to_tokens(
id: &proc_macro2::TokenStream,
pubkey_type: proc_macro2::TokenStream,
tokens: &mut proc_macro2::TokenStream,
) {
tokens.extend(quote! {
/// The static program ID
pub static ID: #pubkey_type = #id;
/// Confirms that a given pubkey is equivalent to the program ID
pub fn check_id(id: &#pubkey_type) -> bool {
id == &ID
}
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 {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let id = &self.0;
tokens.extend(quote! {
/// The static program ID
pub static ID: ::solana_sdk::pubkey::Pubkey = #id;
id_to_tokens(&self.0, quote! { ::solana_sdk::pubkey::Pubkey }, tokens)
}
}
/// Confirms that a given pubkey is equivalent to the program ID
pub fn check_id(id: &::solana_sdk::pubkey::Pubkey) -> bool {
id == &ID
}
struct ProgramSdkId(proc_macro2::TokenStream);
/// Returns the program ID
pub fn id() -> ::solana_sdk::pubkey::Pubkey {
ID
}
impl Parse for ProgramSdkId {
fn parse(input: ParseStream) -> Result<Self> {
parse_id(input, quote! { ::solana_program_sdk::pubkey::Pubkey }).map(Self)
}
}
#[cfg(test)]
#[test]
fn test_id() {
assert!(check_id(&id()));
}
});
impl ToTokens for ProgramSdkId {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
id_to_tokens(
&self.0,
quote! { ::solana_program_sdk::pubkey::Pubkey },
tokens,
)
}
}
@ -135,7 +167,16 @@ pub fn declare_id(input: TokenStream) -> TokenStream {
TokenStream::from(quote! {#id})
}
fn parse_pubkey(id_literal: &LitStr) -> Result<proc_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())
.into_vec()
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
@ -147,7 +188,7 @@ fn parse_pubkey(id_literal: &LitStr) -> Result<proc_macro2::TokenStream> {
})?;
let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
Ok(quote! {
::solana_sdk::pubkey::Pubkey::new_from_array(
#pubkey_type::new_from_array(
[#(#bytes,)*]
)
})
@ -160,11 +201,15 @@ struct Pubkeys {
}
impl Parse for Pubkeys {
fn parse(input: ParseStream) -> Result<Self> {
let pubkey_type = quote! {
::solana_sdk::pubkey::Pubkey
};
let method = input.parse()?;
let _comma: Token![,] = input.parse()?;
let (num, pubkeys) = if input.peek(syn::LitStr) {
let id_literal: LitStr = input.parse()?;
(1, parse_pubkey(&id_literal)?)
(1, parse_pubkey(&id_literal, &pubkey_type)?)
} else if input.peek(Bracket) {
let pubkey_strings;
bracketed!(pubkey_strings in input);
@ -172,7 +217,7 @@ impl Parse for Pubkeys {
Punctuated::parse_terminated(&pubkey_strings)?;
let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
for string in punctuated.iter() {
pubkeys.push(parse_pubkey(string)?);
pubkeys.push(parse_pubkey(string, &pubkey_type)?);
}
(pubkeys.len(), quote! {#pubkeys})
} else {
@ -195,15 +240,19 @@ impl ToTokens for Pubkeys {
num,
pubkeys,
} = self;
let pubkey_type = quote! {
::solana_sdk::pubkey::Pubkey
};
if *num == 1 {
tokens.extend(quote! {
pub fn #method() -> ::solana_sdk::pubkey::Pubkey {
pub fn #method() -> #pubkey_type {
#pubkeys
}
});
} else {
tokens.extend(quote! {
pub fn #method() -> ::std::vec::Vec<::solana_sdk::pubkey::Pubkey> {
pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
vec![#pubkeys]
}
});

View File

@ -9,8 +9,37 @@ license = "Apache-2.0"
edition = "2018"
[dependencies]
bincode = "1.3.1"
bs58 = "0.3.1"
bv = { version = "0.11.1", features = ["serde"] }
hex = "0.4.2"
itertools = "0.9.0"
lazy_static = "1.4.0"
log = "0.4.8"
num-derive = "0.3"
num-traits = "0.2"
rustversion = "1.0.3"
serde = "1.0.112"
serde_bytes = "0.11"
serde_derive = "1.0.103"
sha2 = "0.8.2"
solana-frozen-abi = { path = "../../frozen-abi", version = "1.5.0" }
solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "1.5.0" }
solana-sdk-macro = { path = "../macro", version = "1.5.0" }
thiserror = "1.0"
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
curve25519-dalek = { version = "2.1.0" }
rand = "0.7.0"
solana-logger = { path = "../../logger", version = "1.5.0" }
[dev-dependencies]
assert_matches = "1.3.0"
bincode = "1.3.1"
serde_json = "1.0.56"
[build-dependencies]
rustc_version = "0.2"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

2
sdk/program/Xargo.toml Normal file
View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

1
sdk/program/build.rs Symbolic link
View File

@ -0,0 +1 @@
../../frozen-abi/build.rs

View File

@ -8,7 +8,7 @@ use std::{
/// An Account with data that is stored on chain
#[repr(C)]
#[frozen_abi(digest = "By9FhuLAM947tkLxbTVQru9ZKTrRQuvCR5W387nPSLNu")]
#[frozen_abi(digest = "AVG6bXqVUippoYRAE8e1ewHeZi1UvFx5r5gKspFSEbxM")]
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Default, AbiExample)]
#[serde(rename_all = "camelCase")]
pub struct Account {

View File

@ -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>()));
}
}
}

View File

@ -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)
}

186
sdk/program/src/hash.rs Normal file
View File

@ -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)
);
}
}

View File

@ -1,2 +1,71 @@
#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))]
#![cfg_attr(RUSTC_NEEDS_PROC_MACRO_HYGIENE, feature(proc_macro_hygiene))]
// Allows macro expansion of `use ::solana_program_sdk::*` to work within this crate
extern crate self as solana_program_sdk;
pub mod account;
pub mod account_info;
pub mod account_utils;
pub mod bpf_loader;
pub mod bpf_loader_deprecated;
pub mod clock;
pub mod decode_error;
pub mod entrypoint;
pub mod entrypoint_deprecated;
pub mod epoch_schedule;
pub mod fee_calculator;
pub mod hash;
pub mod incinerator;
pub mod instruction;
pub mod loader_instruction;
pub mod log;
pub mod message;
pub mod native_token;
pub mod nonce;
pub mod program;
pub mod program_error;
pub mod program_option;
pub mod program_pack;
pub mod program_stubs;
pub mod pubkey;
pub mod rent;
pub mod sanitize;
pub mod secp256k1_program;
pub mod serialize_utils;
pub mod short_vec;
pub mod slot_hashes;
pub mod slot_history;
pub mod stake_history;
pub mod system_instruction;
pub mod system_program;
pub mod sysvar;
/// Convenience macro to declare a static public key and functions to interact with it
///
/// Input: a single literal base58 string representation of a program's id
///
/// # Example
///
/// ```
/// # // wrapper is used so that the macro invocation occurs in the item position
/// # // rather than in the statement position which isn't allowed.
/// use std::str::FromStr;
/// use solana_program_sdk::{declare_id, pubkey::Pubkey};
///
/// # mod item_wrapper {
/// # use solana_program_sdk::declare_id;
/// declare_id!("My11111111111111111111111111111111111111111");
/// # }
/// # use item_wrapper::id;
///
/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
/// assert_eq!(id(), my_id);
/// ```
pub use solana_sdk_macro::program_sdk_declare_id as declare_id;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate solana_frozen_abi_macro;

98
sdk/program/src/log.rs Normal file
View File

@ -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);
}

View File

@ -426,10 +426,7 @@ impl Message {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
instruction::AccountMeta,
signature::{Keypair, Signer},
};
use crate::instruction::AccountMeta;
#[test]
fn test_message_unique_program_ids() {
@ -444,7 +441,7 @@ mod tests {
#[test]
fn test_message_unique_program_ids_not_adjacent() {
let program_id0 = Pubkey::default();
let program_id1 = solana_sdk::pubkey::new_rand();
let program_id1 = Pubkey::new_unique();
let program_ids = get_program_ids(&[
Instruction::new(program_id0, &0, vec![]),
Instruction::new(program_id1, &0, vec![]),
@ -455,7 +452,7 @@ mod tests {
#[test]
fn test_message_unique_program_ids_order_preserved() {
let program_id0 = solana_sdk::pubkey::new_rand();
let program_id0 = Pubkey::new_unique();
let program_id1 = Pubkey::default(); // Key less than program_id0
let program_ids = get_program_ids(&[
Instruction::new(program_id0, &0, vec![]),
@ -558,7 +555,7 @@ mod tests {
#[test]
fn test_message_unique_keys_order_preserved() {
let program_id = Pubkey::default();
let id0 = solana_sdk::pubkey::new_rand();
let id0 = Pubkey::new_unique();
let id1 = Pubkey::default(); // Key less than id0
let keys = get_keys(
&[
@ -574,7 +571,7 @@ mod tests {
fn test_message_unique_keys_not_adjacent() {
let program_id = Pubkey::default();
let id0 = Pubkey::default();
let id1 = solana_sdk::pubkey::new_rand();
let id1 = Pubkey::new_unique();
let keys = get_keys(
&[
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
@ -590,7 +587,7 @@ mod tests {
fn test_message_signed_keys_first() {
let program_id = Pubkey::default();
let id0 = Pubkey::default();
let id1 = solana_sdk::pubkey::new_rand();
let id1 = Pubkey::new_unique();
let keys = get_keys(
&[
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
@ -619,9 +616,9 @@ mod tests {
fn test_message_readonly_keys_last() {
let program_id = Pubkey::default();
let id0 = Pubkey::default(); // Identical key/program_id should be de-duped
let id1 = solana_sdk::pubkey::new_rand();
let id2 = solana_sdk::pubkey::new_rand();
let id3 = solana_sdk::pubkey::new_rand();
let id1 = Pubkey::new_unique();
let id2 = Pubkey::new_unique();
let id3 = Pubkey::new_unique();
let keys = get_keys(
&[
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
@ -639,11 +636,10 @@ mod tests {
#[test]
fn test_message_kitchen_sink() {
let program_id0 = solana_sdk::pubkey::new_rand();
let program_id1 = solana_sdk::pubkey::new_rand();
let program_id0 = Pubkey::new_unique();
let program_id1 = Pubkey::new_unique();
let id0 = Pubkey::default();
let keypair1 = Keypair::new();
let id1 = keypair1.pubkey();
let id1 = Pubkey::new_unique();
let message = Message::new(
&[
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
@ -669,7 +665,7 @@ mod tests {
#[test]
fn test_message_payer_first() {
let program_id = Pubkey::default();
let payer = solana_sdk::pubkey::new_rand();
let payer = Pubkey::new_unique();
let id0 = Pubkey::default();
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
@ -692,8 +688,8 @@ mod tests {
#[test]
fn test_message_program_last() {
let program_id = Pubkey::default();
let id0 = solana_sdk::pubkey::new_rand();
let id1 = solana_sdk::pubkey::new_rand();
let id0 = Pubkey::new_unique();
let id1 = Pubkey::new_unique();
let keys = get_keys(
&[
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id0, false)]),
@ -710,8 +706,8 @@ mod tests {
#[test]
fn test_program_position() {
let program_id0 = Pubkey::default();
let program_id1 = solana_sdk::pubkey::new_rand();
let id = solana_sdk::pubkey::new_rand();
let program_id1 = Pubkey::new_unique();
let id = Pubkey::new_unique();
let message = Message::new(
&[
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
@ -726,12 +722,12 @@ mod tests {
#[test]
fn test_is_writable() {
let key0 = solana_sdk::pubkey::new_rand();
let key1 = solana_sdk::pubkey::new_rand();
let key2 = solana_sdk::pubkey::new_rand();
let key3 = solana_sdk::pubkey::new_rand();
let key4 = solana_sdk::pubkey::new_rand();
let key5 = solana_sdk::pubkey::new_rand();
let key0 = Pubkey::new_unique();
let key1 = Pubkey::new_unique();
let key2 = Pubkey::new_unique();
let key3 = Pubkey::new_unique();
let key4 = Pubkey::new_unique();
let key5 = Pubkey::new_unique();
let message = Message {
header: MessageHeader {
@ -754,10 +750,10 @@ mod tests {
#[test]
fn test_get_account_keys_by_lock_type() {
let program_id = Pubkey::default();
let id0 = solana_sdk::pubkey::new_rand();
let id1 = solana_sdk::pubkey::new_rand();
let id2 = solana_sdk::pubkey::new_rand();
let id3 = solana_sdk::pubkey::new_rand();
let id0 = Pubkey::new_unique();
let id1 = Pubkey::new_unique();
let id2 = Pubkey::new_unique();
let id3 = Pubkey::new_unique();
let message = Message::new(
&[
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
@ -776,12 +772,12 @@ mod tests {
#[test]
fn test_decompile_instructions() {
solana_logger::setup();
let program_id0 = solana_sdk::pubkey::new_rand();
let program_id1 = solana_sdk::pubkey::new_rand();
let id0 = solana_sdk::pubkey::new_rand();
let id1 = solana_sdk::pubkey::new_rand();
let id2 = solana_sdk::pubkey::new_rand();
let id3 = solana_sdk::pubkey::new_rand();
let program_id0 = Pubkey::new_unique();
let program_id1 = Pubkey::new_unique();
let id0 = Pubkey::new_unique();
let id1 = Pubkey::new_unique();
let id2 = Pubkey::new_unique();
let id3 = Pubkey::new_unique();
let instructions = vec![
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, true)]),

View File

@ -170,12 +170,11 @@ pub fn create_account(lamports: u64) -> RefCell<account::Account> {
}
/// 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)
where
F: Fn(&KeyedAccount),
{
let pubkey = solana_sdk::pubkey::new_rand();
let pubkey = Pubkey::new_unique();
let account = create_account(lamports);
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);
f(&keyed_account)

View File

@ -1,4 +1,4 @@
use solana_sdk::{
use crate::{
account::Account,
account_utils::StateMut,
fee_calculator::FeeCalculator,
@ -26,7 +26,7 @@ pub fn fee_calculator_of(account: &Account) -> Option<FeeCalculator> {
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{
use crate::{
account_utils::State as AccountUtilsState,
hash::Hash,
nonce::{account::with_test_keyed_account, Account as NonceAccount, State},

View File

@ -1,5 +1,3 @@
#![cfg(feature = "program")]
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
/// Invoke a cross-program instruction

View File

@ -1,13 +1,9 @@
use crate::info;
use crate::{decode_error::DecodeError, instruction::InstructionError, pubkey::PubkeyError};
use num_traits::{FromPrimitive, ToPrimitive};
use std::convert::TryFrom;
use thiserror::Error;
#[cfg(feature = "program")]
use crate::info;
#[cfg(all(feature = "everything", not(feature = "program")))]
use log::info;
/// Reasons the program may fail
#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
pub enum ProgramError {

View File

@ -1,9 +1,8 @@
//! @brief Syscall stubs when building for programs for non-BPF targets
use crate::{
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
program_error::ProgramError,
};
#![cfg(not(target_arch = "bpf"))]
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
use std::sync::{Arc, RwLock};
lazy_static::lazy_static! {
@ -27,7 +26,7 @@ pub trait SyscallStubs: Sync + Send {
_signers_seeds: &[&[&[u8]]],
) -> ProgramResult {
sol_log("SyscallStubs: sol_invoke_signed() not available");
Err(ProgramError::InvalidArgument)
Ok(())
}
}

426
sdk/program/src/pubkey.rs Normal file
View File

@ -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()
);
}
}
}

View File

@ -133,8 +133,9 @@ mod tests {
#[test]
#[should_panic]
fn show_rent_model() {
use crate::{clock::*, sysvar::Sysvar, timing::*};
use crate::{clock::*, sysvar::Sysvar};
const SECONDS_PER_YEAR: f64 = 365.242_199 * 24.0 * 60.0 * 60.0;
const SLOTS_PER_YEAR: f64 =
SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64);

View File

@ -0,0 +1 @@
crate::declare_id!("KeccakSecp256k11111111111111111111111111111");

View File

@ -491,9 +491,9 @@ mod tests {
#[test]
fn test_move_many() {
let alice_pubkey = solana_sdk::pubkey::new_rand();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let carol_pubkey = solana_sdk::pubkey::new_rand();
let alice_pubkey = Pubkey::new_unique();
let bob_pubkey = Pubkey::new_unique();
let carol_pubkey = Pubkey::new_unique();
let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)];
let instructions = transfer_many(&alice_pubkey, &to_lamports);
@ -504,8 +504,8 @@ mod tests {
#[test]
fn test_create_nonce_account() {
let from_pubkey = solana_sdk::pubkey::new_rand();
let nonce_pubkey = solana_sdk::pubkey::new_rand();
let from_pubkey = Pubkey::new_unique();
let nonce_pubkey = Pubkey::new_unique();
let authorized = nonce_pubkey;
let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42);
assert_eq!(ixs.len(), 2);

View File

@ -1,9 +1,6 @@
//! This account contains the serialized transaction instructions
//!
use crate::instruction::Instruction;
use crate::sanitize::SanitizeError;
use crate::sysvar::Sysvar;
use crate::{instruction::Instruction, sanitize::SanitizeError, sysvar::Sysvar};
pub type Instructions = Vec<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> {
solana_sdk::message::Message::deserialize_instruction(index, data)
crate::message::Message::deserialize_instruction(index, data)
}
#[cfg(test)]

BIN
sdk/src/.lib.rs.swo Normal file

Binary file not shown.

View File

@ -7,6 +7,8 @@
//! Asynchronous implementations are expected to create transactions, sign them, and send
//! them but without waiting to see if the server accepted it.
#![cfg(feature = "everything")]
use crate::{
account::Account,
clock::Slot,

View File

@ -1,48 +1,10 @@
//! @brief Solana Rust-based BPF program entry point supported by the latest
//! BPFLoader. For more information see './bpf_loader.rs'
pub use solana_program_sdk::entrypoint::*;
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]
#[deprecated(
since = "1.4.3",
note = "use solana_program_sdk::entrypoint::entrypoint instead"
)]
macro_rules! entrypoint {
($process_instruction:ident) => {
#[cfg(all(not(feature = "custom-heap"), not(test)))]
@ -64,194 +26,3 @@ macro_rules! entrypoint {
}
};
}
/// The bump allocator used as the default rust heap when running programs.
pub struct BumpAllocator {
pub start: usize,
pub len: usize,
}
unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let pos_ptr = self.start as *mut usize;
let mut pos = *pos_ptr;
if pos == 0 {
// First time, set starting position
pos = self.start + self.len;
}
pos = pos.saturating_sub(layout.size());
pos &= !(layout.align().wrapping_sub(1));
if pos < self.start + size_of::<*mut u8>() {
return null_mut();
}
*pos_ptr = pos;
pos as *mut u8
}
#[inline]
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
// I'm a bump allocator, I don't free
}
}
/// Maximum number of bytes a program may add to an account during a single realloc
pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
/// Deserialize the input arguments
///
/// # Safety
#[allow(clippy::type_complexity)]
pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<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>()));
}
}
}

View File

@ -1,39 +1,10 @@
//! @brief Solana Rust-based BPF program entry point supported by the original
//! and now deprecated BPFLoader. For more information see
//! './bpf_loader_deprecated.rs'
pub use solana_program_sdk::entrypoint_deprecated::*;
extern crate alloc;
use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
use alloc::vec::Vec;
use std::{
cell::RefCell,
mem::size_of,
rc::Rc,
// Hide Result from bindgen gets confused about generics in non-generic type declarations
result::Result as ResultGeneric,
slice::{from_raw_parts, from_raw_parts_mut},
};
pub type ProgramResult = ResultGeneric<(), ProgramError>;
/// User implemented function to process an instruction
///
/// program_id: Program ID of the currently executing program
/// accounts: Accounts passed as part of the instruction
/// instruction_data: Instruction data
pub type ProcessInstruction =
fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult;
/// Programs indicate success with a return value of 0
pub const SUCCESS: u64 = 0;
/// Declare the entry point of the program.
///
/// Deserialize the program input arguments and call
/// the user defined `process_instruction` function.
/// Users must call this macro otherwise an entry point for
/// their program will not be created.
#[macro_export]
#[deprecated(
since = "1.4.3",
note = "use solana_program_sdk::entrypoint::entrypoint instead"
)]
macro_rules! entrypoint_deprecated {
($process_instruction:ident) => {
/// # Safety
@ -48,90 +19,3 @@ macro_rules! entrypoint_deprecated {
}
};
}
/// Deserialize the input arguments
///
/// # Safety
#[allow(clippy::type_complexity)]
pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<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)
}

View File

@ -1,5 +1,7 @@
//! The `genesis_config` module is a library for generating the chain's genesis config.
#![cfg(feature = "everything")]
use crate::{
account::Account,
clock::{UnixTimestamp, DEFAULT_TICKS_PER_SLOT},

View File

@ -1,6 +1,8 @@
//! The `hard_forks` module is used to maintain the list of slot boundaries for when a hard fork
//! should occur.
#![cfg(feature = "everything")]
use byteorder::{ByteOrder, LittleEndian};
use solana_sdk::clock::Slot;

View File

@ -1,82 +1,6 @@
//! The `hash` module provides functions for creating SHA-256 hashes.
pub use solana_program_sdk::hash::*;
use crate::sanitize::Sanitize;
use sha2::{Digest, Sha256};
use std::{convert::TryFrom, fmt, mem, str::FromStr};
use thiserror::Error;
pub const HASH_BYTES: usize = 32;
#[derive(
Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash, AbiExample,
)]
#[repr(transparent)]
pub struct Hash(pub [u8; HASH_BYTES]);
#[derive(Clone, Default)]
pub struct Hasher {
hasher: Sha256,
}
impl Hasher {
pub fn hash(&mut self, val: &[u8]) {
self.hasher.input(val);
}
pub fn hashv(&mut self, vals: &[&[u8]]) {
for val in vals {
self.hash(val);
}
}
pub fn result(self) -> Hash {
// At the time of this writing, the sha2 library is stuck on an old version
// of generic_array (0.9.0). Decouple ourselves with a clone to our version.
Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.result().as_slice()).unwrap())
}
}
impl Sanitize for Hash {}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", bs58::encode(self.0).into_string())
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", bs58::encode(self.0).into_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseHashError {
#[error("string decoded to wrong size for hash")]
WrongSize,
#[error("failed to decoded string to hash")]
Invalid,
}
impl FromStr for Hash {
type Err = ParseHashError;
fn from_str(s: &str) -> Result<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.
/// random hash value for tests and benchmarks.
#[cfg(feature = "everything")]
pub fn new_rand<R: ?Sized>(rng: &mut R) -> Hash
where
@ -86,106 +10,3 @@ where
rng.fill(&mut buf);
Hash::new(&buf)
}
impl Hash {
pub fn new(hash_slice: &[u8]) -> Self {
Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap())
}
pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self {
Self(hash_array)
}
pub fn to_bytes(self) -> [u8; HASH_BYTES] {
self.0
}
/// New random hash value for tests and benchmarks.
#[cfg(all(feature = "everything", not(target_arch = "bpf")))]
#[deprecated(since = "1.3.9", note = "Please use 'hash::new_rand' instead")]
pub fn new_rand<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)
);
}
}

View File

@ -4,47 +4,36 @@
// Allows macro expansion of `use ::solana_sdk::*` to work within this crate
extern crate self as solana_sdk;
pub mod account;
pub mod account_utils;
pub mod bpf_loader;
pub mod bpf_loader_deprecated;
pub use solana_program_sdk::*;
pub mod builtins;
pub mod clock;
pub mod client;
pub mod commitment_config;
pub mod decode_error;
pub mod deserialize_utils;
pub mod entrypoint;
pub mod entrypoint_deprecated;
pub mod entrypoint_native;
pub mod epoch_info;
pub mod epoch_schedule;
pub mod fee_calculator;
pub mod genesis_config;
pub mod hard_forks;
pub mod hash;
pub mod incinerator;
pub mod inflation;
pub mod instruction;
pub mod loader_instruction;
pub mod message;
pub mod log;
pub mod native_loader;
pub mod native_token;
pub mod nonce;
pub mod packet;
pub mod poh_config;
pub mod program_option;
pub mod program_pack;
pub mod program_utils;
pub mod pubkey;
pub mod rent;
pub mod rpc_port;
pub mod sanitize;
pub mod secp256k1_program;
pub mod short_vec;
pub mod slot_hashes;
pub mod slot_history;
pub mod stake_history;
pub mod secp256k1;
pub mod shred_version;
pub mod signature;
pub mod signers;
pub mod stake_weighted_timestamp;
pub mod system_instruction;
pub mod system_program;
pub mod sysvar;
pub mod system_transaction;
pub mod timing;
pub mod transaction;
pub mod transport;
/// Convenience macro to declare a static public key and functions to interact with it
///
@ -72,54 +61,16 @@ pub use solana_sdk_macro::pubkeys;
#[rustversion::since(1.46.0)]
pub use solana_sdk_macro::respan;
// On-chain program specific modules
pub mod account_info;
pub mod entrypoint;
pub mod entrypoint_deprecated;
pub mod log;
pub mod program;
pub mod program_error;
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
extern crate lazy_static;
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
pub mod program_stubs;
// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with v1.3.x programs
// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with older programs
#[macro_export]
#[deprecated(
since = "1.4.2",
since = "1.4.3",
note = "program_stubs macro is obsolete and can be safely removed"
)]
macro_rules! program_stubs {
() => {};
}
pub mod serialize_utils;
// Modules not usable by on-chain programs
#[cfg(feature = "everything")]
pub mod client;
#[cfg(feature = "everything")]
pub mod genesis_config;
#[cfg(feature = "everything")]
pub mod hard_forks;
#[cfg(feature = "everything")]
pub mod secp256k1;
#[cfg(feature = "everything")]
pub mod shred_version;
#[cfg(feature = "everything")]
pub mod signature;
#[cfg(feature = "everything")]
pub mod signers;
#[cfg(feature = "everything")]
pub mod system_transaction;
#[cfg(feature = "everything")]
pub mod transaction;
#[cfg(feature = "everything")]
pub mod transport;
#[macro_use]
extern crate serde_derive;
pub extern crate bs58;

View File

@ -1,14 +1,9 @@
//! @brief Solana Rust-based BPF program logging
#![cfg(feature = "program")]
use crate::account_info::AccountInfo;
pub use solana_program_sdk::log::*;
/// Prints a string
/// There are two forms and are fast
/// 1. Single string
/// 2. 5 integers
#[macro_export]
#[deprecated(since = "1.4.3", note = "solana_program_sdk::log::info instead")]
macro_rules! info {
($msg:expr) => {
$crate::log::sol_log($msg)
@ -21,80 +16,5 @@ macro_rules! info {
$arg4 as u64,
$arg5 as u64,
)
}; // `format!()` is not supported yet, Issue #3099
// `format!()` incurs a very large runtime overhead so it should be used with care
// ($($arg:tt)*) => ($crate::log::sol_log(&format!($($arg)*)));
}
/// Prints a string to stdout
///
/// @param message - Message to print
#[inline]
pub fn sol_log(message: &str) {
#[cfg(target_arch = "bpf")]
unsafe {
sol_log_(message.as_ptr(), message.len() as u64);
}
#[cfg(not(target_arch = "bpf"))]
crate::program_stubs::sol_log(message);
}
#[cfg(target_arch = "bpf")]
extern "C" {
fn sol_log_(message: *const u8, len: u64);
}
/// Prints 64 bit values represented as hexadecimal to stdout
///
/// @param argx - integer arguments to print
#[inline]
pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
#[cfg(target_arch = "bpf")]
unsafe {
sol_log_64_(arg1, arg2, arg3, arg4, arg5);
}
#[cfg(not(target_arch = "bpf"))]
crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5);
}
#[cfg(target_arch = "bpf")]
extern "C" {
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64);
}
/// Prints the hexadecimal representation of a slice
///
/// @param slice - The array to print
#[allow(dead_code)]
pub fn sol_log_slice(slice: &[u8]) {
for (i, s) in slice.iter().enumerate() {
info!(0, 0, 0, i, *s);
}
}
/// Prints the hexadecimal representation of the program's input parameters
///
/// @param ka - A pointer to an array of `AccountInfo` to print
/// @param data - A pointer to the instruction data to print
#[allow(dead_code)]
pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) {
for (i, account) in accounts.iter().enumerate() {
info!("AccountInfo");
info!(0, 0, 0, 0, i);
info!("- Is signer");
info!(0, 0, 0, 0, account.is_signer);
info!("- Key");
account.key.log();
info!("- Lamports");
info!(0, 0, 0, 0, account.lamports());
info!("- Account data length");
info!(0, 0, 0, 0, account.data_len());
info!("- Owner");
account.owner.log();
}
info!("Instruction data");
sol_log_slice(data);
};
}

View File

@ -1,71 +1,4 @@
use crate::{decode_error::DecodeError, hash::hashv};
use num_derive::{FromPrimitive, ToPrimitive};
use std::{convert::TryFrom, fmt, mem, str::FromStr};
use thiserror::Error;
pub use bs58;
/// maximum length of derived pubkey seed
pub const MAX_SEED_LEN: usize = 32;
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum PubkeyError {
/// Length of the seed is too long for address generation
#[error("Length of the seed is too long for address generation")]
MaxSeedLengthExceeded,
#[error("Provided seeds do not result in a valid address")]
InvalidSeeds,
}
impl<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))
}
}
}
pub use solana_program_sdk::pubkey::*;
/// New random Pubkey for tests and benchmarks.
#[cfg(feature = "everything")]
@ -73,171 +6,6 @@ pub fn new_rand() -> Pubkey {
Pubkey::new(&rand::random::<[u8; 32]>())
}
impl Pubkey {
pub fn new(pubkey_vec: &[u8]) -> Self {
Self(
<[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec))
.expect("Slice must be the same length as a Pubkey"),
)
}
pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
Self(pubkey_array)
}
pub fn create_with_seed(
base: &Pubkey,
seed: &str,
owner: &Pubkey,
) -> Result<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")]
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
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>> {
let f = std::fs::File::open(infile.to_string())?;
let printable: String = serde_json::from_reader(f)?;
use std::str::FromStr;
Ok(Pubkey::from_str(&printable)?)
}
#[cfg(test)]
mod tests {
use super::*;
use std::{fs::remove_file, str::from_utf8};
#[test]
fn pubkey_fromstr() {
let pubkey = solana_sdk::pubkey::new_rand();
let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string();
assert_eq!(pubkey_base58_str.parse::<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()
);
}
}
use std::fs::remove_file;
#[test]
fn test_read_write_pubkey() -> Result<(), Box<dyn std::error::Error>> {

View File

@ -1,3 +1,5 @@
#![cfg(feature = "everything")]
use digest::Digest;
use serde_derive::{Deserialize, Serialize};

View File

@ -1 +0,0 @@
solana_sdk::declare_id!("KeccakSecp256k11111111111111111111111111111");

View File

@ -1,3 +1,5 @@
#![cfg(feature = "everything")]
use solana_sdk::{
hard_forks::HardForks,
hash::{extend_and_hash, Hash},

View File

@ -1,4 +1,5 @@
//! The `signature` module provides functionality for public, and private keys.
#![cfg(feature = "everything")]
use crate::{pubkey::Pubkey, transaction::TransactionError};
use ed25519_dalek::Signer as DalekSigner;

View File

@ -1,3 +1,4 @@
#![cfg(feature = "everything")]
use crate::{
pubkey::Pubkey,
signature::{Signature, Signer, SignerError},

View File

@ -1,4 +1,5 @@
//! The `system_transaction` module provides functionality for creating system transactions.
#![cfg(feature = "everything")]
use crate::{
hash::Hash,

View File

@ -1,5 +1,7 @@
//! Defines a Transaction type to package an atomic sequence of instructions.
#![cfg(feature = "everything")]
use crate::sanitize::{Sanitize, SanitizeError};
use crate::secp256k1::verify_eth_addresses;
use crate::{

View File

@ -1,3 +1,5 @@
#![cfg(feature = "everything")]
use crate::transaction::TransactionError;
use std::io;
use thiserror::Error;