From 504b55c63c7b5b984e570dd08790694ad2170df9 Mon Sep 17 00:00:00 2001 From: "Frank V. Castellucci" Date: Fri, 21 May 2021 13:28:12 -0400 Subject: [PATCH] Bignum syscalls --- Cargo.lock | 14 +- programs/bpf/Cargo.lock | 9 + programs/bpf/Cargo.toml | 1 + programs/bpf/build.rs | 1 + programs/bpf/rust/bignum/Cargo.toml | 19 + programs/bpf/rust/bignum/src/lib.rs | 130 + programs/bpf/tests/programs.rs | 1 + programs/bpf_loader/Cargo.toml | 1 + programs/bpf_loader/src/syscalls.rs | 4296 ++++++++++++++++++++++----- sdk/bpf/c/inc/solana_sdk.h | 178 ++ sdk/program/Cargo.toml | 1 + sdk/program/src/bignum.rs | 784 +++++ sdk/program/src/lib.rs | 1 + sdk/src/feature_set.rs | 5 + sdk/src/process_instruction.rs | 39 + 15 files changed, 4659 insertions(+), 821 deletions(-) create mode 100644 programs/bpf/rust/bignum/Cargo.toml create mode 100644 programs/bpf/rust/bignum/src/lib.rs create mode 100644 sdk/program/src/bignum.rs diff --git a/Cargo.lock b/Cargo.lock index 15f62950c56c04..df21036c99b8fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2624,15 +2624,15 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.29" +version = "0.10.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" +checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" dependencies = [ "bitflags", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "foreign-types", - "lazy_static", "libc", + "once_cell", "openssl-sys", ] @@ -2644,9 +2644,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.57" +version = "0.9.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" +checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" dependencies = [ "autocfg 1.0.0", "cc", @@ -4084,6 +4084,7 @@ dependencies = [ "log 0.4.11", "num-derive", "num-traits", + "openssl", "rand 0.7.3", "rand_core 0.6.2", "rustversion", @@ -4960,6 +4961,7 @@ dependencies = [ "log 0.4.11", "num-derive", "num-traits", + "openssl", "rand 0.7.3", "rustc_version", "rustversion", diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 51a64075a5b371..175bb1e116a758 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2762,6 +2762,7 @@ dependencies = [ "log", "num-derive 0.3.0", "num-traits", + "openssl", "rand_core 0.6.2", "sha3", "solana-measure", @@ -2816,6 +2817,13 @@ dependencies = [ "solana-program 1.7.0", ] +[[package]] +name = "solana-bpf-rust-bignum" +version = "1.7.0" +dependencies = [ + "solana-program 1.7.0", +] + [[package]] name = "solana-bpf-rust-call-depth" version = "1.7.0" @@ -3360,6 +3368,7 @@ dependencies = [ "log", "num-derive 0.3.0", "num-traits", + "openssl", "rand 0.7.3", "rustc_version", "rustversion", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index d2138569c5f74d..efcf4e0112c7d7 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -45,6 +45,7 @@ members = [ "rust/128bit", "rust/128bit_dep", "rust/alloc", + "rust/bignum", "rust/call_depth", "rust/caller_access", "rust/custom_heap", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index a044671a04175a..a287fd6a290a61 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -60,6 +60,7 @@ fn main() { let rust_programs = [ "128bit", "alloc", + "bignum", "call_depth", "caller_access", "custom_heap", diff --git a/programs/bpf/rust/bignum/Cargo.toml b/programs/bpf/rust/bignum/Cargo.toml new file mode 100644 index 00000000000000..edf1d1723b5f49 --- /dev/null +++ b/programs/bpf/rust/bignum/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "solana-bpf-rust-bignum" +version = "1.7.0" +description = "Solana BPF test program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +documentation = "https://docs.rs/solana-bpf-rust-bignum" +edition = "2018" + +[dependencies] +solana-program = { path = "../../../../sdk/program", version = "=1.7.0" } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/bignum/src/lib.rs b/programs/bpf/rust/bignum/src/lib.rs new file mode 100644 index 00000000000000..146d80a1c5b812 --- /dev/null +++ b/programs/bpf/rust/bignum/src/lib.rs @@ -0,0 +1,130 @@ +//! @brief BigNumber Syscall test + +// #17082 +extern crate solana_program; +use solana_program::{bignum::BigNumber, custom_panic_default, msg}; + +const LONG_DEC_STRING: &str = "1470463693494555670176851280755142329532258274256991544781479988\ + 712408107190720087233560906792937436573943189716784305633216335039\ + 300236370809933808677983409391545753391897467230180786617074456716\ + 591448871466263060696957107957862111484694673874424855359234132302\ + 162208163361387727626078022804936564470716886986414133429438273232\ + 416190048073715996321578752244853524209178212395809614878549824744\ + 227969245726015222693764433413133633359171080169137831743765672068\ + 374040331773668233371864426354886263106537340208256187214278963052\ + 996538599452325797319977413534714912781503130883692806087195354368\ + 8304190675878204079994222"; +const NEG_LONG_DEC_STRING: &str = + "-1470463693494555670176851280755142329532258274256991544781479988\ + 712408107190720087233560906792937436573943189716784305633216335039\ + 300236370809933808677983409391545753391897467230180786617074456716\ + 591448871466263060696957107957862111484694673874424855359234132302\ + 162208163361387727626078022804936564470716886986414133429438273232\ + 416190048073715996321578752244853524209178212395809614878549824744\ + 227969245726015222693764433413133633359171080169137831743765672068\ + 374040331773668233371864426354886263106537340208256187214278963052\ + 996538599452325797319977413534714912781503130883692806087195354368\ + 8304190675878204079994222"; + +/// BigNumber construction +fn test_constructors() { + msg!("BigNumber constructors"); + let base_bn_0 = BigNumber::new(); + assert_eq!(base_bn_0.to_bytes(), vec![0]); + let default_0 = BigNumber::default(); + assert!(base_bn_0 == default_0); + let new_bn_0 = BigNumber::from_u32(0); + assert!(new_bn_0 == default_0); + let max_bn_u32 = BigNumber::from_u32(u32::MAX); + assert_eq!(max_bn_u32.to_bytes(), vec![255, 255, 255, 255]); + let bn_from_dec = BigNumber::from_dec_str(LONG_DEC_STRING); + assert!(!bn_from_dec.is_negative()); + let bn_from_dec = BigNumber::from_dec_str(NEG_LONG_DEC_STRING); + assert!(bn_from_dec.is_negative()); +} + +/// BigNumber simple number and simple maths +fn test_basic_maths() { + msg!("BigNumber Basic Maths"); + let bn_5 = BigNumber::from_u32(5); + let bn_258 = BigNumber::from_u32(258); + let added = bn_5.add(&bn_258); + assert_eq!(added.to_bytes(), [1, 7]); + let subed = bn_5.sub(&bn_258); + assert_eq!(subed.to_bytes(), vec![253]); + let muled = bn_5.mul(&bn_5); + assert_eq!(muled.to_bytes(), vec![25]); + let bn_300 = BigNumber::from_u32(300); + let bn_10 = BigNumber::from_u32(10); + let dived = bn_300.div(&bn_10); + assert_eq!(dived.to_bytes(), vec![30]); +} + +/// BigNumber bigger numbers and complex maths +fn test_complex_maths() { + msg!("BigNumber Complex Maths"); + let bn_arg1 = BigNumber::from_u32(300); + let sqr_res = bn_arg1.sqr(); + assert_eq!(sqr_res.to_bytes(), vec![1, 95, 144]); + let bn_arg2 = BigNumber::from_u32(8); + let bn_arg3 = BigNumber::from_u32(2); + let exp_res = bn_arg2.exp(&bn_arg3); + assert_eq!(exp_res.to_bytes(), vec![64]); + let bn_arg1 = BigNumber::from_u32(300); + let bn_arg2 = BigNumber::from_u32(11); + let mod_sqr = bn_arg1.mod_sqr(&bn_arg2); + assert_eq!(mod_sqr.to_bytes(), vec![9]); + let bn_arg1 = BigNumber::from_u32(300); + let bn_arg2 = BigNumber::from_u32(11); + let bn_arg3 = BigNumber::from_u32(7); + let mod_exp = bn_arg1.mod_exp(&bn_arg2, &bn_arg3); + assert_eq!(mod_exp.to_bytes(), vec![6]); + let mod_mul = bn_arg1.mod_mul(&bn_arg2, &bn_arg3); + assert_eq!(mod_mul.to_bytes(), vec![3]); + let bn_arg1 = BigNumber::from_u32(415); + let bn_arg2 = BigNumber::from_u32(7); + let mod_inv = bn_arg1.mod_inv(&bn_arg2); + assert_eq!(mod_inv.to_bytes(), vec![4]); +} + +fn test_output_logging() { + let bn_from_dec = BigNumber::from_dec_str(LONG_DEC_STRING); + bn_from_dec.log(); + let bn_from_dec = BigNumber::from_dec_str(NEG_LONG_DEC_STRING); + bn_from_dec.log(); +} + +#[no_mangle] +pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { + msg!("bignum"); + test_constructors(); + test_basic_maths(); + test_complex_maths(); + test_output_logging(); + 0u64 +} + +custom_panic_default!(); + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_basic_constructors_pass() { + test_constructors(); + } + #[test] + fn test_simple_maths_pass() { + test_basic_maths(); + } + #[test] + fn test_complex_maths_pass() { + test_complex_maths(); + } + + #[test] + fn test_logging() { + test_output_logging(); + } +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 94599191e30ff3..21b63a1fae7a8a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -443,6 +443,7 @@ fn test_program_bpf_sanity() { programs.extend_from_slice(&[ ("solana_bpf_rust_128bit", true), ("solana_bpf_rust_alloc", true), + ("solana_bpf_rust_bignum", true), ("solana_bpf_rust_custom_heap", true), ("solana_bpf_rust_dep_crate", true), ("solana_bpf_rust_external_spend", false), diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index c8b3400254642f..ac96de98248ab2 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -15,6 +15,7 @@ byteorder = "1.3.4" log = "0.4.11" num-derive = "0.3" num-traits = "0.2" +openssl = "0.10.32" rand_core = "0.6.2" sha3 = "0.9.1" solana-measure = { path = "../../measure", version = "=1.7.0" } diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 15406fe1ed3e48..e0623274af6bf0 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -1,5 +1,6 @@ use crate::{alloc, BpfError}; use alloc::Alloc; +use openssl::bn::*; use solana_rbpf::{ aligned_memory::AlignedMemory, ebpf::MM_HEAP_START, @@ -19,8 +20,8 @@ use solana_sdk::{ entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, epoch_schedule::EpochSchedule, feature_set::{ - cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, - enforce_aligned_host_addrs, keccak256_syscall_enabled, + bignum_syscall_enabled, cpi_data_cost, cpi_share_ro_and_exec_accounts, + demote_sysvar_write_locks, enforce_aligned_host_addrs, keccak256_syscall_enabled, set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall, update_data_on_realloc, }, hash::{Hasher, HASH_BYTES}, @@ -74,6 +75,30 @@ pub enum SyscallError { InstructionTooLarge(usize, usize), #[error("Too many accounts passed to inner instruction")] TooManyAccounts, + #[error("BigNumber: Modular exponentiation error")] + BigNumberModExpError, + #[error("BigNumber: Bytes would exceed buffer provided")] + BigNumberToBytesError, + #[error("BigNumber: {0} expected {1} args but provided {2}")] + BigNumberArgError(String, u64, u64), + #[error("BigNumber: add error")] + BigNumberAddError, + #[error("BigNumber: sub error")] + BigNumberSubError, + #[error("BigNumber: mul error")] + BigNumberMulError, + #[error("BigNumber: div error")] + BigNumberDivError, + #[error("BigNumber: exp error")] + BigNumberExpError, + #[error("BigNumber: sqr error")] + BigNumberSqrError, + #[error("BigNumber: mod_sqr error")] + BigNumberModSqrError, + #[error("BigNumber: mod_mul error")] + BigNumberModMulError, + #[error("BigNumber: mod_inv error")] + BigNumberModInvError, } impl From for EbpfError { fn from(error: SyscallError) -> Self { @@ -151,6 +176,28 @@ pub fn register_syscalls( .register_syscall_by_name(b"sol_invoke_signed_rust", SyscallInvokeSignedRust::call)?; syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?; + if invoke_context.is_feature_active(&bignum_syscall_enabled::id()) { + syscall_registry + .register_syscall_by_name(b"sol_bignum_from_u32", SyscallBigNumFromU32::call)?; + syscall_registry + .register_syscall_by_name(b"sol_bignum_from_dec_str", SyscallBigNumFromDecStr::call)?; + syscall_registry + .register_syscall_by_name(b"sol_bignum_mod_exp", SyscallBigNumModExp::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_log", SyscallBigNumLog::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_add", SyscallBigNumAdd::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_sub", SyscallBigNumSub::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_mul", SyscallBigNumMul::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_div", SyscallBigNumDiv::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_exp", SyscallBigNumExp::call)?; + syscall_registry.register_syscall_by_name(b"sol_bignum_sqr", SyscallBigNumSqr::call)?; + syscall_registry + .register_syscall_by_name(b"sol_bignum_mod_sqr", SyscallBigNumModSqr::call)?; + syscall_registry + .register_syscall_by_name(b"sol_bignum_mod_mul", SyscallBigNumModMul::call)?; + syscall_registry + .register_syscall_by_name(b"sol_bignum_mod_inv", SyscallBigNumModInv::call)?; + } + Ok(syscall_registry) } @@ -268,6 +315,126 @@ pub fn bind_syscall_context_objects<'a>( }), ); + // Bignum + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumFromU32 { + cost: bpf_compute_budget.bignum_from_u32_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumFromDecStr { + cost: bpf_compute_budget.bignum_from_dec_str_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumAdd { + cost: bpf_compute_budget.bignum_add_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumSub { + cost: bpf_compute_budget.bignum_sub_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumMul { + cost: bpf_compute_budget.bignum_mul_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumDiv { + cost: bpf_compute_budget.bignum_div_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumExp { + cost: bpf_compute_budget.bignum_exp_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumSqr { + cost: bpf_compute_budget.bignum_sqr_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumModSqr { + cost: bpf_compute_budget.bignum_mod_sqr_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumModMul { + cost: bpf_compute_budget.bignum_mod_mul_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumModInv { + cost: bpf_compute_budget.bignum_mod_inv_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumModExp { + cost: bpf_compute_budget.bignum_mod_exp_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&bignum_syscall_enabled::id()), + Box::new(SyscallBigNumLog { + cost: bpf_compute_budget.bignum_log_cost, + compute_meter: invoke_context.get_compute_meter(), + logger: invoke_context.get_logger(), + loader_id, + }), + ); + let is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id()); let invoke_context = Rc::new(RefCell::new(invoke_context)); @@ -2118,736 +2285,3446 @@ fn call<'a>( Ok(SUCCESS) } -#[cfg(test)] -mod tests { - use super::*; - use solana_rbpf::{ - ebpf::HOST_ALIGN, memory_region::MemoryRegion, user_error::UserError, vm::Config, - }; - use solana_sdk::{ - bpf_loader, - fee_calculator::FeeCalculator, - hash::hashv, - process_instruction::{MockComputeMeter, MockInvokeContext, MockLogger}, +/// BIGNUM data cost +const BIGNUM_WORDCOST: f64 = 6.0; +/// BIGNUM cost divisor +const BIGNUM_WORDCOST_DIVISOR: f64 = 32.0; +/// Bignum data cost calculator +macro_rules! calc_bignum_cost { + ($input:expr) => { + (BIGNUM_WORDCOST * ($input / BIGNUM_WORDCOST_DIVISOR).ceil()) as u64 }; - use std::str::FromStr; +} - const DEFAULT_CONFIG: Config = Config { - max_call_depth: 20, - stack_frame_size: 4_096, - enable_instruction_meter: true, - enable_instruction_tracing: false, - }; +struct SyscallBigNumFromU32<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumFromU32<'a> { + fn call( + &mut self, + bn_out_addr: u64, + u64_out_len: u64, + u32_val: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(u64_out_len as f64)), + result + ); + let byte_slice = question_mark!( + translate_slice_mut::( + memory_mapping, + bn_out_addr, + u64_out_len, + self.loader_id, + true + ), + result + ); + let bn_bytes = BigNum::from_u32(u32_val as u32).unwrap().to_vec(); + if bn_bytes.len() as u64 > u64_out_len { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + (*byte_slice).copy_from_slice(&bn_bytes); + *result = Ok(0) + } + } +} +/// Converts a decimal string to a bignum and returns +/// the vector bytes and if negative or not +struct SyscallBigNumFromDecStr<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumFromDecStr<'a> { + fn call( + &mut self, + in_dec_str_addr: u64, + in_size: u64, + out_array_addr: u64, + out_size_addr: u64, + out_negative_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(in_size as f64)), + result + ); - macro_rules! assert_access_violation { - ($result:expr, $va:expr, $len:expr) => { - match $result { - Err(EbpfError::AccessViolation(_, _, va, len, _)) if $va == va && len == len => (), - _ => panic!(), - } + // Get the string and convert to BigNum + let in_dec_raw = question_mark!( + translate_slice::( + memory_mapping, + in_dec_str_addr, + in_size, + self.loader_id, + true + ), + result + ); + let i = match in_dec_raw.iter().position(|byte| *byte == 0) { + Some(i) => i, + None => in_size as usize, }; + let big_number = BigNum::from_dec_str(from_utf8(&in_dec_raw[..i]).unwrap()).unwrap(); + let big_number_bytes = big_number.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + + // Get result + let byte_slice = question_mark!( + translate_slice_mut::( + memory_mapping, + out_array_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::(memory_mapping, out_negative_addr, self.loader_id, true), + result + ); + // Get return bytes + // Exceeds allocated size + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*byte_slice).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*byte_slice)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = big_number.is_negative() as u64; + *result = Ok(0) + } } +} - #[test] - fn test_translate() { - const START: u64 = 100; - const LENGTH: u64 = 1000; - let data = vec![0u8; LENGTH as usize]; - let addr = data.as_ptr() as u64; - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion::new_from_slice(&data, START, 0, false)], - &DEFAULT_CONFIG, +/// Converts the arguments array into BigNum and set negative flag +/// Argument array is BigNum slices for 0..n-1, n is negative flag array +fn translate_bignum_args( + arg_addr: u64, + arg_count: u64, + loader_id: &Pubkey, + buffers: &mut Vec, + bytes: &mut u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, +) { + let args = question_mark!( + translate_slice::<&[u8]>(memory_mapping, arg_addr, arg_count, loader_id, true,), + result + ); + *bytes = 0; + // BigNums are all but ast + for arg in args[0..args.len() - 1].iter() { + buffers.push( + BigNum::from_slice(question_mark!( + translate_slice::( + memory_mapping, + arg.as_ptr() as u64, + arg.len() as u64, + loader_id, + true + ), + result + )) + .unwrap(), ) - .unwrap(); + } + // Negative flags are last + let last = args.last().unwrap(); + let neg_flags = question_mark!( + translate_slice::( + memory_mapping, + last.as_ptr() as u64, + last.len() as u64, + loader_id, + true + ), + result + ); + // Get byte count and set negative flag + *bytes += neg_flags.len() as u64; + for x in 0..neg_flags.len() { + *bytes += buffers[x].num_bytes() as u64; + buffers[x].set_negative(neg_flags[x] == 1) + } +} - let cases = vec![ - (true, START, 0, addr), - (true, START, 1, addr), - (true, START, LENGTH, addr), - (true, START + 1, LENGTH - 1, addr + 1), - (false, START + 1, LENGTH, 0), - (true, START + LENGTH - 1, 1, addr + LENGTH - 1), - (true, START + LENGTH, 0, addr + LENGTH), - (false, START + LENGTH, 1, 0), - (false, START, LENGTH + 1, 0), - (false, 0, 0, 0), - (false, 0, 1, 0), - (false, START - 1, 0, 0), - (false, START - 1, 1, 0), - (true, START + LENGTH / 2, LENGTH / 2, addr + LENGTH / 2), - ]; - for (ok, start, length, value) in cases { - if ok { - assert_eq!( - translate(&memory_mapping, AccessType::Load, start, length).unwrap(), - value - ) - } else { - assert!(translate(&memory_mapping, AccessType::Load, start, length).is_err()) +/// Add BigNums +struct SyscallBigNumAdd<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumAdd<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_add"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.checked_add(&buffer[0], &buffer[1]) { + Ok(_) => { + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberAddError.into()), + } } } } +} - #[test] - fn test_translate_type() { - // Pubkey +/// Subtract BigNums +struct SyscallBigNumSub<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumSub<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_sub"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.checked_sub(&buffer[0], &buffer[1]) { + Ok(_) => { + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberAddError.into()), + } + } + } + } +} +/// Subtract BigNums +struct SyscallBigNumMul<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumMul<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_mul"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.checked_mul( + &buffer[0], + &buffer[1], + &mut BigNumContext::new().unwrap(), + ) { + Ok(_) => { + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberAddError.into()), + } + } + } + } +} +struct SyscallBigNumDiv<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumDiv<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_div"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.checked_div( + &buffer[0], + &buffer[1], + &mut BigNumContext::new().unwrap(), + ) { + Ok(_) => { + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberAddError.into()), + } + } + } + } +} +struct SyscallBigNumSqr<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumSqr<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_sqr"); + match arguments_count == 2 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 1u64, arguments_count - 1).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + // Get negative flag pointer + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.sqr(&buffer[0], &mut BigNumContext::new().unwrap()) { + Ok(_) => { + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberSqrError.into()), + } + } + } + } +} +struct SyscallBigNumExp<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumExp<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_exp"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.exp(&buffer[0], &buffer[1], &mut BigNumContext::new().unwrap()) { + Ok(_) => { + // Get out_size pointer and negative flag pointer + // Get negative flag pointer + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberExpError.into()), + } + } + } + } +} + +struct SyscallBigNumModSqr<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumModSqr<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_mod_sqr"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.mod_sqr(&buffer[0], &buffer[1], &mut BigNumContext::new().unwrap()) + { + Ok(_) => { + // Get out_size pointer and negative flag pointer + // Get negative flag pointer + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberModSqrError.into()), + } + } + } + } +} + +/// Calculates a data cost for mod_exp as per https://eips.ethereum.org/EIPS/eip-198 +fn calculate_exp_byte_cost(base: &BigNum, exponent: &BigNum, modulus: &BigNum) -> u64 { + let zero = BigNum::from_u32(0).unwrap(); + let exp_bytes_length = exponent.num_bytes() as u32; + let exp_bits_length = exponent.num_bits() as u32; + let base_bytes_length = base.num_bytes() as u32; + // Adjusted length calculation + let adjusted_exp_length = match exp_bytes_length { + 0..=32 if exponent == &zero => 0, + 0..=32 => exp_bits_length - 1, + _ => { + let first_32 = BigNum::from_slice(&exponent.as_ref().to_vec()[0..31]).unwrap(); + if first_32 == zero { + 8 * (exp_bytes_length - 32) + } else { + 8 * (exp_bytes_length - 32) + ((first_32.num_bits() as u32) - 1) + } + } + } as u64; + // mult_complexity + let mult_compexity_arg = std::cmp::max(base_bytes_length, modulus.num_bytes() as u32) as u64; + let mult_complexity = if mult_compexity_arg <= 64 { + mult_compexity_arg * mult_compexity_arg + } else if mult_compexity_arg <= 1024 { + (((mult_compexity_arg * mult_compexity_arg) / 4) + (96 * mult_compexity_arg)) - 3_072 + } else { + (((mult_compexity_arg * mult_compexity_arg) / 16) + (480 * mult_compexity_arg)) - 199_680 + }; + // Calc data costs and return + ((mult_complexity * std::cmp::max(adjusted_exp_length, 1u64)) as f64 / 20f64).floor() as u64 +} +/// BIGNUM sol_bignum_mod_exp +struct SyscallBigNumModExp<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumModExp<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_mod_exp"); + match arguments_count == 4 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + question_mark!( + self.compute_meter.consume( + self.cost + calculate_exp_byte_cost(&buffer[0], &buffer[1], &buffer[2]) + ), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.mod_exp( + &buffer[0], + &buffer[1], + &buffer[2], + &mut BigNumContext::new().unwrap(), + ) { + Ok(_) => { + // Get out_size pointer and negative flag pointer + // Get negative flag pointer + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberModExpError.into()), + } + } + } + } +} + +struct SyscallBigNumModMul<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumModMul<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_mod_mul"); + match arguments_count == 4 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.mod_mul( + &buffer[0], + &buffer[1], + &buffer[2], + &mut BigNumContext::new().unwrap(), + ) { + Ok(_) => { + // Get out_size pointer and negative flag pointer + // Get negative flag pointer + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberModExpError.into()), + } + } + } + } +} + +struct SyscallBigNumModInv<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumModInv<'a> { + fn call( + &mut self, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let my_func = String::from("sol_bignum_mod_mul"); + match arguments_count == 3 { + false => { + *result = + Err(SyscallError::BigNumberArgError(my_func, 3u64, arguments_count).into()) + } + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, + self.loader_id, + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); + + // Get out size and out vector + let out_size = question_mark!( + translate_type_mut::(memory_mapping, out_size_addr, self.loader_id, true), + result + ); + let bignum_result = question_mark!( + translate_slice_mut::( + memory_mapping, + out_result_addr, + *out_size, + self.loader_id, + true + ), + result + ); + let is_negative = question_mark!( + translate_type_mut::( + memory_mapping, + out_negative_addr, + self.loader_id, + true + ), + result + ); + + // Execute function + let mut bn_result = BigNum::new().unwrap(); + match bn_result.mod_inverse( + &buffer[0], + &buffer[1], + &mut BigNumContext::new().unwrap(), + ) { + Ok(_) => { + // Get out_size pointer and negative flag pointer + // Get negative flag pointer + let big_number_bytes = bn_result.as_ref().to_vec(); + let big_number_len = big_number_bytes.len() as u64; + if big_number_len > *out_size { + *result = Err(SyscallError::BigNumberToBytesError.into()) + } else { + // Equal (memcpy) + if big_number_len == *out_size { + (*bignum_result).copy_from_slice(&big_number_bytes); + } + // Smaller than our buffer + else { + let mut index = 0; + for byte in big_number_bytes.iter() { + (*bignum_result)[index] = *byte; + index += 1; + } + *out_size = index as u64; + } + *is_negative = bn_result.is_negative() as u64; + *result = Ok(0) + } + } + Err(_) => *result = Err(SyscallError::BigNumberModExpError.into()), + } + } + } + } +} +/// Log BigNum values +struct SyscallBigNumLog<'a> { + cost: u64, + compute_meter: Rc>, + logger: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumLog<'a> { + fn call( + &mut self, + bn_addr: u64, + bn_size: u64, + neg_flag: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + let bignum_ptr = question_mark!( + translate_slice::(memory_mapping, bn_addr, bn_size, self.loader_id, true), + result + ); + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bn_size as f64)), + result + ); + let mut bignum = BigNum::from_slice(bignum_ptr).unwrap(); + bignum.set_negative(neg_flag == 1u64); + stable_log::program_log(&self.logger, &bignum.to_string()); + *result = Ok(0); + } +} +#[cfg(test)] +mod tests { + use super::*; + use solana_rbpf::{ + ebpf::HOST_ALIGN, memory_region::MemoryRegion, user_error::UserError, vm::Config, + }; + use solana_sdk::{ + bpf_loader, + fee_calculator::FeeCalculator, + hash::hashv, + process_instruction::{MockComputeMeter, MockInvokeContext, MockLogger}, + }; + use std::str::FromStr; + + const DEFAULT_CONFIG: Config = Config { + max_call_depth: 20, + stack_frame_size: 4_096, + enable_instruction_meter: true, + enable_instruction_tracing: false, + }; + + const LONG_DEC_STRING: &str = + "1470463693494555670176851280755142329532258274256991544781479988\ + 712408107190720087233560906792937436573943189716784305633216335039\ + 300236370809933808677983409391545753391897467230180786617074456716\ + 591448871466263060696957107957862111484694673874424855359234132302\ + 162208163361387727626078022804936564470716886986414133429438273232\ + 416190048073715996321578752244853524209178212395809614878549824744\ + 227969245726015222693764433413133633359171080169137831743765672068\ + 374040331773668233371864426354886263106537340208256187214278963052\ + 996538599452325797319977413534714912781503130883692806087195354368\ + 8304190675878204079994222"; + + const NEG_LONG_DEC_STRING: &str = + "-1470463693494555670176851280755142329532258274256991544781479988\ + 712408107190720087233560906792937436573943189716784305633216335039\ + 300236370809933808677983409391545753391897467230180786617074456716\ + 591448871466263060696957107957862111484694673874424855359234132302\ + 162208163361387727626078022804936564470716886986414133429438273232\ + 416190048073715996321578752244853524209178212395809614878549824744\ + 227969245726015222693764433413133633359171080169137831743765672068\ + 374040331773668233371864426354886263106537340208256187214278963052\ + 996538599452325797319977413534714912781503130883692806087195354368\ + 8304190675878204079994222"; + + macro_rules! assert_access_violation { + ($result:expr, $va:expr, $len:expr) => { + match $result { + Err(EbpfError::AccessViolation(_, _, va, len, _)) if $va == va && len == len => (), + _ => panic!(), + } + }; + } + + #[test] + fn test_translate() { + const START: u64 = 100; + const LENGTH: u64 = 1000; + let data = vec![0u8; LENGTH as usize]; + let addr = data.as_ptr() as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion::new_from_slice(&data, START, 0, false)], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let cases = vec![ + (true, START, 0, addr), + (true, START, 1, addr), + (true, START, LENGTH, addr), + (true, START + 1, LENGTH - 1, addr + 1), + (false, START + 1, LENGTH, 0), + (true, START + LENGTH - 1, 1, addr + LENGTH - 1), + (true, START + LENGTH, 0, addr + LENGTH), + (false, START + LENGTH, 1, 0), + (false, START, LENGTH + 1, 0), + (false, 0, 0, 0), + (false, 0, 1, 0), + (false, START - 1, 0, 0), + (false, START - 1, 1, 0), + (true, START + LENGTH / 2, LENGTH / 2, addr + LENGTH / 2), + ]; + for (ok, start, length, value) in cases { + if ok { + assert_eq!( + translate(&memory_mapping, AccessType::Load, start, length).unwrap(), + value + ) + } else { + assert!(translate(&memory_mapping, AccessType::Load, start, length).is_err()) + } + } + } + + #[test] + fn test_translate_type() { + // Pubkey let pubkey = solana_sdk::pubkey::new_rand(); let addr = &pubkey as *const _ as u64; let memory_mapping = MemoryMapping::new::( vec![MemoryRegion { host_addr: addr, vm_addr: 100, - len: std::mem::size_of::() as u64, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_pubkey = + translate_type::(&memory_mapping, 100, &bpf_loader::id(), true).unwrap(); + assert_eq!(pubkey, *translated_pubkey); + + // Instruction + let instruction = Instruction::new_with_bincode( + solana_sdk::pubkey::new_rand(), + &"foobar", + vec![AccountMeta::new(solana_sdk::pubkey::new_rand(), false)], + ); + let addr = &instruction as *const _ as u64; + let mut memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 96, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_instruction = + translate_type::(&memory_mapping, 96, &bpf_loader::id(), true).unwrap(); + assert_eq!(instruction, *translated_instruction); + memory_mapping.resize_region::(0, 1).unwrap(); + assert!( + translate_type::(&memory_mapping, 100, &bpf_loader::id(), true).is_err() + ); + } + + #[test] + fn test_translate_slice() { + // zero len + let good_data = vec![1u8, 2, 3, 4, 5]; + let data: Vec = vec![]; + assert_eq!(0x1 as *const u8, data.as_ptr()); + let addr = good_data.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: good_data.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_data = translate_slice::( + &memory_mapping, + data.as_ptr() as u64, + 0, + &bpf_loader::id(), + true, + ) + .unwrap(); + assert_eq!(data, translated_data); + assert_eq!(0, translated_data.len()); + + // u8 + let mut data = vec![1u8, 2, 3, 4, 5]; + let addr = data.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: data.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_data = translate_slice::( + &memory_mapping, + 100, + data.len() as u64, + &bpf_loader::id(), + true, + ) + .unwrap(); + assert_eq!(data, translated_data); + data[0] = 10; + assert_eq!(data, translated_data); + assert!(translate_slice::( + &memory_mapping, + data.as_ptr() as u64, + u64::MAX, + &bpf_loader::id(), + true, + ) + .is_err()); + + assert!(translate_slice::( + &memory_mapping, + 100 - 1, + data.len() as u64, + &bpf_loader::id(), + true, + ) + .is_err()); + + // u64 + let mut data = vec![1u64, 2, 3, 4, 5]; + let addr = data.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 96, + len: (data.len() * size_of::()) as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_data = translate_slice::( + &memory_mapping, + 96, + data.len() as u64, + &bpf_loader::id(), + true, + ) + .unwrap(); + assert_eq!(data, translated_data); + data[0] = 10; + assert_eq!(data, translated_data); + assert!( + translate_slice::(&memory_mapping, 96, u64::MAX, &bpf_loader::id(), true,) + .is_err() + ); + + // Pubkeys + let mut data = vec![solana_sdk::pubkey::new_rand(); 5]; + let addr = data.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: (data.len() * std::mem::size_of::()) as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + let translated_data = translate_slice::( + &memory_mapping, + 100, + data.len() as u64, + &bpf_loader::id(), + true, + ) + .unwrap(); + assert_eq!(data, translated_data); + data[0] = solana_sdk::pubkey::new_rand(); // Both should point to same place + assert_eq!(data, translated_data); + } + + #[test] + fn test_translate_string_and_do() { + let string = "Gaggablaghblagh!"; + let addr = string.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: string.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + assert_eq!( + 42, + translate_string_and_do( + &memory_mapping, + 100, + string.len() as u64, + &bpf_loader::id(), + true, + &mut |string: &str| { + assert_eq!(string, "Gaggablaghblagh!"); + Ok(42) + } + ) + .unwrap() + ); + } + + #[test] + #[should_panic(expected = "UserError(SyscallError(Abort))")] + fn test_syscall_abort() { + let memory_mapping = + MemoryMapping::new::(vec![MemoryRegion::default()], &DEFAULT_CONFIG) + .unwrap(); + let mut result: Result> = Ok(0); + SyscallAbort::call( + &mut SyscallAbort {}, + 0, + 0, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + } + + #[test] + #[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")] + fn test_syscall_sol_panic() { + let string = "Gaggablaghblagh!"; + let addr = string.as_ptr() as *const _ as u64; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: string.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: string.len() as u64 - 1, + })); + let mut syscall_panic = SyscallPanic { + compute_meter, + loader_id: &bpf_loader::id(), + enforce_aligned_host_addrs: true, + }; + let mut result: Result> = Ok(0); + syscall_panic.call( + 100, + string.len() as u64, + 42, + 84, + 0, + &memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: string.len() as u64, + })); + let mut syscall_panic = SyscallPanic { + compute_meter, + loader_id: &bpf_loader::id(), + enforce_aligned_host_addrs: true, + }; + let mut result: Result> = Ok(0); + syscall_panic.call( + 100, + string.len() as u64, + 42, + 84, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + } + + #[test] + fn test_syscall_sol_log() { + let string = "Gaggablaghblagh!"; + let addr = string.as_ptr() as *const _ as u64; + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { remaining: 1000000 })); + let log = Rc::new(RefCell::new(vec![])); + let logger: Rc> = + Rc::new(RefCell::new(MockLogger { log: log.clone() })); + let mut syscall_sol_log = SyscallLog { + compute_meter, + logger, + loader_id: &bpf_loader::id(), + enforce_aligned_host_addrs: true, + }; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: string.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 100, + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + assert_eq!(log.borrow().len(), 1); + assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!"); + + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 101, // AccessViolation + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, 101, string.len() as u64); + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 100, + string.len() as u64 * 2, // AccessViolation + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, 100, string.len() as u64 * 2); + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 100, + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (string.len() as u64 * 2) - 1, + })); + let logger: Rc> = Rc::new(RefCell::new(MockLogger { log })); + let mut syscall_sol_log = SyscallLog { + compute_meter, + logger, + loader_id: &bpf_loader::id(), + enforce_aligned_host_addrs: true, + }; + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 100, + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 100, + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_sol_log_u64() { + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: std::u64::MAX, + })); + let log = Rc::new(RefCell::new(vec![])); + let logger: Rc> = + Rc::new(RefCell::new(MockLogger { log: log.clone() })); + let mut syscall_sol_log_u64 = SyscallLogU64 { + cost: 0, + compute_meter, + logger, + }; + let memory_mapping = MemoryMapping::new::(vec![], &DEFAULT_CONFIG).unwrap(); + + let mut result: Result> = Ok(0); + syscall_sol_log_u64.call(1, 2, 3, 4, 5, &memory_mapping, &mut result); + result.unwrap(); + + assert_eq!(log.borrow().len(), 1); + assert_eq!(log.borrow()[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5"); + } + + #[test] + fn test_syscall_sol_pubkey() { + let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap(); + let addr = &pubkey.as_ref()[0] as *const _ as u64; + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { remaining: 2 })); + let log = Rc::new(RefCell::new(vec![])); + let logger: Rc> = + Rc::new(RefCell::new(MockLogger { log: log.clone() })); + let mut syscall_sol_pubkey = SyscallLogPubkey { + cost: 1, + compute_meter, + logger, + loader_id: &bpf_loader::id(), + enforce_aligned_host_addrs: true, + }; + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: addr, + vm_addr: 100, + len: 32, vm_gap_shift: 63, is_writable: false, }], &DEFAULT_CONFIG, ) .unwrap(); - let translated_pubkey = - translate_type::(&memory_mapping, 100, &bpf_loader::id(), true).unwrap(); - assert_eq!(pubkey, *translated_pubkey); - // Instruction - let instruction = Instruction::new_with_bincode( - solana_sdk::pubkey::new_rand(), - &"foobar", - vec![AccountMeta::new(solana_sdk::pubkey::new_rand(), false)], + let mut result: Result> = Ok(0); + syscall_sol_pubkey.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + assert_eq!(log.borrow().len(), 1); + assert_eq!( + log.borrow()[0], + "Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN" + ); + let mut result: Result> = Ok(0); + syscall_sol_pubkey.call( + 101, // AccessViolation + 32, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, 101, 32); + let mut result: Result> = Ok(0); + syscall_sol_pubkey.call(100, 32, 0, 0, 0, &memory_mapping, &mut result); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_sol_alloc_free() { + // large alloc + { + let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion::new_from_slice( + heap.as_slice(), + MM_HEAP_START, + 0, + true, + )], + &DEFAULT_CONFIG, + ) + .unwrap(); + let mut syscall = SyscallAllocFree { + aligned: true, + allocator: BpfAllocator::new(heap, MM_HEAP_START), + }; + let mut result: Result> = Ok(0); + syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_ne!(result.unwrap(), 0); + let mut result: Result> = Ok(0); + syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_eq!(result.unwrap(), 0); + let mut result: Result> = Ok(0); + syscall.call(u64::MAX, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_eq!(result.unwrap(), 0); + } + // many small unaligned allocs + { + let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion::new_from_slice( + heap.as_slice(), + MM_HEAP_START, + 0, + true, + )], + &DEFAULT_CONFIG, + ) + .unwrap(); + let mut syscall = SyscallAllocFree { + aligned: false, + allocator: BpfAllocator::new(heap, MM_HEAP_START), + }; + for _ in 0..100 { + let mut result: Result> = Ok(0); + syscall.call(1, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_ne!(result.unwrap(), 0); + } + let mut result: Result> = Ok(0); + syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_eq!(result.unwrap(), 0); + } + // many small aligned allocs + { + let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion::new_from_slice( + heap.as_slice(), + MM_HEAP_START, + 0, + true, + )], + &DEFAULT_CONFIG, + ) + .unwrap(); + let mut syscall = SyscallAllocFree { + aligned: true, + allocator: BpfAllocator::new(heap, MM_HEAP_START), + }; + for _ in 0..12 { + let mut result: Result> = Ok(0); + syscall.call(1, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_ne!(result.unwrap(), 0); + } + let mut result: Result> = Ok(0); + syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); + assert_eq!(result.unwrap(), 0); + } + // aligned allocs + + fn check_alignment() { + let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion::new_from_slice( + heap.as_slice(), + MM_HEAP_START, + 0, + true, + )], + &DEFAULT_CONFIG, + ) + .unwrap(); + let mut syscall = SyscallAllocFree { + aligned: true, + allocator: BpfAllocator::new(heap, MM_HEAP_START), + }; + let mut result: Result> = Ok(0); + syscall.call( + size_of::() as u64, + 0, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + let address = result.unwrap(); + assert_ne!(address, 0); + assert_eq!((address as *const u8).align_offset(align_of::()), 0); + } + check_alignment::(); + check_alignment::(); + check_alignment::(); + check_alignment::(); + check_alignment::(); + } + + #[test] + fn test_syscall_sha256() { + let bytes1 = "Gaggablaghblagh!"; + let bytes2 = "flurbos"; + + #[allow(dead_code)] + struct MockSlice { + pub addr: u64, + pub len: usize, + } + let mock_slice1 = MockSlice { + addr: 4096, + len: bytes1.len(), + }; + let mock_slice2 = MockSlice { + addr: 8192, + len: bytes2.len(), + }; + let bytes_to_hash = [mock_slice1, mock_slice2]; + let hash_result = [0; HASH_BYTES]; + let ro_len = bytes_to_hash.len() as u64; + let ro_va = 96; + let rw_va = 192; + let memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion { + host_addr: bytes1.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: bytes1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: bytes2.as_ptr() as *const _ as u64, + vm_addr: 8192, + len: bytes2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: bytes_to_hash.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: hash_result.as_ptr() as *const _ as u64, + vm_addr: rw_va, + len: HASH_BYTES as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &DEFAULT_CONFIG, + ) + .unwrap(); + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (bytes1.len() + bytes2.len()) as u64, + })); + let mut syscall = SyscallSha256 { + sha256_base_cost: 0, + sha256_byte_cost: 2, + compute_meter, + loader_id: &bpf_loader_deprecated::id(), + enforce_aligned_host_addrs: true, + }; + + let mut result: Result> = Ok(0); + syscall.call(ro_va, ro_len, rw_va, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + + let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes(); + assert_eq!(hash_result, hash_local); + let mut result: Result> = Ok(0); + syscall.call( + ro_va - 1, // AccessViolation + ro_len, + rw_va, + 0, + 0, + &memory_mapping, + &mut result, ); - let addr = &instruction as *const _ as u64; - let mut memory_mapping = MemoryMapping::new::( + assert_access_violation!(result, ro_va - 1, ro_len); + let mut result: Result> = Ok(0); + syscall.call( + ro_va, + ro_len + 1, // AccessViolation + rw_va, + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, ro_va, ro_len + 1); + let mut result: Result> = Ok(0); + syscall.call( + ro_va, + ro_len, + rw_va - 1, // AccessViolation + 0, + 0, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64); + + syscall.call(ro_va, ro_len, rw_va, 0, 0, &memory_mapping, &mut result); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_get_sysvar() { + // Test clock sysvar + { + let got_clock = Clock::default(); + let got_clock_va = 2048; + + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: &got_clock as *const _ as u64, + vm_addr: got_clock_va, + len: size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let src_clock = Clock { + slot: 1, + epoch_start_timestamp: 2, + epoch: 3, + leader_schedule_epoch: 4, + unix_timestamp: 5, + }; + let mut invoke_context = MockInvokeContext::new(vec![]); + let mut data = vec![]; + bincode::serialize_into(&mut data, &src_clock).unwrap(); + invoke_context + .sysvars + .push((sysvar::clock::id(), Some(Rc::new(data)))); + + let mut syscall = SyscallGetClockSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + + syscall.call(got_clock_va, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + assert_eq!(got_clock, src_clock); + } + + // Test epoch_schedule sysvar + { + let got_epochschedule = EpochSchedule::default(); + let got_epochschedule_va = 2048; + + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: &got_epochschedule as *const _ as u64, + vm_addr: got_epochschedule_va, + len: size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let src_epochschedule = EpochSchedule { + slots_per_epoch: 1, + leader_schedule_slot_offset: 2, + warmup: false, + first_normal_epoch: 3, + first_normal_slot: 4, + }; + let mut invoke_context = MockInvokeContext::new(vec![]); + let mut data = vec![]; + bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); + invoke_context + .sysvars + .push((sysvar::epoch_schedule::id(), Some(Rc::new(data)))); + + let mut syscall = SyscallGetEpochScheduleSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + + syscall.call( + got_epochschedule_va, + 0, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + assert_eq!(got_epochschedule, src_epochschedule); + } + + // Test fees sysvar + { + let got_fees = Fees::default(); + let got_fees_va = 2048; + + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: &got_fees as *const _ as u64, + vm_addr: got_fees_va, + len: size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let src_fees = Fees { + fee_calculator: FeeCalculator { + lamports_per_signature: 1, + }, + }; + let mut invoke_context = MockInvokeContext::new(vec![]); + let mut data = vec![]; + bincode::serialize_into(&mut data, &src_fees).unwrap(); + invoke_context + .sysvars + .push((sysvar::fees::id(), Some(Rc::new(data)))); + + let mut syscall = SyscallGetFeesSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + + syscall.call(got_fees_va, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + assert_eq!(got_fees, src_fees); + } + + // Test rent sysvar + { + let got_rent = Rent::default(); + let got_rent_va = 2048; + + let memory_mapping = MemoryMapping::new::( + vec![MemoryRegion { + host_addr: &got_rent as *const _ as u64, + vm_addr: got_rent_va, + len: size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }], + &DEFAULT_CONFIG, + ) + .unwrap(); + + let src_rent = Rent { + lamports_per_byte_year: 1, + exemption_threshold: 2.0, + burn_percent: 3, + }; + let mut invoke_context = MockInvokeContext::new(vec![]); + let mut data = vec![]; + bincode::serialize_into(&mut data, &src_rent).unwrap(); + invoke_context + .sysvars + .push((sysvar::rent::id(), Some(Rc::new(data)))); + + let mut syscall = SyscallGetRentSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + + syscall.call(got_rent_va, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + assert_eq!(got_rent, src_rent); + } + } + // Bignum tests + // creates new bignum with u32 value + fn new_u32_bignum(val: u32) -> Vec { + let bignum_size = { + match val { + 0..=255 => 1, + 256..=65_535 => 2, + 65_536..=16_777_215 => 3, + _ => 4, + } + }; + let mut my_buffer = Vec::::with_capacity(bignum_size); + let memory_mapping = MemoryMapping::new::( vec![MemoryRegion { - host_addr: addr, - vm_addr: 96, - len: std::mem::size_of::() as u64, + host_addr: my_buffer.as_mut_ptr() as *mut _ as u64, + vm_addr: 1024, + len: bignum_size as u64, vm_gap_shift: 63, - is_writable: false, + is_writable: true, }], &DEFAULT_CONFIG, ) .unwrap(); - let translated_instruction = - translate_type::(&memory_mapping, 96, &bpf_loader::id(), true).unwrap(); - assert_eq!(instruction, *translated_instruction); - memory_mapping.resize_region::(0, 1).unwrap(); - assert!( - translate_type::(&memory_mapping, 100, &bpf_loader::id(), true).is_err() + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { remaining: 400 })); + let mut syscall = SyscallBigNumFromU32 { + cost: 100, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call( + 1024, + bignum_size as u64, + val as u64, + 0, + 0, + &memory_mapping, + &mut result, ); + result.unwrap(); + unsafe { my_buffer.set_len(bignum_size) }; + my_buffer } + // creates new bignum with bytes array + fn new_dec_str_bignum(string: &str) -> (bool, Vec) { + let dec_str_addr = string.as_ptr() as *const _ as u64; + let dec_str_len = string.len(); + let bytes_buf = vec![0u8; dec_str_len]; + let mut bytes_len = bytes_buf.len(); + let bytes_len_addr = &mut bytes_len as *mut _ as u64; + let mut neg_flag = 0u64; + let neg_flag_addr = &mut neg_flag as *mut _ as u64; - #[test] - fn test_translate_slice() { - // zero len - let good_data = vec![1u8, 2, 3, 4, 5]; - let data: Vec = vec![]; - assert_eq!(0x1 as *const u8, data.as_ptr()); - let addr = good_data.as_ptr() as *const _ as u64; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: good_data.len() as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: dec_str_addr, + vm_addr: 2048, + len: dec_str_len as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: bytes_buf.as_ptr() as *const _ as u64, + vm_addr: 8196, + len: bytes_len as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: bytes_len_addr, + vm_addr: 96, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - let translated_data = translate_slice::( + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { remaining: 400 })); + let mut syscall = SyscallBigNumFromDecStr { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call( + 2048, + dec_str_len as u64, + 8196, + 96, + 8, &memory_mapping, - data.as_ptr() as u64, - 0, - &bpf_loader::id(), - true, + &mut result, + ); + result.unwrap(); + (neg_flag != 0, bytes_buf[0..bytes_len].to_vec()) + } + + #[test] + fn test_syscall_bignum_from_u32() { + let bn_20 = new_u32_bignum(20u32); + let bn = BigNum::from_u32(20u32).unwrap().to_vec(); + assert_eq!(bn_20, bn); + } + #[test] + fn test_syscall_bignum_from_dec_str() { + let (is_neg, result_vec) = new_dec_str_bignum(LONG_DEC_STRING); + assert!(!is_neg); + let bns = BigNum::from_dec_str(LONG_DEC_STRING).unwrap(); + let bns_vec = bns.as_ref().to_vec(); + assert_eq!(result_vec, bns_vec); + let (is_neg, result_vec) = new_dec_str_bignum(NEG_LONG_DEC_STRING); + assert!(is_neg); + let bns = BigNum::from_dec_str(NEG_LONG_DEC_STRING).unwrap(); + let bns_vec = bns.as_ref().to_vec(); + assert_eq!(result_vec, bns_vec); + } + + #[test] + fn test_syscall_bignum_add() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(5).unwrap(); + let bn_arg2 = BigNum::from_u32(258).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; + let memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &DEFAULT_CONFIG, ) .unwrap(); - assert_eq!(data, translated_data); - assert_eq!(0, translated_data.len()); - // u8 - let mut data = vec![1u8, 2, 3, 4, 5]; - let addr = data.as_ptr() as *const _ as u64; + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumAdd { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![1, 7]); + } + #[test] + fn test_syscall_bignum_sub() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(5).unwrap(); + let bn_arg2 = BigNum::from_u32(258).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: data.len() as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - let translated_data = translate_slice::( - &memory_mapping, - 100, - data.len() as u64, - &bpf_loader::id(), - true, + + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumSub { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![253]); + } + + #[test] + fn test_syscall_bignum_mul() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(5).unwrap(); + let bn_arg2 = BigNum::from_u32(5).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; + let memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &DEFAULT_CONFIG, ) .unwrap(); - assert_eq!(data, translated_data); - data[0] = 10; - assert_eq!(data, translated_data); - assert!(translate_slice::( - &memory_mapping, - data.as_ptr() as u64, - u64::MAX, - &bpf_loader::id(), - true, - ) - .is_err()); - assert!(translate_slice::( - &memory_mapping, - 100 - 1, - data.len() as u64, - &bpf_loader::id(), - true, - ) - .is_err()); + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumMul { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![25]); + } - // u64 - let mut data = vec![1u64, 2, 3, 4, 5]; - let addr = data.as_ptr() as *const _ as u64; + #[test] + fn test_syscall_bignum_div() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(300).unwrap(); + let bn_arg2 = BigNum::from_u32(10).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 96, - len: (data.len() * size_of::()) as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - let translated_data = translate_slice::( - &memory_mapping, - 96, - data.len() as u64, - &bpf_loader::id(), - true, - ) - .unwrap(); - assert_eq!(data, translated_data); - data[0] = 10; - assert_eq!(data, translated_data); - assert!( - translate_slice::(&memory_mapping, 96, u64::MAX, &bpf_loader::id(), true,) - .is_err() - ); - // Pubkeys - let mut data = vec![solana_sdk::pubkey::new_rand(); 5]; - let addr = data.as_ptr() as *const _ as u64; - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: (data.len() * std::mem::size_of::()) as u64, - vm_gap_shift: 63, - is_writable: false, - }], - &DEFAULT_CONFIG, - ) - .unwrap(); - let translated_data = translate_slice::( - &memory_mapping, - 100, - data.len() as u64, - &bpf_loader::id(), - true, - ) - .unwrap(); - assert_eq!(data, translated_data); - data[0] = solana_sdk::pubkey::new_rand(); // Both should point to same place - assert_eq!(data, translated_data); + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumDiv { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![30]); } - #[test] - fn test_translate_string_and_do() { - let string = "Gaggablaghblagh!"; - let addr = string.as_ptr() as *const _ as u64; + fn test_syscall_bignum_sqr() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + + let bn_arg1 = BigNum::from_u32(300).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() * 2); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 2048, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: string.len() as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - assert_eq!( - 42, - translate_string_and_do( - &memory_mapping, - 100, - string.len() as u64, - &bpf_loader::id(), - true, - &mut |string: &str| { - assert_eq!(string, "Gaggablaghblagh!"); - Ok(42) - } - ) - .unwrap() - ); - } - #[test] - #[should_panic(expected = "UserError(SyscallError(Abort))")] - fn test_syscall_abort() { - let memory_mapping = - MemoryMapping::new::(vec![MemoryRegion::default()], &DEFAULT_CONFIG) - .unwrap(); + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumSqr { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; let mut result: Result> = Ok(0); - SyscallAbort::call( - &mut SyscallAbort {}, - 0, - 0, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); + syscall.call(96, 2, 8192, 8, 16, &memory_mapping, &mut result); result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![1, 95, 144]); } - #[test] - #[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")] - fn test_syscall_sol_panic() { - let string = "Gaggablaghblagh!"; - let addr = string.as_ptr() as *const _ as u64; + fn test_syscall_bignum_exp() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(8).unwrap(); + let bn_arg2 = BigNum::from_u32(2).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: string.len() as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: string.len() as u64 - 1, - })); - let mut syscall_panic = SyscallPanic { - compute_meter, - loader_id: &bpf_loader::id(), - enforce_aligned_host_addrs: true, - }; - let mut result: Result> = Ok(0); - syscall_panic.call( - 100, - string.len() as u64, - 42, - 84, - 0, - &memory_mapping, - &mut result, - ); - assert_eq!( - Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) - ))), - result - ); - let compute_meter: Rc> = Rc::new(RefCell::new(MockComputeMeter { - remaining: string.len() as u64, + remaining: (20 + 20) as u64, })); - let mut syscall_panic = SyscallPanic { + let mut syscall = SyscallBigNumExp { + cost: 1, compute_meter, loader_id: &bpf_loader::id(), - enforce_aligned_host_addrs: true, }; let mut result: Result> = Ok(0); - syscall_panic.call( - 100, - string.len() as u64, - 42, - 84, - 0, - &memory_mapping, - &mut result, - ); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![64]); } #[test] - fn test_syscall_sol_log() { - let string = "Gaggablaghblagh!"; - let addr = string.as_ptr() as *const _ as u64; - - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining: 1000000 })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_log = SyscallLog { - compute_meter, - logger, - loader_id: &bpf_loader::id(), - enforce_aligned_host_addrs: true, + fn test_syscall_bignum_mod_sqr() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(11).unwrap(); + let bn_arg2 = BigNum::from_u32(7).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: string.len() as u64, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 8192, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: 48, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - result.unwrap(); - assert_eq!(log.borrow().len(), 1); - assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!"); - - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 101, // AccessViolation - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - assert_access_violation!(result, 101, string.len() as u64); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64 * 2, // AccessViolation - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - assert_access_violation!(result, 100, string.len() as u64 * 2); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - let compute_meter: Rc> = Rc::new(RefCell::new(MockComputeMeter { - remaining: (string.len() as u64 * 2) - 1, + remaining: (20 + 20) as u64, })); - let logger: Rc> = Rc::new(RefCell::new(MockLogger { log })); - let mut syscall_sol_log = SyscallLog { + let mut syscall = SyscallBigNumModSqr { + cost: 1, compute_meter, - logger, loader_id: &bpf_loader::id(), - enforce_aligned_host_addrs: true, }; let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); + syscall.call(96, 3, 8192, 8, 16, &memory_mapping, &mut result); result.unwrap(); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - assert_eq!( - Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) - ))), - result - ); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![2]); } #[test] - fn test_syscall_sol_log_u64() { + fn test_syscall_bignum_mod_exp() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(300).unwrap(); + let bn_arg2 = BigNum::from_u32(11).unwrap(); + let bn_arg3 = BigNum::from_u32(7).unwrap(); + + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let arg3 = bn_arg3.as_ref().to_vec(); + let neg_flags = vec![ + bn_arg1.is_negative() as u8, + bn_arg2.is_negative() as u8, + bn_arg3.is_negative() as u8, + ]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_arg3 = ArgSlice { + addr: 4096, + len: arg3.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 8192, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_arg3, mock_neg_flags]; + let memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg3.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: arg3.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 8192, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 16_384, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: adds_array.len() as u64 * 16, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &DEFAULT_CONFIG, + ) + .unwrap(); + let compute_meter: Rc> = Rc::new(RefCell::new(MockComputeMeter { - remaining: std::u64::MAX, - })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_log_u64 = SyscallLogU64 { - cost: 0, + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumModExp { + cost: 1, compute_meter, - logger, + loader_id: &bpf_loader::id(), }; - let memory_mapping = MemoryMapping::new::(vec![], &DEFAULT_CONFIG).unwrap(); - let mut result: Result> = Ok(0); - syscall_sol_log_u64.call(1, 2, 3, 4, 5, &memory_mapping, &mut result); + syscall.call( + 96, + adds_array.len() as u64, + 16_384, + 8, + 16, + &memory_mapping, + &mut result, + ); result.unwrap(); - - assert_eq!(log.borrow().len(), 1); - assert_eq!(log.borrow()[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5"); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![6]); } - #[test] - fn test_syscall_sol_pubkey() { - let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap(); - let addr = &pubkey.as_ref()[0] as *const _ as u64; - - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining: 2 })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_pubkey = SyscallLogPubkey { - cost: 1, - compute_meter, - logger, - loader_id: &bpf_loader::id(), - enforce_aligned_host_addrs: true, + fn test_syscall_bignum_mod_mul() { + #[allow(dead_code)] + struct ArgSlice { + pub addr: u64, + pub len: usize, + } + let bn_arg1 = BigNum::from_u32(300).unwrap(); + let bn_arg2 = BigNum::from_u32(11).unwrap(); + let bn_arg3 = BigNum::from_u32(7).unwrap(); + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let arg3 = bn_arg3.as_ref().to_vec(); + let neg_flags = vec![ + bn_arg1.is_negative() as u8, + bn_arg2.is_negative() as u8, + bn_arg3.is_negative() as u8, + ]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_arg3 = ArgSlice { + addr: 4096, + len: arg3.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 8192, + len: neg_flags.len(), }; + let adds_array = [mock_arg1, mock_arg2, mock_arg3, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: addr, - vm_addr: 100, - len: 32, - vm_gap_shift: 63, - is_writable: false, - }], + vec![ + MemoryRegion { + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg3.as_ptr() as *const _ as u64, + vm_addr: 4096, + len: arg3.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, + vm_addr: 8192, + len: neg_flags.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_vec.as_mut_ptr() as u64, + vm_addr: 16_384, + len: result_vec.capacity() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: adds_array.as_ptr() as *const _ as u64, + vm_addr: 96, + len: adds_array.len() as u64 * 16, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + ], &DEFAULT_CONFIG, ) .unwrap(); + let compute_meter: Rc> = + Rc::new(RefCell::new(MockComputeMeter { + remaining: (20 + 20) as u64, + })); + let mut syscall = SyscallBigNumModMul { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; let mut result: Result> = Ok(0); - syscall_sol_pubkey.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - assert_eq!(log.borrow().len(), 1); - assert_eq!( - log.borrow()[0], - "Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN" - ); - let mut result: Result> = Ok(0); - syscall_sol_pubkey.call( - 101, // AccessViolation - 32, - 0, - 0, - 0, + syscall.call( + 96, + adds_array.len() as u64, + 16_384, + 8, + 16, &memory_mapping, &mut result, ); - assert_access_violation!(result, 101, 32); - let mut result: Result> = Ok(0); - syscall_sol_pubkey.call(100, 32, 0, 0, 0, &memory_mapping, &mut result); - assert_eq!( - Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) - ))), - result - ); - } - - #[test] - fn test_syscall_sol_alloc_free() { - // large alloc - { - let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion::new_from_slice( - heap.as_slice(), - MM_HEAP_START, - 0, - true, - )], - &DEFAULT_CONFIG, - ) - .unwrap(); - let mut syscall = SyscallAllocFree { - aligned: true, - allocator: BpfAllocator::new(heap, MM_HEAP_START), - }; - let mut result: Result> = Ok(0); - syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_ne!(result.unwrap(), 0); - let mut result: Result> = Ok(0); - syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_eq!(result.unwrap(), 0); - let mut result: Result> = Ok(0); - syscall.call(u64::MAX, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_eq!(result.unwrap(), 0); - } - // many small unaligned allocs - { - let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion::new_from_slice( - heap.as_slice(), - MM_HEAP_START, - 0, - true, - )], - &DEFAULT_CONFIG, - ) - .unwrap(); - let mut syscall = SyscallAllocFree { - aligned: false, - allocator: BpfAllocator::new(heap, MM_HEAP_START), - }; - for _ in 0..100 { - let mut result: Result> = Ok(0); - syscall.call(1, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_ne!(result.unwrap(), 0); - } - let mut result: Result> = Ok(0); - syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_eq!(result.unwrap(), 0); - } - // many small aligned allocs - { - let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion::new_from_slice( - heap.as_slice(), - MM_HEAP_START, - 0, - true, - )], - &DEFAULT_CONFIG, - ) - .unwrap(); - let mut syscall = SyscallAllocFree { - aligned: true, - allocator: BpfAllocator::new(heap, MM_HEAP_START), - }; - for _ in 0..12 { - let mut result: Result> = Ok(0); - syscall.call(1, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_ne!(result.unwrap(), 0); - } - let mut result: Result> = Ok(0); - syscall.call(100, 0, 0, 0, 0, &memory_mapping, &mut result); - assert_eq!(result.unwrap(), 0); - } - // aligned allocs - - fn check_alignment() { - let heap = AlignedMemory::new_with_size(100, HOST_ALIGN); - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion::new_from_slice( - heap.as_slice(), - MM_HEAP_START, - 0, - true, - )], - &DEFAULT_CONFIG, - ) - .unwrap(); - let mut syscall = SyscallAllocFree { - aligned: true, - allocator: BpfAllocator::new(heap, MM_HEAP_START), - }; - let mut result: Result> = Ok(0); - syscall.call( - size_of::() as u64, - 0, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - let address = result.unwrap(); - assert_ne!(address, 0); - assert_eq!((address as *const u8).align_offset(align_of::()), 0); - } - check_alignment::(); - check_alignment::(); - check_alignment::(); - check_alignment::(); - check_alignment::(); + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![3]); } #[test] - fn test_syscall_sha256() { - let bytes1 = "Gaggablaghblagh!"; - let bytes2 = "flurbos"; - + fn test_syscall_bignum_mod_inv() { #[allow(dead_code)] - struct MockSlice { + struct ArgSlice { pub addr: u64, pub len: usize, } - let mock_slice1 = MockSlice { - addr: 4096, - len: bytes1.len(), - }; - let mock_slice2 = MockSlice { - addr: 8192, - len: bytes2.len(), - }; - let bytes_to_hash = [mock_slice1, mock_slice2]; - let hash_result = [0; HASH_BYTES]; - let ro_len = bytes_to_hash.len() as u64; - let ro_va = 96; - let rw_va = 192; + let bn_arg1 = BigNum::from_u32(415).unwrap(); + let bn_arg2 = BigNum::from_u32(7).unwrap(); + + let arg1 = bn_arg1.as_ref().to_vec(); + let arg2 = bn_arg2.as_ref().to_vec(); + let neg_flags = vec![bn_arg1.is_negative() as u8, bn_arg2.is_negative() as u8]; + let mut result_vec = Vec::::with_capacity(arg1.len() + arg2.len()); + let mut out_size = result_vec.capacity() as u64; + let out_size_addr = &mut out_size as *mut _ as u64; + let mut neg_result = 0u64; + let neg_flag_addr = &mut neg_result as *mut _ as u64; + + let mock_arg1 = ArgSlice { + addr: 1024, + len: arg1.len(), + }; + let mock_arg2 = ArgSlice { + addr: 2048, + len: arg2.len(), + }; + let mock_neg_flags = ArgSlice { + addr: 4096, + len: neg_flags.len(), + }; + let adds_array = [mock_arg1, mock_arg2, mock_neg_flags]; let memory_mapping = MemoryMapping::new::( vec![ MemoryRegion { - host_addr: bytes1.as_ptr() as *const _ as u64, + host_addr: arg1.as_ptr() as *const _ as u64, + vm_addr: 1024, + len: arg1.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: arg2.as_ptr() as *const _ as u64, + vm_addr: 2048, + len: arg2.len() as u64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: neg_flags.as_ptr() as *const _ as u64, vm_addr: 4096, - len: bytes1.len() as u64, + len: neg_flags.len() as u64, vm_gap_shift: 63, is_writable: false, }, MemoryRegion { - host_addr: bytes2.as_ptr() as *const _ as u64, + host_addr: result_vec.as_mut_ptr() as u64, vm_addr: 8192, - len: bytes2.len() as u64, + len: result_vec.capacity() as u64, vm_gap_shift: 63, - is_writable: false, + is_writable: true, }, MemoryRegion { - host_addr: bytes_to_hash.as_ptr() as *const _ as u64, + host_addr: adds_array.as_ptr() as *const _ as u64, vm_addr: 96, - len: 32, + len: adds_array.len() as u64 * 16, vm_gap_shift: 63, is_writable: false, }, MemoryRegion { - host_addr: hash_result.as_ptr() as *const _ as u64, - vm_addr: rw_va, - len: HASH_BYTES as u64, + host_addr: neg_flag_addr, + vm_addr: 8, + len: std::mem::size_of::() as u64, + vm_gap_shift: 63, + is_writable: true, + }, + MemoryRegion { + host_addr: out_size_addr, + vm_addr: 16, + len: std::mem::size_of::() as u64, vm_gap_shift: 63, is_writable: true, }, @@ -2855,239 +5732,28 @@ mod tests { &DEFAULT_CONFIG, ) .unwrap(); + let compute_meter: Rc> = Rc::new(RefCell::new(MockComputeMeter { - remaining: (bytes1.len() + bytes2.len()) as u64, + remaining: (20 + 20) as u64, })); - let mut syscall = SyscallSha256 { - sha256_base_cost: 0, - sha256_byte_cost: 2, + let mut syscall = SyscallBigNumModInv { + cost: 1, compute_meter, - loader_id: &bpf_loader_deprecated::id(), - enforce_aligned_host_addrs: true, + loader_id: &bpf_loader::id(), }; - - let mut result: Result> = Ok(0); - syscall.call(ro_va, ro_len, rw_va, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - - let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes(); - assert_eq!(hash_result, hash_local); - let mut result: Result> = Ok(0); - syscall.call( - ro_va - 1, // AccessViolation - ro_len, - rw_va, - 0, - 0, - &memory_mapping, - &mut result, - ); - assert_access_violation!(result, ro_va - 1, ro_len); - let mut result: Result> = Ok(0); - syscall.call( - ro_va, - ro_len + 1, // AccessViolation - rw_va, - 0, - 0, - &memory_mapping, - &mut result, - ); - assert_access_violation!(result, ro_va, ro_len + 1); let mut result: Result> = Ok(0); syscall.call( - ro_va, - ro_len, - rw_va - 1, // AccessViolation - 0, - 0, + 96, + adds_array.len() as u64, + 8192, + 8, + 16, &memory_mapping, &mut result, ); - assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64); - - syscall.call(ro_va, ro_len, rw_va, 0, 0, &memory_mapping, &mut result); - assert_eq!( - Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) - ))), - result - ); - } - - #[test] - fn test_syscall_get_sysvar() { - // Test clock sysvar - { - let got_clock = Clock::default(); - let got_clock_va = 2048; - - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: &got_clock as *const _ as u64, - vm_addr: got_clock_va, - len: size_of::() as u64, - vm_gap_shift: 63, - is_writable: true, - }], - &DEFAULT_CONFIG, - ) - .unwrap(); - - let src_clock = Clock { - slot: 1, - epoch_start_timestamp: 2, - epoch: 3, - leader_schedule_epoch: 4, - unix_timestamp: 5, - }; - let mut invoke_context = MockInvokeContext::new(vec![]); - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_clock).unwrap(); - invoke_context - .sysvars - .push((sysvar::clock::id(), Some(Rc::new(data)))); - - let mut syscall = SyscallGetClockSysvar { - invoke_context: Rc::new(RefCell::new(&mut invoke_context)), - loader_id: &bpf_loader::id(), - }; - let mut result: Result> = Ok(0); - - syscall.call(got_clock_va, 0, 0, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - assert_eq!(got_clock, src_clock); - } - - // Test epoch_schedule sysvar - { - let got_epochschedule = EpochSchedule::default(); - let got_epochschedule_va = 2048; - - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: &got_epochschedule as *const _ as u64, - vm_addr: got_epochschedule_va, - len: size_of::() as u64, - vm_gap_shift: 63, - is_writable: true, - }], - &DEFAULT_CONFIG, - ) - .unwrap(); - - let src_epochschedule = EpochSchedule { - slots_per_epoch: 1, - leader_schedule_slot_offset: 2, - warmup: false, - first_normal_epoch: 3, - first_normal_slot: 4, - }; - let mut invoke_context = MockInvokeContext::new(vec![]); - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); - invoke_context - .sysvars - .push((sysvar::epoch_schedule::id(), Some(Rc::new(data)))); - - let mut syscall = SyscallGetEpochScheduleSysvar { - invoke_context: Rc::new(RefCell::new(&mut invoke_context)), - loader_id: &bpf_loader::id(), - }; - let mut result: Result> = Ok(0); - - syscall.call( - got_epochschedule_va, - 0, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - result.unwrap(); - assert_eq!(got_epochschedule, src_epochschedule); - } - - // Test fees sysvar - { - let got_fees = Fees::default(); - let got_fees_va = 2048; - - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: &got_fees as *const _ as u64, - vm_addr: got_fees_va, - len: size_of::() as u64, - vm_gap_shift: 63, - is_writable: true, - }], - &DEFAULT_CONFIG, - ) - .unwrap(); - - let src_fees = Fees { - fee_calculator: FeeCalculator { - lamports_per_signature: 1, - }, - }; - let mut invoke_context = MockInvokeContext::new(vec![]); - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_fees).unwrap(); - invoke_context - .sysvars - .push((sysvar::fees::id(), Some(Rc::new(data)))); - - let mut syscall = SyscallGetFeesSysvar { - invoke_context: Rc::new(RefCell::new(&mut invoke_context)), - loader_id: &bpf_loader::id(), - }; - let mut result: Result> = Ok(0); - - syscall.call(got_fees_va, 0, 0, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - assert_eq!(got_fees, src_fees); - } - - // Test rent sysvar - { - let got_rent = Rent::default(); - let got_rent_va = 2048; - - let memory_mapping = MemoryMapping::new::( - vec![MemoryRegion { - host_addr: &got_rent as *const _ as u64, - vm_addr: got_rent_va, - len: size_of::() as u64, - vm_gap_shift: 63, - is_writable: true, - }], - &DEFAULT_CONFIG, - ) - .unwrap(); - - let src_rent = Rent { - lamports_per_byte_year: 1, - exemption_threshold: 2.0, - burn_percent: 3, - }; - let mut invoke_context = MockInvokeContext::new(vec![]); - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_rent).unwrap(); - invoke_context - .sysvars - .push((sysvar::rent::id(), Some(Rc::new(data)))); - - let mut syscall = SyscallGetRentSysvar { - invoke_context: Rc::new(RefCell::new(&mut invoke_context)), - loader_id: &bpf_loader::id(), - }; - let mut result: Result> = Ok(0); - - syscall.call(got_rent_va, 0, 0, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - assert_eq!(got_rent, src_rent); - } + result.unwrap(); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![4]); } } diff --git a/sdk/bpf/c/inc/solana_sdk.h b/sdk/bpf/c/inc/solana_sdk.h index bb24e758e46f65..3a1c221baefd37 100644 --- a/sdk/bpf/c/inc/solana_sdk.h +++ b/sdk/bpf/c/inc/solana_sdk.h @@ -526,6 +526,184 @@ uint64_t sol_try_find_program_address( uint8_t *bump_seed ); +/** + * BigNum sol_bignum_from_u32 + * + * @param out_ptr address of vector array (r/w) + * @param ptr_size size of bytes in ptr + * @param val_u32 unsigned 32 bit value to assign to the new object + */ +static uint64_t sol_bignum_from_u32( + const uint64_t *out_ptr, + const uint64_t out_size, + const uint64_t val_u32 +); +/** + * BigNum sol_bignum_from_dec_str + * + * @param in_dec_str_ptr address of decimal string (r/o) + * @param in_size size of bytes array (r/o) + * @param out_ptr address of vector array (r/w) + * @param out_size_ptr size of bytes in ptr (r/o) + * @param out_is_neg_flag_ptr if string was negative + */ +static uint64_t sol_bignum_from_dec_str( + const uint64_t *in_dec_str_ptr, + const uint64_t in_size, + const uint64_t *out_ptr, + const uint64_t *out_size_ptr, + const uint64_t *out_is_neg_flag_ptr +); + +/** + * BigNum sol_bignum_add + * Performs add and returns bignum for sum + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_add( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); +/** + * BigNum sol_bignum_sub + * Performs subtraction and returns bignum for difference + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_sub( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); +/** + * BigNum sol_bignum_mul + * Performs multiplication and returns bignum for product + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_mul( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); +/** + * BigNum sol_bignum_div + * Performs division and returns bignum for product + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_div( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + +/** + * BigNum sol_bignum_exp + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_exp( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + +/** + * BigNum sol_bignum_sqr + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ + +static uint64_t sol_bignum_sqr( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + +/** + * BigNum sol_bignum_mod_mul + * Performs (base * multiplier) % modulus and updates self_ptr BigNum + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_mod_mul( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + +/** + * BigNum sol_bignum_mod_inverse + * Performs (base * multiplier) % modulus + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_mod_inverse( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + +/** + * BigNum sol_bignum_mod_exp + * Performs base^exponent % modulus + * @param args_ptr pointer to array of arguments + * @param arg_count count of arrays in args_ptr + * @param result_ptr address of buffer for result, + * @param negative_ptr address to store if result is negative (true) + * @param outsize_ptr on in, contains size of result_ptr, on out it num bytes copied + */ +static uint64_t sol_bignum_mod_exp( + const uint64_t *args_ptr, + const uint64_t arg_count, + const uint64_t *result_ptr, + const uint64_t *negative_ptr, + const uint64_t *outsize_ptr +); + /** * Cross-program invocation * * @{ diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 3b42e9d14af6cf..22ca9c5700ef8f 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -35,6 +35,7 @@ thiserror = "1.0" [target.'cfg(not(target_arch = "bpf"))'.dependencies] blake3 = "0.3.7" curve25519-dalek = "2.1.0" +openssl = "0.10.32" rand = "0.7.0" solana-logger = { path = "../../logger", version = "=1.7.0" } diff --git a/sdk/program/src/bignum.rs b/sdk/program/src/bignum.rs new file mode 100644 index 00000000000000..60a259f4459c35 --- /dev/null +++ b/sdk/program/src/bignum.rs @@ -0,0 +1,784 @@ +//! @brief Solana Rust-based BigNumber for bpf-programs + +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use std::fmt; + +#[repr(C)] +#[derive( + BorshSerialize, + BorshDeserialize, + BorshSchema, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + AbiExample, +)] +pub struct BigNumber { + value: Vec, + negative: bool, +} + +// pub struct BigNumber(u64); + +impl BigNumber { + /// Returns a BigNumber with initial value of 0 + pub fn new() -> Self { + Self { + negative: false, + value: vec![0u8], + } + } + + /// Returns the size, in bytes, of BigNum. Typically used in + /// assessing buffer size needed to fetch the bytes for serialization, etc. + pub fn size_in_bytes(&self) -> usize { + self.value.len() + } + + /// Returns a BigNumber with initial value set to a u32 value + pub fn from_u32(val: u32) -> Self { + if val == 0 { + Self { + negative: false, + value: vec![0u8], + } + } else { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let value = BigNum::from_u32(val).unwrap().to_vec(); + Self { + negative: false, + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_from_u32(bytes_ptr: *mut u8, bytes_len: u64, val: u64) -> u64; + } + let vec_size = { + match val { + 0..=255 => 1, + 256..=65_535 => 2, + 65_536..=16_777_215 => 3, + _ => 4, + } + }; + let mut value = Vec::::with_capacity(vec_size); + unsafe { + sol_bignum_from_u32( + value.as_mut_ptr() as *mut _ as *mut u8, + vec_size as u64, + val as u64, + ); + value.set_len(vec_size); + } + Self { + negative: false, + value, + } + } + } + } + /// Returns a BigNumber from a decimal string + pub fn from_dec_str(string: &str) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let bn = BigNum::from_dec_str(string).unwrap(); + let value = bn.to_vec(); + Self { + negative: bn.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_from_dec_str( + in_dec_str_ptr: *const u64, + in_size: u64, + out_ptr: *mut u8, + out_size_ptr: *mut u64, + out_is_neg_flag_ptr: *mut u64, + ) -> u64; + } + let mut value = Vec::::with_capacity(string.len()); + let mut value_len = string.len() as u64; + let mut is_negative = 0u64; + unsafe { + sol_bignum_from_dec_str( + string.as_ptr() as *const _ as *const u64, + string.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut value_len as *mut _ as *mut u64, + &mut is_negative as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Flag indicating if negative or not + pub fn is_negative(&self) -> bool { + self.negative + } + + /// Returns an array of bytes of self + pub fn to_bytes(&self) -> &[u8] { + &self.value + } + + /// Add BigNumbers + pub fn add(&self, rhs: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&rhs.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res.checked_add(&my_num, &rhs_num).unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_add( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, rhs.negative as u8]; + let arg_array = vec![self.to_bytes(), rhs.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = std::cmp::max(self.value.len(), rhs.value.len()) + 1; + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_add( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + /// Subtract BigNumbers + pub fn sub(&self, rhs: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&rhs.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res.checked_sub(&my_num, &rhs_num).unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_sub( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, rhs.negative as u8]; + let arg_array = vec![self.to_bytes(), rhs.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = std::cmp::max(self.value.len(), rhs.value.len()) + 1; + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_sub( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Multiple BigNumbers + pub fn mul(&self, rhs: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&rhs.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .checked_mul(&my_num, &rhs_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_mul( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, rhs.negative as u8]; + let arg_array = vec![self.to_bytes(), rhs.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = self.value.len() + rhs.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_mul( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + /// Divide BigNumbers + pub fn div(&self, rhs: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&rhs.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .checked_div(&my_num, &rhs_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_div( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, rhs.negative as u8]; + let arg_array = vec![self.to_bytes(), rhs.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = self.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_div( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Square BigNumbers + pub fn sqr(&self) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .sqr(&my_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_sqr( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8]; + let arg_array = vec![self.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = self.value.len() * 2; + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + + unsafe { + sol_bignum_sqr( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Raise BigNumber to exponent + pub fn exp(&self, exponent: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&exponent.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .exp(&my_num, &rhs_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_exp( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, exponent.negative as u8]; + let arg_array = vec![self.to_bytes(), exponent.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = self.value.len() * exponent.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_exp( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + /// BigNumbers modulus square + pub fn mod_sqr(&self, modulus: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&modulus.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .mod_sqr(&my_num, &rhs_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_mod_sqr( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, modulus.negative as u8]; + let arg_array = vec![self.to_bytes(), modulus.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = modulus.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_mod_sqr( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Compute modular exponentiation (self ^ rhs mod order) and return the result + pub fn mod_exp(&self, exponent: &BigNumber, modulus: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let exp_num = BigNum::from_slice(&exponent.value).unwrap(); + let mod_num = BigNum::from_slice(&modulus.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .mod_exp( + &my_num, + &exp_num, + &mod_num, + &mut BigNumContext::new().unwrap(), + ) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_mod_exp( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (4) + let neg_values = vec![ + self.negative as u8, + exponent.negative as u8, + modulus.negative as u8, + ]; + let arg_array = vec![ + self.to_bytes(), + exponent.to_bytes(), + modulus.to_bytes(), + &neg_values, + ]; + // Setup the result information + let mut value_len = self.value.len() * exponent.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + + unsafe { + sol_bignum_mod_exp( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Mod multiplier (self * multiplier) % modulus + pub fn mod_mul(&self, multiplier: &BigNumber, modulus: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let mul_num = BigNum::from_slice(&multiplier.value).unwrap(); + let mod_num = BigNum::from_slice(&modulus.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .mod_mul( + &my_num, + &mul_num, + &mod_num, + &mut BigNumContext::new().unwrap(), + ) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_mod_mul( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (4) + let neg_values = vec![ + self.negative as u8, + multiplier.negative as u8, + modulus.negative as u8, + ]; + let arg_array = vec![ + self.to_bytes(), + multiplier.to_bytes(), + modulus.to_bytes(), + &neg_values, + ]; + // Setup the result information + let mut value_len = modulus.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + + unsafe { + sol_bignum_mod_mul( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Finds the inverse of modulus on self + pub fn mod_inv(&self, modulus: &BigNumber) -> Self { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::{BigNum, BigNumContext}; + let my_num = BigNum::from_slice(&self.value).unwrap(); + let rhs_num = BigNum::from_slice(&modulus.value).unwrap(); + let mut bn_res = BigNum::new().unwrap(); + bn_res + .mod_inverse(&my_num, &rhs_num, &mut BigNumContext::new().unwrap()) + .unwrap(); + let value = bn_res.as_ref().to_vec(); + Self { + negative: bn_res.is_negative(), + value, + } + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_mod_inv( + arg_address: *const u64, + arg_count: u64, + out_value_address: *mut u8, + out_neg_address: *mut u64, + out_size_address: *mut u64, + ) -> u64; + } + // Setup the argument array (3) + let neg_values = vec![self.negative as u8, modulus.negative as u8]; + let arg_array = vec![self.to_bytes(), modulus.to_bytes(), &neg_values]; + // Setup the result information + let mut value_len = modulus.value.len(); + let mut value = Vec::::with_capacity(value_len as usize); + let mut is_negative = 0u64; + unsafe { + sol_bignum_mod_inv( + arg_array.as_ptr() as *const _ as *const u64, + arg_array.len() as u64, + value.as_mut_ptr() as *mut _ as *mut u8, + &mut is_negative as *mut _ as *mut u64, + &mut value_len as *mut _ as *mut u64, + ); + value.set_len(value_len as usize); + } + Self { + negative: is_negative != 0, + value, + } + } + } + + /// Log a `BigNum` from a program + pub fn log(&self) { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let mut my_num = BigNum::from_slice(&self.value).unwrap(); + my_num.set_negative(self.negative); + crate::program_stubs::sol_log(&my_num.to_dec_str().unwrap()); + } + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_bignum_log( + bignum_addr: *const u64, + bignum_size: u64, + negative_set: u64, + ) -> u64; + } + unsafe { + sol_bignum_log( + self.value.as_ptr() as *const _ as *const u64, + self.value.len() as u64, + self.negative as u64, + ) + }; + } + } +} + +impl Default for BigNumber { + fn default() -> Self { + Self { + negative: false, + value: vec![0u8], + } + } +} +impl fmt::Debug for BigNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let braw = BigNum::from_slice(&self.value) + .unwrap() + .to_dec_str() + .unwrap(); + // let braw: &BigNum = unsafe { &*(self.0 as *mut BigNum) }; + write!(f, "{}", braw) + } + #[cfg(target_arch = "bpf")] + { + write!(f, "{}", 0) + } + } +} + +impl fmt::Display for BigNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(not(target_arch = "bpf"))] + { + use openssl::bn::BigNum; + let braw = BigNum::from_slice(&self.value) + .unwrap() + .to_dec_str() + .unwrap(); + // let braw: &BigNum = unsafe { &*(self.0 as *mut BigNum) }; + write!(f, "{}", braw) + } + #[cfg(target_arch = "bpf")] + { + write!(f, "{}", 0) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_bignumber_new() { + let bn1 = BigNumber::new(); + assert_eq!(bn1.to_bytes(), BigNumber::new().to_bytes()); + } + + #[test] + fn test_bignumber_clone() { + let bn_u = BigNumber::from_u32(11); + let bn_u2 = bn_u.clone(); + assert_eq!(bn_u2.to_bytes(), bn_u.to_bytes()); + } + + #[test] + fn test_bignumber_add() { + let bn_1 = BigNumber::from_u32(5); + let bn_2 = BigNumber::from_u32(5); + let add_res = bn_1.add(&bn_2); + assert_eq!(add_res.to_bytes(), BigNumber::from_u32(10).to_bytes()); + assert!(!add_res.is_negative()); + } +} diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 7b550008e1f6ae..b26c3c99a140af 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -6,6 +6,7 @@ extern crate self as solana_program; pub mod account_info; +pub mod bignum; pub mod borsh; pub mod bpf_loader; pub mod bpf_loader_deprecated; diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index aaccf5b0743c1d..a3436a3701c6e5 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -143,6 +143,10 @@ pub mod stake_program_v4 { solana_sdk::declare_id!("Dc7djyhP9aLfdq2zktpvskeAjpG56msCU1yexpxXiWZb"); } +pub mod bignum_syscall_enabled { + solana_sdk::declare_id!("EwdGiDPkwRCGBEFwxv3w2nXY76p2houWxwpJLT7nkMcY"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -180,6 +184,7 @@ lazy_static! { (keccak256_syscall_enabled::id(), "keccak256 syscall"), (stake_program_v4::id(), "solana_stake_program v4"), /*************** ADD NEW FEATURES HERE ***************/ + (bignum_syscall_enabled::id(), "bignum syscall"), ] .iter() .cloned() diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index c97dd9ac5b460c..ab098e531f4db8 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -181,6 +181,32 @@ pub struct BpfComputeBudget { pub cpi_bytes_per_unit: u64, /// Base number of compute units consumed to get a sysvar pub sysvar_base_cost: u64, + /// Base number of compute units consumed to call BigNum new from u32 value + pub bignum_from_u32_base_cost: u64, + /// Base number of compute units consumed to call BigNum new from a decimal string + pub bignum_from_dec_str_base_cost: u64, + /// Incremental number of units consumed to drop/dealloc BigNum (based on bytes) + pub bignum_mod_exp_base_cost: u64, + /// Number of compute units consumed by logging a `BigNum` + pub bignum_log_cost: u64, + /// Number of compute units consumed by BigNum add + pub bignum_add_cost: u64, + /// Number of compute units consumed by BigNum subtract + pub bignum_sub_cost: u64, + /// Number of compute units consumed by BigNum mutliply + pub bignum_mul_cost: u64, + /// Number of compute units consumed by BigNum divide + pub bignum_div_cost: u64, + /// Number of compute units consumed by BigNum exp + pub bignum_exp_cost: u64, + /// Number of compute units consumed by BigNum sqr + pub bignum_sqr_cost: u64, + /// Number of compute units consumed by BigNum mod_sqr + pub bignum_mod_sqr_cost: u64, + /// Number of compute units consumed by BigNum mod_mul + pub bignum_mod_mul_cost: u64, + /// Number of compute units consumed by BigNum mod_inv + pub bignum_mod_inv_cost: u64, } impl Default for BpfComputeBudget { fn default() -> Self { @@ -204,6 +230,19 @@ impl BpfComputeBudget { max_cpi_instruction_size: 1280, // IPv6 Min MTU size cpi_bytes_per_unit: 250, // ~50MB at 200,000 units sysvar_base_cost: 100, + bignum_from_u32_base_cost: 100, + bignum_from_dec_str_base_cost: 100, + bignum_mod_exp_base_cost: 100, + bignum_log_cost: 100, + bignum_add_cost: 30, + bignum_sub_cost: 30, + bignum_mul_cost: 60, + bignum_div_cost: 60, + bignum_exp_cost: 60, + bignum_sqr_cost: 30, + bignum_mod_sqr_cost: 60, + bignum_mod_mul_cost: 90, + bignum_mod_inv_cost: 90, } } }