diff --git a/Cargo.lock b/Cargo.lock index 2ed71fd23fe318..a51429ed2d366e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1176,7 +1176,7 @@ version = "3.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1676e1daadfd216bda88d3a6fedd1bf53b829a085f5cc4d81c6f3054f50ef983" dependencies = [ - "num-bigint", + "num-bigint 0.3.1", "num-traits", "proc-macro2 1.0.24", "quote 1.0.6", @@ -2527,6 +2527,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.2" @@ -2624,15 +2635,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 +2655,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.57" +version = "0.9.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" +checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd" dependencies = [ "autocfg 1.0.0", "cc", @@ -4084,6 +4095,7 @@ dependencies = [ "log 0.4.11", "num-derive", "num-traits", + "openssl", "rand 0.7.3", "rand_core 0.6.2", "rustversion", @@ -4958,8 +4970,10 @@ dependencies = [ "itertools", "lazy_static", "log 0.4.11", + "num-bigint 0.4.0", "num-derive", "num-traits", + "openssl", "rand 0.7.3", "rustc_version", "rustversion", @@ -5508,9 +5522,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcec120278017a67e2dd98494dfdd8e565f53f1d05ab558d1656c369c5dd95e" +checksum = "debbc13545a1d972955a4fd3014e7c9d6d81da16c3626ee5f64bf3aa619548f8" dependencies = [ "byteorder", "combine", diff --git a/accounts-bench/src/main.rs b/accounts-bench/src/main.rs index e23be3ce71b35c..01524b07135d0a 100644 --- a/accounts-bench/src/main.rs +++ b/accounts-bench/src/main.rs @@ -6,7 +6,8 @@ use rayon::prelude::*; use solana_measure::measure::Measure; use solana_runtime::{ accounts::{create_test_accounts, update_accounts_bench, Accounts}, - accounts_index::{AccountSecondaryIndexes, Ancestors}, + accounts_index::AccountSecondaryIndexes, + ancestors::Ancestors, }; use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey}; use std::{env, fs, path::PathBuf}; diff --git a/cli/Cargo.toml b/cli/Cargo.toml index cf8592507e2e8b..33fcc45144bbca 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -38,7 +38,7 @@ solana-config-program = { path = "../programs/config", version = "=1.7.0" } solana-faucet = { path = "../faucet", version = "=1.7.0" } solana-logger = { path = "../logger", version = "=1.7.0" } solana-net-utils = { path = "../net-utils", version = "=1.7.0" } -solana_rbpf = "=0.2.8" +solana_rbpf = "=0.2.9" solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.0" } solana-sdk = { path = "../sdk", version = "=1.7.0" } solana-stake-program = { path = "../programs/stake", version = "=1.7.0" } diff --git a/core/src/cluster_info.rs b/core/src/cluster_info.rs index 5393ef6651b004..78e15fd3adb477 100644 --- a/core/src/cluster_info.rs +++ b/core/src/cluster_info.rs @@ -2564,7 +2564,7 @@ impl ClusterInfo { thread_pool: &ThreadPool, recycler: &PacketsRecycler, response_sender: &PacketSender, - stakes: HashMap, + stakes: &HashMap, feature_set: Option<&FeatureSet>, epoch_time_ms: u64, should_check_duplicate_instance: bool, @@ -2637,13 +2637,13 @@ impl ClusterInfo { self.stats .packets_received_prune_messages_count .add_relaxed(prune_messages.len() as u64); - let require_stake_for_gossip = self.require_stake_for_gossip(feature_set, &stakes); + let require_stake_for_gossip = self.require_stake_for_gossip(feature_set, stakes); if require_stake_for_gossip { for (_, data) in &mut pull_responses { - retain_staked(data, &stakes); + retain_staked(data, stakes); } for (_, data) in &mut push_messages { - retain_staked(data, &stakes); + retain_staked(data, stakes); } pull_responses.retain(|(_, data)| !data.is_empty()); push_messages.retain(|(_, data)| !data.is_empty()); @@ -2654,18 +2654,18 @@ impl ClusterInfo { push_messages, thread_pool, recycler, - &stakes, + stakes, response_sender, require_stake_for_gossip, ); - self.handle_batch_pull_responses(pull_responses, thread_pool, &stakes, epoch_time_ms); - self.trim_crds_table(CRDS_UNIQUE_PUBKEY_CAPACITY, &stakes); + self.handle_batch_pull_responses(pull_responses, thread_pool, stakes, epoch_time_ms); + self.trim_crds_table(CRDS_UNIQUE_PUBKEY_CAPACITY, stakes); self.handle_batch_pong_messages(pong_messages, Instant::now()); self.handle_batch_pull_requests( pull_requests, thread_pool, recycler, - &stakes, + stakes, response_sender, require_stake_for_gossip, ); @@ -2684,6 +2684,7 @@ impl ClusterInfo { should_check_duplicate_instance: bool, ) -> Result<()> { const RECV_TIMEOUT: Duration = Duration::from_secs(1); + const SUBMIT_GOSSIP_STATS_INTERVAL: Duration = Duration::from_secs(2); let packets: Vec<_> = requests_receiver.recv_timeout(RECV_TIMEOUT)?.packets.into(); let mut packets = VecDeque::from(packets); while let Ok(packet) = requests_receiver.try_recv() { @@ -2714,13 +2715,13 @@ impl ClusterInfo { thread_pool, recycler, response_sender, - stakes, + &stakes, feature_set.as_deref(), epoch_time_ms, should_check_duplicate_instance, )?; - if last_print.elapsed().as_millis() > 2000 { - submit_gossip_stats(&self.stats, &self.gossip); + if last_print.elapsed() > SUBMIT_GOSSIP_STATS_INTERVAL { + submit_gossip_stats(&self.stats, &self.gossip, &stakes); *last_print = Instant::now(); } Ok(()) diff --git a/core/src/cluster_info_metrics.rs b/core/src/cluster_info_metrics.rs index 1250824560502c..2bdb5e00c3be1d 100644 --- a/core/src/cluster_info_metrics.rs +++ b/core/src/cluster_info_metrics.rs @@ -1,6 +1,8 @@ use crate::crds_gossip::CrdsGossip; use solana_measure::measure::Measure; +use solana_sdk::pubkey::Pubkey; use std::{ + collections::HashMap, sync::{ atomic::{AtomicU64, Ordering}, RwLock, @@ -116,15 +118,21 @@ pub(crate) struct GossipStats { pub(crate) tvu_peers: Counter, } -pub(crate) fn submit_gossip_stats(stats: &GossipStats, gossip: &RwLock) { - let (table_size, purged_values_size, failed_inserts_size) = { +pub(crate) fn submit_gossip_stats( + stats: &GossipStats, + gossip: &RwLock, + stakes: &HashMap, +) { + let (table_size, num_nodes, purged_values_size, failed_inserts_size) = { let gossip = gossip.read().unwrap(); ( gossip.crds.len(), + gossip.crds.num_nodes(), gossip.pull.purged_values.len(), gossip.pull.failed_inserts.len(), ) }; + let num_nodes_staked = stakes.values().filter(|stake| **stake > 0).count(); datapoint_info!( "cluster_info_stats", ("entrypoint", stats.entrypoint.clear(), i64), @@ -142,6 +150,8 @@ pub(crate) fn submit_gossip_stats(stats: &GossipStats, gossip: &RwLock Ordering { - self.id.cmp(&other.id) - } -} - -impl PartialOrd for ContactInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for ContactInfo { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for ContactInfo {} - #[macro_export] macro_rules! socketaddr { ($ip:expr, $port:expr) => { std::net::SocketAddr::from((std::net::Ipv4Addr::from($ip), $port)) }; ($str:expr) => {{ - let a: std::net::SocketAddr = $str.parse().unwrap(); - a + $str.parse::().unwrap() }}; } #[macro_export] diff --git a/core/src/crds.rs b/core/src/crds.rs index 4af80a7e5426c1..c5e231ae30f296 100644 --- a/core/src/crds.rs +++ b/core/src/crds.rs @@ -19,7 +19,7 @@ //! CrdsValue enums. //! //! Merge strategy is implemented in: -//! impl PartialOrd for VersionedCrdsValue +//! fn overrides(value: &CrdsValue, other: &VersionedCrdsValue) -> bool //! //! A value is updated to a new version if the labels match, and the value //! wallclock is later, or the value hash is greater. @@ -66,8 +66,6 @@ pub enum CrdsError { } /// This structure stores some local metadata associated with the CrdsValue -/// The implementation of PartialOrd ensures that the "highest" version is always picked to be -/// stored in the Crds #[derive(PartialEq, Debug, Clone)] pub struct VersionedCrdsValue { /// Ordinal index indicating insert order. diff --git a/core/src/crds_gossip_push.rs b/core/src/crds_gossip_push.rs index 7cf01776c11810..f0b3b95e85e451 100644 --- a/core/src/crds_gossip_push.rs +++ b/core/src/crds_gossip_push.rs @@ -753,36 +753,32 @@ mod test { #[test] fn test_personalized_push_messages() { let now = timestamp(); + let mut rng = rand::thread_rng(); let mut crds = Crds::default(); let mut push = CrdsGossipPush::default(); - let peer_1 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); - assert_eq!(crds.insert(peer_1.clone(), now), Ok(None)); - let peer_2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); - assert_eq!(crds.insert(peer_2.clone(), now), Ok(None)); - let peer_3 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - now, - ))); + let peers: Vec<_> = vec![0, 0, now] + .into_iter() + .map(|wallclock| { + let mut peer = ContactInfo::new_rand(&mut rng, /*pubkey=*/ None); + peer.wallclock = wallclock; + CrdsValue::new_unsigned(CrdsData::ContactInfo(peer)) + }) + .collect(); + assert_eq!(crds.insert(peers[0].clone(), now), Ok(None)); + assert_eq!(crds.insert(peers[1].clone(), now), Ok(None)); assert_eq!( - push.process_push_message(&mut crds, &Pubkey::default(), peer_3.clone(), now), + push.process_push_message(&mut crds, &Pubkey::default(), peers[2].clone(), now), Ok(None) ); push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1); // push 3's contact info to 1 and 2 and 3 - let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &peer_3.pubkey(), - 0, - ))); - let mut expected = HashMap::new(); - expected.insert(peer_1.pubkey(), vec![new_msg.clone()]); - expected.insert(peer_2.pubkey(), vec![new_msg]); + let expected: HashMap<_, _> = vec![ + (peers[0].pubkey(), vec![peers[2].clone()]), + (peers[1].pubkey(), vec![peers[2].clone()]), + ] + .into_iter() + .collect(); assert_eq!(push.active_set.len(), 3); assert_eq!(push.new_push_messages(&crds, now), expected); } diff --git a/docs/src/cli/conventions.md b/docs/src/cli/conventions.md index 87521d12d98a31..4c73f21808371e 100644 --- a/docs/src/cli/conventions.md +++ b/docs/src/cli/conventions.md @@ -46,7 +46,7 @@ on your wallet type. #### Paper Wallet In a paper wallet, the keypair is securely derived from the seed words and -optional passphrase you entered when the wallet was create. To use a paper +optional passphrase you entered when the wallet was created. To use a paper wallet keypair anywhere the `` text is shown in examples or help documents, enter the uri scheme `prompt://` and the program will prompt you to enter your seed words when you run the command. diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 9e73d4a6c07128..3c01146c6635e0 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -859,7 +859,7 @@ version = "3.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d52ff39419d3e16961ecfb9e32f5042bdaacf9a4cc553d2d688057117bae49b" dependencies = [ - "num-bigint", + "num-bigint 0.3.2", "num-traits", "proc-macro2 1.0.24", "quote 1.0.6", @@ -1748,6 +1748,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.2.5" @@ -2762,6 +2773,7 @@ dependencies = [ "log", "num-derive 0.3.0", "num-traits", + "openssl", "rand_core 0.6.2", "sha3", "solana-measure", @@ -2816,6 +2828,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" @@ -3358,8 +3377,10 @@ dependencies = [ "itertools 0.9.0", "lazy_static", "log", + "num-bigint 0.4.0", "num-derive 0.3.0", "num-traits", + "openssl", "rand 0.7.3", "rustc_version", "rustversion", @@ -3637,9 +3658,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcec120278017a67e2dd98494dfdd8e565f53f1d05ab558d1656c369c5dd95e" +checksum = "debbc13545a1d972955a4fd3014e7c9d6d81da16c3626ee5f64bf3aa619548f8" dependencies = [ "byteorder 1.3.4", "combine", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index 284c28b490a23c..efcf4e0112c7d7 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -30,7 +30,7 @@ solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.7.0" } solana-cli-output = { path = "../../cli-output", version = "=1.7.0" } solana-logger = { path = "../../logger", version = "=1.7.0" } solana-measure = { path = "../../measure", version = "=1.7.0" } -solana_rbpf = "=0.2.8" +solana_rbpf = "=0.2.9" solana-runtime = { path = "../../runtime", version = "=1.7.0" } solana-sdk = { path = "../../sdk", version = "=1.7.0" } solana-transaction-status = { path = "../../transaction-status", version = "=1.7.0" } @@ -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..db7c28af835882 --- /dev/null +++ b/programs/bpf/rust/bignum/src/lib.rs @@ -0,0 +1,129 @@ +//! @brief BigNumber Syscall test + +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 7dd5244640991d..745f103ec70893 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -442,6 +442,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), @@ -1244,11 +1245,11 @@ fn assert_instruction_count() { ("alloc", 1137), ("bpf_to_bpf", 13), ("multiple_static", 8), - ("noop", 45), - ("noop++", 45), + ("noop", 5), + ("noop++", 5), ("relative_call", 10), - ("sanity", 175), - ("sanity++", 177), + ("sanity", 169), + ("sanity++", 168), ("sha", 694), ("struct_pass", 8), ("struct_ret", 22), @@ -1257,19 +1258,19 @@ fn assert_instruction_count() { #[cfg(feature = "bpf_rust")] { programs.extend_from_slice(&[ - ("solana_bpf_rust_128bit", 572), - ("solana_bpf_rust_alloc", 8906), - ("solana_bpf_rust_custom_heap", 516), + ("solana_bpf_rust_128bit", 584), + ("solana_bpf_rust_alloc", 4967), + ("solana_bpf_rust_custom_heap", 365), ("solana_bpf_rust_dep_crate", 2), - ("solana_bpf_rust_external_spend", 498), - ("solana_bpf_rust_iter", 724), - ("solana_bpf_rust_many_args", 237), - ("solana_bpf_rust_mem", 2297), - ("solana_bpf_rust_noop", 472), + ("solana_bpf_rust_external_spend", 334), + ("solana_bpf_rust_iter", 8), + ("solana_bpf_rust_many_args", 189), + ("solana_bpf_rust_mem", 1665), + ("solana_bpf_rust_noop", 322), ("solana_bpf_rust_param_passing", 46), - ("solana_bpf_rust_rand", 475), - ("solana_bpf_rust_sanity", 894), - ("solana_bpf_rust_sha", 29099), + ("solana_bpf_rust_rand", 325), + ("solana_bpf_rust_sanity", 587), + ("solana_bpf_rust_sha", 22417), ]); } diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index a2c0515f123387..b9945ad56bc716 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -15,14 +15,16 @@ 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" } solana-runtime = { path = "../../runtime", version = "=1.7.0" } solana-sdk = { path = "../../sdk", version = "=1.7.0" } -solana_rbpf = "=0.2.8" +solana_rbpf = "=0.2.9" thiserror = "1.0" + [dev-dependencies] rand = "0.7.3" rustversion = "1.0.4" diff --git a/programs/bpf_loader/benches/serialization.rs b/programs/bpf_loader/benches/serialization.rs new file mode 100644 index 00000000000000..e99c953b496d4d --- /dev/null +++ b/programs/bpf_loader/benches/serialization.rs @@ -0,0 +1,141 @@ +#![feature(test)] + +extern crate test; + +use solana_bpf_loader_program::serialization::{ + serialize_parameters_aligned, serialize_parameters_unaligned, +}; +use solana_sdk::{ + account::{Account, AccountSharedData}, + bpf_loader, +}; +use solana_sdk::{keyed_account::KeyedAccount, pubkey::Pubkey}; +use std::cell::RefCell; +use test::Bencher; + +fn create_inputs() -> ( + Pubkey, + Vec, + Vec>, + Vec, +) { + let program_id = solana_sdk::pubkey::new_rand(); + let dup_key = solana_sdk::pubkey::new_rand(); + let dup_key2 = solana_sdk::pubkey::new_rand(); + let keys = vec![ + dup_key, + dup_key, + solana_sdk::pubkey::new_rand(), + solana_sdk::pubkey::new_rand(), + dup_key2, + dup_key2, + solana_sdk::pubkey::new_rand(), + solana_sdk::pubkey::new_rand(), + ]; + let accounts = vec![ + RefCell::new(AccountSharedData::from(Account { + lamports: 1, + data: vec![1u8, 2, 3, 4, 5], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + })), + // dup + RefCell::new(AccountSharedData::from(Account { + lamports: 1, + data: vec![1u8; 100000], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + })), + RefCell::new(AccountSharedData::from(Account { + lamports: 2, + data: vec![11u8; 100000], + owner: bpf_loader::id(), + executable: true, + rent_epoch: 200, + })), + RefCell::new(AccountSharedData::from(Account { + lamports: 3, + data: vec![], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 3100, + })), + RefCell::new(AccountSharedData::from(Account { + lamports: 4, + data: vec![1u8; 100000], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + })), + // dup + RefCell::new(AccountSharedData::from(Account { + lamports: 4, + data: vec![1u8; 1000000], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + })), + RefCell::new(AccountSharedData::from(Account { + lamports: 5, + data: vec![11u8; 10000], + owner: bpf_loader::id(), + executable: true, + rent_epoch: 200, + })), + RefCell::new(AccountSharedData::from(Account { + lamports: 6, + data: vec![], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 3100, + })), + ]; + + let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + + (program_id, keys, accounts, instruction_data) +} + +#[bench] +fn bench_serialize_unaligned(bencher: &mut Bencher) { + let (program_id, keys, accounts, instruction_data) = create_inputs(); + let keyed_accounts: Vec<_> = keys + .iter() + .zip(&accounts) + .enumerate() + .map(|(i, (key, account))| { + if i <= accounts.len() / 2 { + KeyedAccount::new_readonly(&key, false, &account) + } else { + KeyedAccount::new(&key, false, &account) + } + }) + .collect(); + bencher.iter(|| { + let _ = serialize_parameters_unaligned(&program_id, &keyed_accounts, &instruction_data) + .unwrap(); + }); +} + +#[bench] +fn bench_serialize_aligned(bencher: &mut Bencher) { + let (program_id, keys, accounts, instruction_data) = create_inputs(); + let keyed_accounts: Vec<_> = keys + .iter() + .zip(&accounts) + .enumerate() + .map(|(i, (key, account))| { + if i <= accounts.len() / 2 { + KeyedAccount::new_readonly(&key, false, &account) + } else { + KeyedAccount::new(&key, false, &account) + } + }) + .collect(); + bencher.iter(|| { + let _ = + serialize_parameters_aligned(&program_id, &keyed_accounts, &instruction_data).unwrap(); + }); +} diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 5b0ef1af7fab3d..864440ebe5e638 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -148,7 +148,7 @@ pub fn create_vm<'a>( parameter_bytes: &mut [u8], invoke_context: &'a mut dyn InvokeContext, ) -> Result, EbpfError> { - let heap = AlignedMemory::new(DEFAULT_HEAP_SIZE, HOST_ALIGN); + let heap = AlignedMemory::new_with_size(DEFAULT_HEAP_SIZE, HOST_ALIGN); let heap_region = MemoryRegion::new_from_slice(heap.as_slice(), MM_HEAP_START, 0, true); let mut vm = EbpfVm::new(program, parameter_bytes, &[heap_region])?; syscalls::bind_syscall_context_objects(loader_id, &mut vm, invoke_context, heap)?; diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index 79b8564a39d1d0..31683274e5950b 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -231,7 +231,7 @@ pub fn serialize_parameters_aligned( .map_err(|_| InstructionError::InvalidArgument)?; v.write_all(&keyed_account.try_account_ref()?.data()) .map_err(|_| InstructionError::InvalidArgument)?; - v.fill( + v.resize( MAX_PERMITTED_DATA_INCREASE + (v.write_index() as *const u8).align_offset(align_of::()), 0, diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 456c400f27433c..135b703e2de36e 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, @@ -74,7 +75,32 @@ 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 { EbpfError::UserError(error.into()) @@ -151,6 +177,22 @@ 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)?; + 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 +310,125 @@ pub fn bind_syscall_context_objects<'a>( }), ); + vm.bind_syscall_context_object( + Box::new(SyscallBigNumFromU32 { + cost: bpf_compute_budget.bignum_from_u32_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumFromDecStr { + cost: bpf_compute_budget.bignum_from_dec_str_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + + vm.bind_syscall_context_object( + Box::new(SyscallBigNumModExp { + cost: bpf_compute_budget.bignum_mod_exp_base_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + + vm.bind_syscall_context_object( + Box::new(SyscallBigNumLog { + cost: bpf_compute_budget.bignum_log_cost, + compute_meter: invoke_context.get_compute_meter(), + logger: invoke_context.get_logger(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumAdd { + cost: bpf_compute_budget.bignum_add_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumSub { + cost: bpf_compute_budget.bignum_sub_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumMul { + cost: bpf_compute_budget.bignum_mul_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumDiv { + cost: bpf_compute_budget.bignum_div_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumExp { + cost: bpf_compute_budget.bignum_exp_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumSqr { + cost: bpf_compute_budget.bignum_sqr_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumModSqr { + cost: bpf_compute_budget.bignum_mod_sqr_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumModMul { + cost: bpf_compute_budget.bignum_mod_mul_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + vm.bind_syscall_context_object( + Box::new(SyscallBigNumModInv { + cost: bpf_compute_budget.bignum_mod_inv_cost, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + None, + )?; + + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&keccak256_syscall_enabled::id()), + Box::new(SyscallKeccak256 { + base_cost: bpf_compute_budget.sha256_base_cost, + byte_cost: bpf_compute_budget.sha256_byte_cost, + compute_meter: invoke_context.get_compute_meter(), + 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)); @@ -949,142 +1110,6 @@ impl<'a> SyscallObject for SyscallSha256<'a> { *result = Ok(0); } } - -fn get_sysvar( - id: &Pubkey, - var_addr: u64, - loader_id: &Pubkey, - memory_mapping: &MemoryMapping, - invoke_context: Rc>, -) -> Result> { - let mut invoke_context = invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed)?; - - invoke_context.get_compute_meter().consume( - invoke_context.get_bpf_compute_budget().sysvar_base_cost + size_of::() as u64, - )?; - let var = translate_type_mut::( - memory_mapping, - var_addr, - loader_id, - invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()), - )?; - - let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| { - ic_msg!(invoke_context, "Unable to get Sysvar {}", id); - SyscallError::InstructionError(InstructionError::UnsupportedSysvar) - })?; - - *var = bincode::deserialize(&sysvar_data).map_err(|e| { - ic_msg!(invoke_context, "Unable to get Sysvar {}: {:?}", id, e); - SyscallError::InstructionError(InstructionError::UnsupportedSysvar) - })?; - - Ok(SUCCESS) -} - -/// Get a Clock sysvar -struct SyscallGetClockSysvar<'a> { - invoke_context: Rc>, - loader_id: &'a Pubkey, -} -impl<'a> SyscallObject for SyscallGetClockSysvar<'a> { - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &MemoryMapping, - result: &mut Result>, - ) { - *result = get_sysvar::( - &sysvar::clock::id(), - var_addr, - self.loader_id, - memory_mapping, - self.invoke_context.clone(), - ); - } -} -/// Get a EpochSchedule sysvar -struct SyscallGetEpochScheduleSysvar<'a> { - invoke_context: Rc>, - loader_id: &'a Pubkey, -} -impl<'a> SyscallObject for SyscallGetEpochScheduleSysvar<'a> { - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &MemoryMapping, - result: &mut Result>, - ) { - *result = get_sysvar::( - &sysvar::epoch_schedule::id(), - var_addr, - self.loader_id, - memory_mapping, - self.invoke_context.clone(), - ); - } -} -/// Get a Fees sysvar -struct SyscallGetFeesSysvar<'a> { - invoke_context: Rc>, - loader_id: &'a Pubkey, -} -impl<'a> SyscallObject for SyscallGetFeesSysvar<'a> { - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &MemoryMapping, - result: &mut Result>, - ) { - *result = get_sysvar::( - &sysvar::fees::id(), - var_addr, - self.loader_id, - memory_mapping, - self.invoke_context.clone(), - ); - } -} -/// Get a Rent sysvar -struct SyscallGetRentSysvar<'a> { - invoke_context: Rc>, - loader_id: &'a Pubkey, -} -impl<'a> SyscallObject for SyscallGetRentSysvar<'a> { - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &MemoryMapping, - result: &mut Result>, - ) { - *result = get_sysvar::( - &sysvar::rent::id(), - var_addr, - self.loader_id, - memory_mapping, - self.invoke_context.clone(), - ); - } -} - // Keccak256 pub struct SyscallKeccak256<'a> { base_cost: u64, @@ -1144,1718 +1169,4547 @@ impl<'a> SyscallObject for SyscallKeccak256<'a> { } } -// Cross-program invocation syscalls - -struct AccountReferences<'a> { - lamports: &'a mut u64, - owner: &'a mut Pubkey, - data: &'a mut [u8], - vm_data_addr: u64, - ref_to_len_in_vm: &'a mut u64, - serialized_len_ptr: &'a mut u64, +/// 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 + }; } -type TranslatedAccount<'a> = ( - Rc>, - Option>, -); -type TranslatedAccounts<'a> = ( - Vec>>, - Vec>>, -); -/// Implemented by language specific data structure translators -trait SyscallInvokeSigned<'a> { - fn get_context_mut(&self) -> Result, EbpfError>; - fn get_context(&self) -> Result, EbpfError>; - fn translate_instruction( - &self, - addr: u64, - memory_mapping: &MemoryMapping, - enforce_aligned_host_addrs: bool, - ) -> Result>; - fn translate_accounts( - &self, - account_keys: &[Pubkey], - caller_write_privileges: &[bool], - program_account_index: usize, - account_infos_addr: u64, - account_infos_len: u64, - memory_mapping: &MemoryMapping, - ) -> Result, EbpfError>; - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &MemoryMapping, - enforce_aligned_host_addrs: bool, - ) -> Result, EbpfError>; -} - -/// Cross-program invocation called from Rust -pub struct SyscallInvokeSignedRust<'a> { - invoke_context: Rc>, +struct SyscallBigNumFromU32<'a> { + cost: u64, + compute_meter: Rc>, loader_id: &'a Pubkey, } -impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { - fn get_context_mut(&self) -> Result, EbpfError> { - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - fn get_context(&self) -> Result, EbpfError> { - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - fn translate_instruction( - &self, - addr: u64, +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, - enforce_aligned_host_addrs: bool, - ) -> Result> { - let ix = translate_type::( - memory_mapping, - addr, - self.loader_id, - enforce_aligned_host_addrs, - )?; - - check_instruction_size( - ix.accounts.len(), - ix.data.len(), - &self.invoke_context.borrow(), - )?; - - let accounts = translate_slice::( - memory_mapping, - ix.accounts.as_ptr() as u64, - ix.accounts.len() as u64, - self.loader_id, - enforce_aligned_host_addrs, - )? - .to_vec(); - let data = translate_slice::( - memory_mapping, - ix.data.as_ptr() as u64, - ix.data.len() as u64, - self.loader_id, - enforce_aligned_host_addrs, - )? - .to_vec(); - Ok(Instruction { - program_id: ix.program_id, - accounts, - data, - }) + 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) + } } - - fn translate_accounts( - &self, - account_keys: &[Pubkey], - caller_write_privileges: &[bool], - program_account_index: usize, - account_infos_addr: u64, - account_infos_len: u64, +} +/// 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, EbpfError> { - let invoke_context = self.invoke_context.borrow(); - let enforce_aligned_host_addrs = - invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); - - let account_infos = translate_slice::( - memory_mapping, - account_infos_addr, - account_infos_len, - self.loader_id, - enforce_aligned_host_addrs, - )?; - check_account_infos(account_infos.len(), &invoke_context)?; - let account_info_keys = account_infos - .iter() - .map(|account_info| { - translate_type::( - memory_mapping, - account_info.key as *const _ as u64, - self.loader_id, - enforce_aligned_host_addrs, - ) - }) - .collect::, EbpfError>>()?; - - let translate = |account_info: &AccountInfo, - invoke_context: &Ref<&mut dyn InvokeContext>| { - // Translate the account from user space + result: &mut Result>, + ) { + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(in_size as f64)), + result + ); - let lamports = { - // Double translate lamports out of RefCell - let ptr = translate_type::( - memory_mapping, - account_info.lamports.as_ptr() as u64, - self.loader_id, - enforce_aligned_host_addrs, - )?; - translate_type_mut::( - memory_mapping, - *ptr, - self.loader_id, - enforce_aligned_host_addrs, - )? - }; - let owner = translate_type_mut::( + // Get the string and convert to BigNum + let in_dec_raw = question_mark!( + translate_slice::( memory_mapping, - account_info.owner as *const _ as u64, + in_dec_str_addr, + in_size, self.loader_id, - enforce_aligned_host_addrs, - )?; - - let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { - // Double translate data out of RefCell - let data = *translate_type::<&[u8]>( - memory_mapping, - account_info.data.as_ptr() as *const _ as u64, - self.loader_id, - enforce_aligned_host_addrs, - )?; + 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 + ); - if invoke_context.is_feature_active(&cpi_data_cost::id()) { - invoke_context.get_compute_meter().consume( - data.len() as u64 - / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, - )?; + // 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) + } + } +} - let translated = translate( - memory_mapping, - AccessType::Store, - unsafe { (account_info.data.as_ptr() as *const u64).offset(1) as u64 }, - 8, - )? as *mut u64; - let ref_to_len_in_vm = unsafe { &mut *translated }; - let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) }; - let serialized_len_ptr = translate_type_mut::( +/// 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, - ref_of_len_in_input_buffer as *const _ as u64, + arg.as_ptr() as u64, + arg.len() as u64, + loader_id, + true + ), + result + )) + .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) + } +} + +/// 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, - enforce_aligned_host_addrs, - )?; - let vm_data_addr = data.as_ptr() as u64; - ( + &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, - vm_data_addr, - data.len() as u64, + out_result_addr, + *out_size, self.loader_id, - enforce_aligned_host_addrs, - )?, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - ) - }; - - Ok(( - Rc::new(RefCell::new(AccountSharedData::from(Account { - lamports: *lamports, - data: data.to_vec(), - executable: account_info.executable, - owner: *owner, - rent_epoch: account_info.rent_epoch, - }))), - Some(AccountReferences { - lamports, - owner, - data, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - }), - )) - }; - - get_translated_accounts( - account_keys, - caller_write_privileges, - program_account_index, - &account_info_keys, - account_infos, - &invoke_context, - translate, - ) + 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()), + } + } + } } +} - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, +/// 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, - enforce_aligned_host_addrs: bool, - ) -> Result, EbpfError> { - let mut signers = Vec::new(); - if signers_seeds_len > 0 { - let signers_seeds = translate_slice::<&[&[u8]]>( - memory_mapping, - signers_seeds_addr, - signers_seeds_len, - self.loader_id, - enforce_aligned_host_addrs, - )?; - if signers_seeds.len() > MAX_SIGNERS { - return Err(SyscallError::TooManySigners.into()); + 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()) } - for signer_seeds in signers_seeds.iter() { - let untranslated_seeds = translate_slice::<&[u8]>( - memory_mapping, - signer_seeds.as_ptr() as *const _ as u64, - signer_seeds.len() as u64, + true => { + let mut buffer = Vec::::new(); + let mut bytes = 0u64; + translate_bignum_args( + arguments_addr, + arguments_count, self.loader_id, - enforce_aligned_host_addrs, - )?; - if untranslated_seeds.len() > MAX_SEEDS { - return Err(SyscallError::InstructionError( - InstructionError::MaxSeedLengthExceeded, - ) - .into()); + &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()), } - let seeds = untranslated_seeds - .iter() - .map(|untranslated_seed| { - translate_slice::( - memory_mapping, - untranslated_seed.as_ptr() as *const _ as u64, - untranslated_seed.len() as u64, - self.loader_id, - enforce_aligned_host_addrs, - ) - }) - .collect::, EbpfError>>()?; - let signer = Pubkey::create_program_address(&seeds, program_id) - .map_err(SyscallError::BadSeeds)?; - signers.push(signer); } - Ok(signers) - } else { - Ok(vec![]) } } } -impl<'a> SyscallObject for SyscallInvokeSignedRust<'a> { +/// Subtract BigNums +struct SyscallBigNumMul<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumMul<'a> { fn call( &mut self, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = call( - self, - instruction_addr, - account_infos_addr, - account_infos_len, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - ); + 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()), + } + } + } } } - -/// Rust representation of C's SolInstruction -#[derive(Debug)] -struct SolInstruction { - program_id_addr: u64, - accounts_addr: u64, - accounts_len: usize, - data_addr: u64, - data_len: usize, -} - -/// Rust representation of C's SolAccountMeta -#[derive(Debug)] -struct SolAccountMeta { - pubkey_addr: u64, - is_writable: bool, - is_signer: bool, -} - -/// Rust representation of C's SolAccountInfo -#[derive(Debug)] -struct SolAccountInfo { - key_addr: u64, - lamports_addr: u64, - data_len: u64, - data_addr: u64, - owner_addr: u64, - rent_epoch: u64, - is_signer: bool, - is_writable: bool, - executable: bool, -} - -/// Rust representation of C's SolSignerSeed -#[derive(Debug)] -struct SolSignerSeedC { - addr: u64, - len: u64, +struct SyscallBigNumDiv<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, } - -/// Rust representation of C's SolSignerSeeds -#[derive(Debug)] -struct SolSignerSeedsC { - addr: u64, - len: u64, +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()), + } + } + } + } } - -/// Cross-program invocation called from C -pub struct SyscallInvokeSignedC<'a> { - invoke_context: Rc>, +struct SyscallBigNumSqr<'a> { + cost: u64, + compute_meter: Rc>, loader_id: &'a Pubkey, } -impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { - fn get_context_mut(&self) -> Result, EbpfError> { - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - fn get_context(&self) -> Result, EbpfError> { - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - - fn translate_instruction( - &self, - addr: u64, +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, - enforce_aligned_host_addrs: bool, - ) -> Result> { - let ix_c = translate_type::( - memory_mapping, - addr, - self.loader_id, - enforce_aligned_host_addrs, - )?; - - check_instruction_size( - ix_c.accounts_len, - ix_c.data_len, - &self.invoke_context.borrow(), - )?; - let program_id = translate_type::( - memory_mapping, - ix_c.program_id_addr, - self.loader_id, - enforce_aligned_host_addrs, - )?; - let meta_cs = translate_slice::( - memory_mapping, - ix_c.accounts_addr, - ix_c.accounts_len as u64, - self.loader_id, - enforce_aligned_host_addrs, - )?; - let data = translate_slice::( - memory_mapping, - ix_c.data_addr, - ix_c.data_len as u64, - self.loader_id, - enforce_aligned_host_addrs, - )? - .to_vec(); - let accounts = meta_cs - .iter() - .map(|meta_c| { - let pubkey = translate_type::( - memory_mapping, - meta_c.pubkey_addr, + 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, - enforce_aligned_host_addrs, - )?; - Ok(AccountMeta { - pubkey: *pubkey, - is_signer: meta_c.is_signer, - is_writable: meta_c.is_writable, - }) - }) - .collect::, EbpfError>>()?; + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); - Ok(Instruction { - program_id: *program_id, - accounts, - data, - }) + // 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()), + } + } + } } - - fn translate_accounts( - &self, - account_keys: &[Pubkey], - caller_write_privileges: &[bool], - program_account_index: usize, - account_infos_addr: u64, - account_infos_len: u64, +} +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, EbpfError> { - let invoke_context = self.invoke_context.borrow(); - let enforce_aligned_host_addrs = - invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); - - let account_infos = translate_slice::( - memory_mapping, - account_infos_addr, - account_infos_len, - self.loader_id, - enforce_aligned_host_addrs, - )?; - check_account_infos(account_infos.len(), &invoke_context)?; - let account_info_keys = account_infos - .iter() - .map(|account_info| { - translate_type::( - memory_mapping, - account_info.key_addr, + 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, - enforce_aligned_host_addrs, - ) - }) - .collect::, EbpfError>>()?; + &mut buffer, + &mut bytes, + &memory_mapping, + result, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); - let translate = |account_info: &SolAccountInfo, - invoke_context: &Ref<&mut dyn InvokeContext>| { - // Translate the account from user space + // 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 + ); - let lamports = translate_type_mut::( - memory_mapping, - account_info.lamports_addr, - self.loader_id, - enforce_aligned_host_addrs, - )?; - let owner = translate_type_mut::( - memory_mapping, - account_info.owner_addr, - self.loader_id, - enforce_aligned_host_addrs, - )?; - let vm_data_addr = account_info.data_addr; + // 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()), + } + } + } + } +} - if invoke_context.is_feature_active(&cpi_data_cost::id()) { - invoke_context.get_compute_meter().consume( - account_info.data_len - / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, - )?; +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 + ); - let data = translate_slice_mut::( - memory_mapping, - vm_data_addr, - account_info.data_len, - self.loader_id, - enforce_aligned_host_addrs, - )?; + // 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 + ); - let first_info_addr = &account_infos[0] as *const _ as u64; - let addr = &account_info.data_len as *const u64 as u64; - let vm_addr = account_infos_addr + (addr - first_info_addr); - let _ = translate( - memory_mapping, - AccessType::Store, - vm_addr, - size_of::() as u64, - )?; - let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) }; + // 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()), + } + } + } + } +} - let ref_of_len_in_input_buffer = - unsafe { (account_info.data_addr as *mut u8).offset(-8) }; - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - ref_of_len_in_input_buffer as *const _ as u64, - self.loader_id, - enforce_aligned_host_addrs, - )?; +/// 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, + ); + // Compute costs + question_mark!( + self.compute_meter + .consume(self.cost + calc_bignum_cost!(bytes as f64)), + result + ); - Ok(( - Rc::new(RefCell::new(AccountSharedData::from(Account { - lamports: *lamports, - data: data.to_vec(), - executable: account_info.executable, - owner: *owner, - rent_epoch: account_info.rent_epoch, - }))), - Some(AccountReferences { - lamports, - owner, - data, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - }), - )) - }; + // 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 + ); - get_translated_accounts( - account_keys, - caller_write_privileges, - program_account_index, - &account_info_keys, - account_infos, - &invoke_context, - translate, - ) + // 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()), + } + } + } } +} - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, +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, - enforce_aligned_host_addrs: bool, - ) -> Result, EbpfError> { - if signers_seeds_len > 0 { - let signers_seeds = translate_slice::( - memory_mapping, - signers_seeds_addr, - signers_seeds_len, - self.loader_id, - enforce_aligned_host_addrs, - )?; - if signers_seeds.len() > MAX_SIGNERS { - return Err(SyscallError::TooManySigners.into()); + 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()) } - Ok(signers_seeds - .iter() - .map(|signer_seeds| { - let seeds = translate_slice::( + 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, - signer_seeds.addr, - signer_seeds.len, + out_result_addr, + *out_size, self.loader_id, - enforce_aligned_host_addrs, - )?; - if seeds.len() > MAX_SEEDS { - return Err(SyscallError::InstructionError( - InstructionError::MaxSeedLengthExceeded, - ) - .into()); + 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) + } } - let seeds_bytes = seeds - .iter() - .map(|seed| { - translate_slice::( - memory_mapping, - seed.addr, - seed.len, - self.loader_id, - enforce_aligned_host_addrs, - ) - }) - .collect::, EbpfError>>()?; - Pubkey::create_program_address(&seeds_bytes, program_id) - .map_err(|err| SyscallError::BadSeeds(err).into()) - }) - .collect::, EbpfError>>()?) - } else { - Ok(vec![]) + Err(_) => *result = Err(SyscallError::BigNumberModExpError.into()), + } + } } } } -impl<'a> SyscallObject for SyscallInvokeSignedC<'a> { + +struct SyscallBigNumModInv<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallBigNumModInv<'a> { fn call( &mut self, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, + arguments_addr: u64, + arguments_count: u64, + out_result_addr: u64, + out_negative_addr: u64, + out_size_addr: u64, memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = call( - self, - instruction_addr, - account_infos_addr, - account_infos_len, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - ); - } -} + 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 + ); -fn get_translated_accounts<'a, T, F>( - account_keys: &[Pubkey], - caller_write_privileges: &[bool], - program_account_index: usize, - account_info_keys: &[&Pubkey], - account_infos: &[T], - invoke_context: &Ref<&mut dyn InvokeContext>, - do_translate: F, -) -> Result, EbpfError> -where - F: Fn(&T, &Ref<&mut dyn InvokeContext>) -> Result, EbpfError>, -{ - let mut accounts = Vec::with_capacity(account_keys.len()); - let mut refs = Vec::with_capacity(account_keys.len()); - for (i, ref account_key) in account_keys.iter().enumerate() { - let account = invoke_context.get_account(&account_key).ok_or_else(|| { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - SyscallError::InstructionError(InstructionError::MissingAccount) - })?; + // 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 + ); - if i == program_account_index - || account.borrow().executable() - || (invoke_context.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) - && !caller_write_privileges[i]) - { - // Use the known account - accounts.push(account); - refs.push(None); - } else if let Some(account_info) = - account_info_keys - .iter() - .zip(account_infos) - .find_map(|(key, account_info)| { - if key == account_key { - Some(account_info) - } else { - None + // 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) + } } - }) - { - let (account, account_ref) = do_translate(account_info, invoke_context)?; - accounts.push(account); - refs.push(account_ref); - } else { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); + Err(_) => *result = Err(SyscallError::BigNumberModExpError.into()), + } + } } } - - Ok((accounts, refs)) } - -fn check_instruction_size( - num_accounts: usize, - data_len: usize, - invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result<(), EbpfError> { - let size = num_accounts - .saturating_mul(size_of::()) - .saturating_add(data_len); - let max_size = invoke_context - .get_bpf_compute_budget() - .max_cpi_instruction_size; - if size > max_size { - return Err(SyscallError::InstructionTooLarge(size, max_size).into()); - } - Ok(()) +/// Log BigNum values +struct SyscallBigNumLog<'a> { + cost: u64, + compute_meter: Rc>, + logger: Rc>, + loader_id: &'a Pubkey, } - -fn check_account_infos( - len: usize, - invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result<(), EbpfError> { - if len * size_of::() - > invoke_context - .get_bpf_compute_budget() - .max_cpi_instruction_size - { - // Cap the number of account_infos a caller can pass to approximate - // maximum that accounts that could be passed in an instruction - return Err(SyscallError::TooManyAccounts.into()); - }; - Ok(()) +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); + } +} + +fn get_sysvar( + id: &Pubkey, + var_addr: u64, + loader_id: &Pubkey, + memory_mapping: &MemoryMapping, + invoke_context: Rc>, +) -> Result> { + let mut invoke_context = invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed)?; + + invoke_context.get_compute_meter().consume( + invoke_context.get_bpf_compute_budget().sysvar_base_cost + size_of::() as u64, + )?; + let var = translate_type_mut::( + memory_mapping, + var_addr, + loader_id, + invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()), + )?; + + let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| { + ic_msg!(invoke_context, "Unable to get Sysvar {}", id); + SyscallError::InstructionError(InstructionError::UnsupportedSysvar) + })?; + + *var = bincode::deserialize(&sysvar_data).map_err(|e| { + ic_msg!(invoke_context, "Unable to get Sysvar {}: {:?}", id, e); + SyscallError::InstructionError(InstructionError::UnsupportedSysvar) + })?; + + Ok(SUCCESS) +} + +/// Get a Clock sysvar +struct SyscallGetClockSysvar<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallGetClockSysvar<'a> { + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = get_sysvar::( + &sysvar::clock::id(), + var_addr, + self.loader_id, + memory_mapping, + self.invoke_context.clone(), + ); + } +} +/// Get a EpochSchedule sysvar +struct SyscallGetEpochScheduleSysvar<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallGetEpochScheduleSysvar<'a> { + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = get_sysvar::( + &sysvar::epoch_schedule::id(), + var_addr, + self.loader_id, + memory_mapping, + self.invoke_context.clone(), + ); + } +} +/// Get a Fees sysvar +struct SyscallGetFeesSysvar<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallGetFeesSysvar<'a> { + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = get_sysvar::( + &sysvar::fees::id(), + var_addr, + self.loader_id, + memory_mapping, + self.invoke_context.clone(), + ); + } +} +/// Get a Rent sysvar +struct SyscallGetRentSysvar<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallGetRentSysvar<'a> { + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = get_sysvar::( + &sysvar::rent::id(), + var_addr, + self.loader_id, + memory_mapping, + self.invoke_context.clone(), + ); + } +} + +// Cross-program invocation syscalls + +struct AccountReferences<'a> { + lamports: &'a mut u64, + owner: &'a mut Pubkey, + data: &'a mut [u8], + vm_data_addr: u64, + ref_to_len_in_vm: &'a mut u64, + serialized_len_ptr: &'a mut u64, +} +type TranslatedAccount<'a> = ( + Rc>, + Option>, +); +type TranslatedAccounts<'a> = ( + Vec>>, + Vec>>, +); + +/// Implemented by language specific data structure translators +trait SyscallInvokeSigned<'a> { + fn get_context_mut(&self) -> Result, EbpfError>; + fn get_context(&self) -> Result, EbpfError>; + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result>; + fn translate_accounts( + &self, + account_keys: &[Pubkey], + caller_write_privileges: &[bool], + program_account_index: usize, + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &MemoryMapping, + ) -> Result, EbpfError>; + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result, EbpfError>; +} + +/// Cross-program invocation called from Rust +pub struct SyscallInvokeSignedRust<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, } +impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { + fn get_context_mut(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + fn get_context(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result> { + let ix = translate_type::( + memory_mapping, + addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + check_instruction_size( + ix.accounts.len(), + ix.data.len(), + &self.invoke_context.borrow(), + )?; + + let accounts = translate_slice::( + memory_mapping, + ix.accounts.as_ptr() as u64, + ix.accounts.len() as u64, + self.loader_id, + enforce_aligned_host_addrs, + )? + .to_vec(); + let data = translate_slice::( + memory_mapping, + ix.data.as_ptr() as u64, + ix.data.len() as u64, + self.loader_id, + enforce_aligned_host_addrs, + )? + .to_vec(); + Ok(Instruction { + program_id: ix.program_id, + accounts, + data, + }) + } + + fn translate_accounts( + &self, + account_keys: &[Pubkey], + caller_write_privileges: &[bool], + program_account_index: usize, + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &MemoryMapping, + ) -> Result, EbpfError> { + let invoke_context = self.invoke_context.borrow(); + let enforce_aligned_host_addrs = + invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); + + let account_infos = translate_slice::( + memory_mapping, + account_infos_addr, + account_infos_len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + check_account_infos(account_infos.len(), &invoke_context)?; + let account_info_keys = account_infos + .iter() + .map(|account_info| { + translate_type::( + memory_mapping, + account_info.key as *const _ as u64, + self.loader_id, + enforce_aligned_host_addrs, + ) + }) + .collect::, EbpfError>>()?; + + let translate = |account_info: &AccountInfo, + invoke_context: &Ref<&mut dyn InvokeContext>| { + // Translate the account from user space + + let lamports = { + // Double translate lamports out of RefCell + let ptr = translate_type::( + memory_mapping, + account_info.lamports.as_ptr() as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + translate_type_mut::( + memory_mapping, + *ptr, + self.loader_id, + enforce_aligned_host_addrs, + )? + }; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner as *const _ as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { + // Double translate data out of RefCell + let data = *translate_type::<&[u8]>( + memory_mapping, + account_info.data.as_ptr() as *const _ as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + if invoke_context.is_feature_active(&cpi_data_cost::id()) { + invoke_context.get_compute_meter().consume( + data.len() as u64 + / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + )?; + } + + let translated = translate( + memory_mapping, + AccessType::Store, + unsafe { (account_info.data.as_ptr() as *const u64).offset(1) as u64 }, + 8, + )? as *mut u64; + let ref_to_len_in_vm = unsafe { &mut *translated }; + let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) }; + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer as *const _ as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + let vm_data_addr = data.as_ptr() as u64; + ( + translate_slice_mut::( + memory_mapping, + vm_data_addr, + data.len() as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + ) + }; + + Ok(( + Rc::new(RefCell::new(AccountSharedData::from(Account { + lamports: *lamports, + data: data.to_vec(), + executable: account_info.executable, + owner: *owner, + rent_epoch: account_info.rent_epoch, + }))), + Some(AccountReferences { + lamports, + owner, + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + }), + )) + }; + + get_translated_accounts( + account_keys, + caller_write_privileges, + program_account_index, + &account_info_keys, + account_infos, + &invoke_context, + translate, + ) + } + + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result, EbpfError> { + let mut signers = Vec::new(); + if signers_seeds_len > 0 { + let signers_seeds = translate_slice::<&[&[u8]]>( + memory_mapping, + signers_seeds_addr, + signers_seeds_len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } + for signer_seeds in signers_seeds.iter() { + let untranslated_seeds = translate_slice::<&[u8]>( + memory_mapping, + signer_seeds.as_ptr() as *const _ as u64, + signer_seeds.len() as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + if untranslated_seeds.len() > MAX_SEEDS { + return Err(SyscallError::InstructionError( + InstructionError::MaxSeedLengthExceeded, + ) + .into()); + } + let seeds = untranslated_seeds + .iter() + .map(|untranslated_seed| { + translate_slice::( + memory_mapping, + untranslated_seed.as_ptr() as *const _ as u64, + untranslated_seed.len() as u64, + self.loader_id, + enforce_aligned_host_addrs, + ) + }) + .collect::, EbpfError>>()?; + let signer = Pubkey::create_program_address(&seeds, program_id) + .map_err(SyscallError::BadSeeds)?; + signers.push(signer); + } + Ok(signers) + } else { + Ok(vec![]) + } + } +} +impl<'a> SyscallObject for SyscallInvokeSignedRust<'a> { + fn call( + &mut self, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = call( + self, + instruction_addr, + account_infos_addr, + account_infos_len, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + ); + } +} + +/// Rust representation of C's SolInstruction +#[derive(Debug)] +struct SolInstruction { + program_id_addr: u64, + accounts_addr: u64, + accounts_len: usize, + data_addr: u64, + data_len: usize, +} + +/// Rust representation of C's SolAccountMeta +#[derive(Debug)] +struct SolAccountMeta { + pubkey_addr: u64, + is_writable: bool, + is_signer: bool, +} + +/// Rust representation of C's SolAccountInfo +#[derive(Debug)] +struct SolAccountInfo { + key_addr: u64, + lamports_addr: u64, + data_len: u64, + data_addr: u64, + owner_addr: u64, + rent_epoch: u64, + is_signer: bool, + is_writable: bool, + executable: bool, +} + +/// Rust representation of C's SolSignerSeed +#[derive(Debug)] +struct SolSignerSeedC { + addr: u64, + len: u64, +} + +/// Rust representation of C's SolSignerSeeds +#[derive(Debug)] +struct SolSignerSeedsC { + addr: u64, + len: u64, +} + +/// Cross-program invocation called from C +pub struct SyscallInvokeSignedC<'a> { + invoke_context: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { + fn get_context_mut(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + fn get_context(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result> { + let ix_c = translate_type::( + memory_mapping, + addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + check_instruction_size( + ix_c.accounts_len, + ix_c.data_len, + &self.invoke_context.borrow(), + )?; + let program_id = translate_type::( + memory_mapping, + ix_c.program_id_addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + let meta_cs = translate_slice::( + memory_mapping, + ix_c.accounts_addr, + ix_c.accounts_len as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + let data = translate_slice::( + memory_mapping, + ix_c.data_addr, + ix_c.data_len as u64, + self.loader_id, + enforce_aligned_host_addrs, + )? + .to_vec(); + let accounts = meta_cs + .iter() + .map(|meta_c| { + let pubkey = translate_type::( + memory_mapping, + meta_c.pubkey_addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + Ok(AccountMeta { + pubkey: *pubkey, + is_signer: meta_c.is_signer, + is_writable: meta_c.is_writable, + }) + }) + .collect::, EbpfError>>()?; + + Ok(Instruction { + program_id: *program_id, + accounts, + data, + }) + } + + fn translate_accounts( + &self, + account_keys: &[Pubkey], + caller_write_privileges: &[bool], + program_account_index: usize, + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &MemoryMapping, + ) -> Result, EbpfError> { + let invoke_context = self.invoke_context.borrow(); + let enforce_aligned_host_addrs = + invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); + + let account_infos = translate_slice::( + memory_mapping, + account_infos_addr, + account_infos_len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + check_account_infos(account_infos.len(), &invoke_context)?; + let account_info_keys = account_infos + .iter() + .map(|account_info| { + translate_type::( + memory_mapping, + account_info.key_addr, + self.loader_id, + enforce_aligned_host_addrs, + ) + }) + .collect::, EbpfError>>()?; + + let translate = |account_info: &SolAccountInfo, + invoke_context: &Ref<&mut dyn InvokeContext>| { + // Translate the account from user space + + let lamports = translate_type_mut::( + memory_mapping, + account_info.lamports_addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner_addr, + self.loader_id, + enforce_aligned_host_addrs, + )?; + let vm_data_addr = account_info.data_addr; + + if invoke_context.is_feature_active(&cpi_data_cost::id()) { + invoke_context.get_compute_meter().consume( + account_info.data_len + / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + )?; + } + + let data = translate_slice_mut::( + memory_mapping, + vm_data_addr, + account_info.data_len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + let first_info_addr = &account_infos[0] as *const _ as u64; + let addr = &account_info.data_len as *const u64 as u64; + let vm_addr = account_infos_addr + (addr - first_info_addr); + let _ = translate( + memory_mapping, + AccessType::Store, + vm_addr, + size_of::() as u64, + )?; + let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) }; + + let ref_of_len_in_input_buffer = + unsafe { (account_info.data_addr as *mut u8).offset(-8) }; + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer as *const _ as u64, + self.loader_id, + enforce_aligned_host_addrs, + )?; + + Ok(( + Rc::new(RefCell::new(AccountSharedData::from(Account { + lamports: *lamports, + data: data.to_vec(), + executable: account_info.executable, + owner: *owner, + rent_epoch: account_info.rent_epoch, + }))), + Some(AccountReferences { + lamports, + owner, + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + }), + )) + }; + + get_translated_accounts( + account_keys, + caller_write_privileges, + program_account_index, + &account_info_keys, + account_infos, + &invoke_context, + translate, + ) + } + + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, + enforce_aligned_host_addrs: bool, + ) -> Result, EbpfError> { + if signers_seeds_len > 0 { + let signers_seeds = translate_slice::( + memory_mapping, + signers_seeds_addr, + signers_seeds_len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } + Ok(signers_seeds + .iter() + .map(|signer_seeds| { + let seeds = translate_slice::( + memory_mapping, + signer_seeds.addr, + signer_seeds.len, + self.loader_id, + enforce_aligned_host_addrs, + )?; + if seeds.len() > MAX_SEEDS { + return Err(SyscallError::InstructionError( + InstructionError::MaxSeedLengthExceeded, + ) + .into()); + } + let seeds_bytes = seeds + .iter() + .map(|seed| { + translate_slice::( + memory_mapping, + seed.addr, + seed.len, + self.loader_id, + enforce_aligned_host_addrs, + ) + }) + .collect::, EbpfError>>()?; + Pubkey::create_program_address(&seeds_bytes, program_id) + .map_err(|err| SyscallError::BadSeeds(err).into()) + }) + .collect::, EbpfError>>()?) + } else { + Ok(vec![]) + } + } +} +impl<'a> SyscallObject for SyscallInvokeSignedC<'a> { + fn call( + &mut self, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + *result = call( + self, + instruction_addr, + account_infos_addr, + account_infos_len, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + ); + } +} + +fn get_translated_accounts<'a, T, F>( + account_keys: &[Pubkey], + caller_write_privileges: &[bool], + program_account_index: usize, + account_info_keys: &[&Pubkey], + account_infos: &[T], + invoke_context: &Ref<&mut dyn InvokeContext>, + do_translate: F, +) -> Result, EbpfError> +where + F: Fn(&T, &Ref<&mut dyn InvokeContext>) -> Result, EbpfError>, +{ + let mut accounts = Vec::with_capacity(account_keys.len()); + let mut refs = Vec::with_capacity(account_keys.len()); + for (i, ref account_key) in account_keys.iter().enumerate() { + let account = invoke_context.get_account(&account_key).ok_or_else(|| { + ic_msg!( + invoke_context, + "Instruction references an unknown account {}", + account_key + ); + SyscallError::InstructionError(InstructionError::MissingAccount) + })?; + + if i == program_account_index + || account.borrow().executable() + || (invoke_context.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) + && !caller_write_privileges[i]) + { + // Use the known account + accounts.push(account); + refs.push(None); + } else if let Some(account_info) = + account_info_keys + .iter() + .zip(account_infos) + .find_map(|(key, account_info)| { + if key == account_key { + Some(account_info) + } else { + None + } + }) + { + let (account, account_ref) = do_translate(account_info, invoke_context)?; + accounts.push(account); + refs.push(account_ref); + } else { + ic_msg!( + invoke_context, + "Instruction references an unknown account {}", + account_key + ); + return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); + } + } + + Ok((accounts, refs)) +} + +fn check_instruction_size( + num_accounts: usize, + data_len: usize, + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result<(), EbpfError> { + let size = num_accounts + .saturating_mul(size_of::()) + .saturating_add(data_len); + let max_size = invoke_context + .get_bpf_compute_budget() + .max_cpi_instruction_size; + if size > max_size { + return Err(SyscallError::InstructionTooLarge(size, max_size).into()); + } + Ok(()) +} + +fn check_account_infos( + len: usize, + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result<(), EbpfError> { + if len * size_of::() + > invoke_context + .get_bpf_compute_budget() + .max_cpi_instruction_size + { + // Cap the number of account_infos a caller can pass to approximate + // maximum that accounts that could be passed in an instruction + return Err(SyscallError::TooManyAccounts.into()); + }; + Ok(()) +} + +fn check_authorized_program( + program_id: &Pubkey, + instruction_data: &[u8], + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result<(), EbpfError> { + if native_loader::check_id(program_id) + || bpf_loader::check_id(program_id) + || bpf_loader_deprecated::check_id(program_id) + || (bpf_loader_upgradeable::check_id(program_id) + && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data) + || (bpf_loader_upgradeable::is_set_authority_instruction(instruction_data) + && invoke_context + .is_feature_active(&set_upgrade_authority_via_cpi_enabled::id())))) + { + return Err(SyscallError::ProgramNotSupported(*program_id).into()); + } + Ok(()) +} + +#[allow(clippy::type_complexity)] +fn get_upgradeable_executable( + callee_program_id: &Pubkey, + program_account: &Rc>, + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result>)>, EbpfError> { + if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { + match program_account.borrow().state() { + Ok(UpgradeableLoaderState::Program { + programdata_address, + }) => { + if let Some(account) = invoke_context.get_account(&programdata_address) { + Ok(Some((programdata_address, account))) + } else { + ic_msg!( + invoke_context, + "Unknown upgradeable programdata account {}", + programdata_address, + ); + Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()) + } + } + _ => { + ic_msg!( + invoke_context, + "Invalid upgradeable program account {}", + callee_program_id, + ); + Err(SyscallError::InstructionError(InstructionError::InvalidAccountData).into()) + } + } + } else { + Ok(None) + } +} + +/// Call process instruction, common to both Rust and C +fn call<'a>( + syscall: &mut dyn SyscallInvokeSigned<'a>, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &MemoryMapping, +) -> Result> { + let ( + message, + executables, + accounts, + account_refs, + caller_write_privileges, + demote_sysvar_write_locks, + ) = { + let invoke_context = syscall.get_context()?; + + invoke_context + .get_compute_meter() + .consume(invoke_context.get_bpf_compute_budget().invoke_units)?; + + let enforce_aligned_host_addrs = + invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); + + let caller_program_id = invoke_context + .get_caller() + .map_err(SyscallError::InstructionError)?; + + // Translate and verify caller's data + + let instruction = syscall.translate_instruction( + instruction_addr, + &memory_mapping, + enforce_aligned_host_addrs, + )?; + let signers = syscall.translate_signers( + caller_program_id, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + enforce_aligned_host_addrs, + )?; + let keyed_account_refs = invoke_context + .get_keyed_accounts() + .map_err(SyscallError::InstructionError)? + .iter() + .collect::>(); + let (message, callee_program_id, callee_program_id_index) = + MessageProcessor::create_message( + &instruction, + &keyed_account_refs, + &signers, + &invoke_context, + ) + .map_err(SyscallError::InstructionError)?; + let caller_write_privileges = message + .account_keys + .iter() + .map(|key| { + if let Some(keyed_account) = keyed_account_refs + .iter() + .find(|keyed_account| key == keyed_account.unsigned_key()) + { + keyed_account.is_writable() + } else { + false + } + }) + .collect::>(); + check_authorized_program(&callee_program_id, &instruction.data, &invoke_context)?; + let (accounts, account_refs) = syscall.translate_accounts( + &message.account_keys, + &caller_write_privileges, + callee_program_id_index, + account_infos_addr, + account_infos_len, + memory_mapping, + )?; + + // Construct executables + + let program_account = accounts + .get(callee_program_id_index) + .ok_or_else(|| { + ic_msg!(invoke_context, "Unknown program {}", callee_program_id,); + SyscallError::InstructionError(InstructionError::MissingAccount) + })? + .clone(); + let programdata_executable = + get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?; + let mut executables = vec![(callee_program_id, program_account)]; + if let Some(executable) = programdata_executable { + executables.push(executable); + } + + // Record the instruction + + invoke_context.record_instruction(&instruction); + + ( + message, + executables, + accounts, + account_refs, + caller_write_privileges, + invoke_context.is_feature_active(&demote_sysvar_write_locks::id()), + ) + }; + + // Process instruction + + #[allow(clippy::deref_addrof)] + match MessageProcessor::process_cross_program_instruction( + &message, + &executables, + &accounts, + &caller_write_privileges, + *(&mut *(syscall.get_context_mut()?)), + ) { + Ok(()) => (), + Err(err) => { + return Err(SyscallError::InstructionError(err).into()); + } + } + + // Copy results back to caller + { + let invoke_context = syscall.get_context()?; + for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() { + let account = account.borrow(); + if let Some(mut account_ref) = account_ref { + if message.is_writable(i, demote_sysvar_write_locks) && !account.executable() { + *account_ref.lamports = account.lamports(); + *account_ref.owner = *account.owner(); + if account_ref.data.len() != account.data().len() { + if !account_ref.data.is_empty() { + // Only support for `CreateAccount` at this time. + // Need a way to limit total realloc size across multiple CPI calls + ic_msg!( + invoke_context, + "Inner instructions do not support realloc, only SystemProgram::CreateAccount", + ); + return Err(SyscallError::InstructionError( + InstructionError::InvalidRealloc, + ) + .into()); + } + if account.data().len() + > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE + { + ic_msg!( + invoke_context, + "SystemProgram::CreateAccount data size limited to {} in inner instructions", + MAX_PERMITTED_DATA_INCREASE + ); + return Err(SyscallError::InstructionError( + InstructionError::InvalidRealloc, + ) + .into()); + } + if invoke_context.is_feature_active(&update_data_on_realloc::id()) { + account_ref.data = translate_slice_mut::( + memory_mapping, + account_ref.vm_data_addr, + account.data().len() as u64, + &bpf_loader_deprecated::id(), // Don't care since it is byte aligned + true, + )?; + } else { + let _ = translate( + memory_mapping, + AccessType::Store, + account_ref.vm_data_addr, + account.data().len() as u64, + )?; + } + *account_ref.ref_to_len_in_vm = account.data().len() as u64; + *account_ref.serialized_len_ptr = account.data().len() as u64; + } + account_ref + .data + .copy_from_slice(&account.data()[0..account_ref.data.len()]); + } + } + } + } + + 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}, + }; + use std::{str::FromStr, vec}; + + 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, + 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(); -fn check_authorized_program( - program_id: &Pubkey, - instruction_data: &[u8], - invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result<(), EbpfError> { - if native_loader::check_id(program_id) - || bpf_loader::check_id(program_id) - || bpf_loader_deprecated::check_id(program_id) - || (bpf_loader_upgradeable::check_id(program_id) - && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data) - || (bpf_loader_upgradeable::is_set_authority_instruction(instruction_data) - && invoke_context - .is_feature_active(&set_upgrade_authority_via_cpi_enabled::id())))) - { - return Err(SyscallError::ProgramNotSupported(*program_id).into()); + 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"); } - Ok(()) -} -#[allow(clippy::type_complexity)] -fn get_upgradeable_executable( - callee_program_id: &Pubkey, - program_account: &Rc>, - invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result>)>, EbpfError> { - if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { - match program_account.borrow().state() { - Ok(UpgradeableLoaderState::Program { - programdata_address, - }) => { - if let Some(account) = invoke_context.get_account(&programdata_address) { - Ok(Some((programdata_address, account))) - } else { - ic_msg!( - invoke_context, - "Unknown upgradeable programdata account {}", - programdata_address, - ); - Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()) - } + #[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 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(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(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); } - _ => { - ic_msg!( - invoke_context, - "Invalid upgradeable program account {}", - callee_program_id, - ); - Err(SyscallError::InstructionError(InstructionError::InvalidAccountData).into()) + 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(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); } - } else { - Ok(None) - } -} + // aligned allocs -/// Call process instruction, common to both Rust and C -fn call<'a>( - syscall: &mut dyn SyscallInvokeSigned<'a>, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &MemoryMapping, -) -> Result> { - let ( - message, - executables, - accounts, - account_refs, - caller_write_privileges, - demote_sysvar_write_locks, - ) = { - let invoke_context = syscall.get_context()?; + fn check_alignment() { + let heap = AlignedMemory::new(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::(); + } - invoke_context - .get_compute_meter() - .consume(invoke_context.get_bpf_compute_budget().invoke_units)?; + #[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 enforce_aligned_host_addrs = - invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()); + let mut result: Result> = Ok(0); + syscall.call(ro_va, ro_len, rw_va, 0, 0, &memory_mapping, &mut result); + result.unwrap(); - let caller_program_id = invoke_context - .get_caller() - .map_err(SyscallError::InstructionError)?; + 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, + &memory_mapping, + &mut result, + ); + assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64); - // Translate and verify caller's data + 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 + ); + } - let instruction = syscall.translate_instruction( - instruction_addr, - &memory_mapping, - enforce_aligned_host_addrs, - )?; - let signers = syscall.translate_signers( - caller_program_id, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - enforce_aligned_host_addrs, - )?; - let keyed_account_refs = invoke_context - .get_keyed_accounts() - .map_err(SyscallError::InstructionError)? - .iter() - .collect::>(); - let (message, callee_program_id, callee_program_id_index) = - MessageProcessor::create_message( - &instruction, - &keyed_account_refs, - &signers, - &invoke_context, + #[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, ) - .map_err(SyscallError::InstructionError)?; - let caller_write_privileges = message - .account_keys - .iter() - .map(|key| { - if let Some(keyed_account) = keyed_account_refs - .iter() - .find(|keyed_account| key == keyed_account.unsigned_key()) - { - keyed_account.is_writable() - } else { - false - } - }) - .collect::>(); - check_authorized_program(&callee_program_id, &instruction.data, &invoke_context)?; - let (accounts, account_refs) = syscall.translate_accounts( - &message.account_keys, - &caller_write_privileges, - callee_program_id_index, - account_infos_addr, - account_infos_len, - memory_mapping, - )?; + .unwrap(); - // Construct executables + 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 program_account = accounts - .get(callee_program_id_index) - .ok_or_else(|| { - ic_msg!(invoke_context, "Unknown program {}", callee_program_id,); - SyscallError::InstructionError(InstructionError::MissingAccount) - })? - .clone(); - let programdata_executable = - get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?; - let mut executables = vec![(callee_program_id, program_account)]; - if let Some(executable) = programdata_executable { - executables.push(executable); + 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); } - // Record the instruction + // Test epoch_schedule sysvar + { + let got_epochschedule = EpochSchedule::default(); + let got_epochschedule_va = 2048; - invoke_context.record_instruction(&instruction); + 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(); - ( - message, - executables, - accounts, - account_refs, - caller_write_privileges, - invoke_context.is_feature_active(&demote_sysvar_write_locks::id()), - ) - }; + 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)))); - // Process instruction + let mut syscall = SyscallGetEpochScheduleSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); - #[allow(clippy::deref_addrof)] - match MessageProcessor::process_cross_program_instruction( - &message, - &executables, - &accounts, - &caller_write_privileges, - *(&mut *(syscall.get_context_mut()?)), - ) { - Ok(()) => (), - Err(err) => { - return Err(SyscallError::InstructionError(err).into()); + syscall.call( + got_epochschedule_va, + 0, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + assert_eq!(got_epochschedule, src_epochschedule); } - } - // Copy results back to caller - { - let invoke_context = syscall.get_context()?; - for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() { - let account = account.borrow(); - if let Some(mut account_ref) = account_ref { - if message.is_writable(i, demote_sysvar_write_locks) && !account.executable() { - *account_ref.lamports = account.lamports(); - *account_ref.owner = *account.owner(); - if account_ref.data.len() != account.data().len() { - if !account_ref.data.is_empty() { - // Only support for `CreateAccount` at this time. - // Need a way to limit total realloc size across multiple CPI calls - ic_msg!( - invoke_context, - "Inner instructions do not support realloc, only SystemProgram::CreateAccount", - ); - return Err(SyscallError::InstructionError( - InstructionError::InvalidRealloc, - ) - .into()); - } - if account.data().len() - > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE - { - ic_msg!( - invoke_context, - "SystemProgram::CreateAccount data size limited to {} in inner instructions", - MAX_PERMITTED_DATA_INCREASE - ); - return Err(SyscallError::InstructionError( - InstructionError::InvalidRealloc, - ) - .into()); - } - if invoke_context.is_feature_active(&update_data_on_realloc::id()) { - account_ref.data = translate_slice_mut::( - memory_mapping, - account_ref.vm_data_addr, - account.data().len() as u64, - &bpf_loader_deprecated::id(), // Don't care since it is byte aligned - true, - )?; - } else { - let _ = translate( - memory_mapping, - AccessType::Store, - account_ref.vm_data_addr, - account.data().len() as u64, - )?; - } - *account_ref.ref_to_len_in_vm = account.data().len() as u64; - *account_ref.serialized_len_ptr = account.data().len() as u64; - } - account_ref - .data - .copy_from_slice(&account.data()[0..account_ref.data.len()]); - } - } - } - } + // Test fees sysvar + { + let got_fees = Fees::default(); + let got_fees_va = 2048; - Ok(SUCCESS) -} + 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(); -#[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; + 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)))); - const DEFAULT_CONFIG: Config = Config { - max_call_depth: 20, - stack_frame_size: 4_096, - enable_instruction_meter: true, - enable_instruction_tracing: false, - }; + let mut syscall = SyscallGetFeesSysvar { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); - macro_rules! assert_access_violation { - ($result:expr, $va:expr, $len:expr) => { - match $result { - Err(EbpfError::AccessViolation(_, _, va, len, _)) if $va == va && len == len => (), - _ => panic!(), - } - }; - } + syscall.call(got_fees_va, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + assert_eq!(got_fees, src_fees); + } - #[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(); + // Test rent sysvar + { + let got_rent = Rent::default(); + let got_rent_va = 2048; - 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()) - } - } - } + 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(); - #[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, - 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); + 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)))); - // 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::( + 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); + } + } + + // 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, + 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, - ); - assert_eq!( - Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) - ))), - 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_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: 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, + remaining: (20 + 20) as u64, })); - let mut syscall_panic = SyscallPanic { + let mut syscall = SyscallBigNumModSqr { + 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![2]); } - #[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_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: 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: 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 = SyscallBigNumModExp { + cost: 1, + compute_meter, + loader_id: &bpf_loader::id(), + }; let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, + 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: 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, - ); + unsafe { result_vec.set_len(out_size as usize) }; + assert_eq!(result_vec, vec![6]); + } + #[test] + 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: 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: (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 = SyscallBigNumModMul { + 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, - ); - result.unwrap(); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 100, - string.len() as u64, - 0, - 0, - 0, + syscall.call( + 96, + adds_array.len() as u64, + 16_384, + 8, + 16, &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, + remaining: (20 + 20) as u64, })); - 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 { + let mut syscall = SyscallBigNumModExp { 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 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(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(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(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(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![6]); } #[test] - fn test_syscall_sha256() { - let bytes1 = "Gaggablaghblagh!"; - let bytes2 = "flurbos"; - - // lint warns field addr and len "never read" + 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, }, @@ -2863,239 +5717,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/runtime/benches/accounts.rs b/runtime/benches/accounts.rs index 99f60c670564e1..d7a27ad5a51727 100644 --- a/runtime/benches/accounts.rs +++ b/runtime/benches/accounts.rs @@ -8,7 +8,8 @@ use rand::Rng; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use solana_runtime::{ accounts::{create_test_accounts, AccountAddressFilter, Accounts}, - accounts_index::{AccountSecondaryIndexes, Ancestors}, + accounts_index::AccountSecondaryIndexes, + ancestors::Ancestors, bank::*, }; use solana_sdk::{ diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index d6223ac722acf1..43d264e4af90e9 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -2,7 +2,8 @@ use crate::{ accounts_db::{ AccountsDb, BankHashInfo, ErrorCounters, LoadHint, LoadedAccount, ScanStorageResult, }, - accounts_index::{AccountSecondaryIndexes, Ancestors, IndexKey}, + accounts_index::{AccountSecondaryIndexes, IndexKey}, + ancestors::Ancestors, bank::{ NonceRollbackFull, NonceRollbackInfo, TransactionCheckResult, TransactionExecutionResult, }, diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index e368208fe90e1a..0be550a69a1207 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -23,8 +23,9 @@ use crate::{ accounts_hash::{AccountsHash, CalculateHashIntermediate, HashStats, PreviousPass}, accounts_index::{ AccountIndexGetResult, AccountSecondaryIndexes, AccountsIndex, AccountsIndexRootsStats, - Ancestors, IndexKey, IsCached, SlotList, SlotSlice, ZeroLamport, + IndexKey, IsCached, SlotList, SlotSlice, ZeroLamport, }, + ancestors::Ancestors, append_vec::{AppendVec, StoredAccountMeta, StoredMeta, StoredMetaWriteVersion}, contains::Contains, read_only_accounts_cache::ReadOnlyAccountsCache, @@ -837,6 +838,7 @@ struct AccountsStats { last_store_report: AtomicU64, store_hash_accounts: AtomicU64, + calc_stored_meta: AtomicU64, store_accounts: AtomicU64, store_update_index: AtomicU64, store_handle_reclaims: AtomicU64, @@ -1055,13 +1057,17 @@ impl ShrinkStats { let last = self.last_report.load(Ordering::Relaxed); let now = solana_sdk::timing::timestamp(); + // last is initialized to 0 by ::default() + // thus, the first 'report' call would always log. + // Instead, the first call now initialializes 'last_report' to now. + let is_first_call = last == 0; let should_report = now.saturating_sub(last) > 1000 && self .last_report .compare_exchange(last, now, Ordering::Relaxed, Ordering::Relaxed) == Ok(last); - if should_report { + if !is_first_call && should_report { datapoint_info!( "shrink_stats", ( @@ -2170,12 +2176,22 @@ impl AccountsDb { num_candidates } - pub fn shrink_all_slots(&self) { - for slot in self.all_slots_in_storage() { - if self.caching_enabled { - self.shrink_slot_forced(slot); - } else { - self.do_shrink_slot_forced_v1(slot); + pub fn shrink_all_slots(&self, is_startup: bool) { + if is_startup && self.caching_enabled { + let slots = self.all_slots_in_storage(); + let chunk_size = std::cmp::max(slots.len() / 8, 1); // approximately 400k slots in a snapshot + slots.par_chunks(chunk_size).for_each(|slots| { + for slot in slots { + self.shrink_slot_forced(*slot); + } + }); + } else { + for slot in self.all_slots_in_storage() { + if self.caching_enabled { + self.shrink_slot_forced(slot); + } else { + self.do_shrink_slot_forced_v1(slot); + } } } } @@ -3800,6 +3816,7 @@ impl AccountsDb { mut write_version_producer: P, is_cached_store: bool, ) -> Vec { + let mut calc_stored_meta_time = Measure::start("calc_stored_meta"); let accounts_and_meta_to_store: Vec<_> = accounts .iter() .map(|(pubkey, account)| { @@ -3820,6 +3837,10 @@ impl AccountsDb { (meta, account) }) .collect(); + calc_stored_meta_time.stop(); + self.stats + .calc_stored_meta + .fetch_add(calc_stored_meta_time.as_us(), Ordering::Relaxed); if self.caching_enabled && is_cached_store { self.write_accounts_to_cache(slot, hashes, &accounts_and_meta_to_store) @@ -4743,6 +4764,11 @@ impl AccountsDb { read_only_cache_misses, i64 ), + ( + "calc_stored_meta_us", + self.stats.calc_stored_meta.swap(0, Ordering::Relaxed), + i64 + ), ); let recycle_stores = self.recycle_stores.read().unwrap(); @@ -8444,13 +8470,15 @@ pub mod tests { #[test] fn test_shrink_all_slots_none() { - let accounts = AccountsDb::new_single(); + for startup in &[false, true] { + let accounts = AccountsDb::new_single(); - for _ in 0..10 { - accounts.shrink_candidate_slots(); - } + for _ in 0..10 { + accounts.shrink_candidate_slots(); + } - accounts.shrink_all_slots(); + accounts.shrink_all_slots(*startup); + } } #[test] @@ -8531,68 +8559,70 @@ pub mod tests { fn test_shrink_stale_slots_processed() { solana_logger::setup(); - let accounts = AccountsDb::new_single(); + for startup in &[false, true] { + let accounts = AccountsDb::new_single(); - let pubkey_count = 100; - let pubkeys: Vec<_> = (0..pubkey_count) - .map(|_| solana_sdk::pubkey::new_rand()) - .collect(); + let pubkey_count = 100; + let pubkeys: Vec<_> = (0..pubkey_count) + .map(|_| solana_sdk::pubkey::new_rand()) + .collect(); - let some_lamport = 223; - let no_data = 0; - let owner = *AccountSharedData::default().owner(); + let some_lamport = 223; + let no_data = 0; + let owner = *AccountSharedData::default().owner(); - let account = AccountSharedData::new(some_lamport, no_data, &owner); + let account = AccountSharedData::new(some_lamport, no_data, &owner); - let mut current_slot = 0; + let mut current_slot = 0; - current_slot += 1; - for pubkey in &pubkeys { - accounts.store_uncached(current_slot, &[(&pubkey, &account)]); - } - let shrink_slot = current_slot; - accounts.get_accounts_delta_hash(current_slot); - accounts.add_root(current_slot); + current_slot += 1; + for pubkey in &pubkeys { + accounts.store_uncached(current_slot, &[(&pubkey, &account)]); + } + let shrink_slot = current_slot; + accounts.get_accounts_delta_hash(current_slot); + accounts.add_root(current_slot); - current_slot += 1; - let pubkey_count_after_shrink = 10; - let updated_pubkeys = &pubkeys[0..pubkey_count - pubkey_count_after_shrink]; + current_slot += 1; + let pubkey_count_after_shrink = 10; + let updated_pubkeys = &pubkeys[0..pubkey_count - pubkey_count_after_shrink]; - for pubkey in updated_pubkeys { - accounts.store_uncached(current_slot, &[(&pubkey, &account)]); - } - accounts.get_accounts_delta_hash(current_slot); - accounts.add_root(current_slot); + for pubkey in updated_pubkeys { + accounts.store_uncached(current_slot, &[(&pubkey, &account)]); + } + accounts.get_accounts_delta_hash(current_slot); + accounts.add_root(current_slot); - accounts.clean_accounts(None); + accounts.clean_accounts(None); - assert_eq!( - pubkey_count, - accounts.all_account_count_in_append_vec(shrink_slot) - ); - accounts.shrink_all_slots(); - assert_eq!( - pubkey_count_after_shrink, - accounts.all_account_count_in_append_vec(shrink_slot) - ); + assert_eq!( + pubkey_count, + accounts.all_account_count_in_append_vec(shrink_slot) + ); + accounts.shrink_all_slots(*startup); + assert_eq!( + pubkey_count_after_shrink, + accounts.all_account_count_in_append_vec(shrink_slot) + ); - let no_ancestors = Ancestors::default(); - accounts.update_accounts_hash(current_slot, &no_ancestors); - accounts - .verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300) - .unwrap(); + let no_ancestors = Ancestors::default(); + accounts.update_accounts_hash(current_slot, &no_ancestors); + accounts + .verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300) + .unwrap(); - let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot); - accounts - .verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300) - .unwrap(); + let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot); + accounts + .verify_bank_hash_and_lamports(current_slot, &no_ancestors, 22300) + .unwrap(); - // repeating should be no-op - accounts.shrink_all_slots(); - assert_eq!( - pubkey_count_after_shrink, - accounts.all_account_count_in_append_vec(shrink_slot) - ); + // repeating should be no-op + accounts.shrink_all_slots(*startup); + assert_eq!( + pubkey_count_after_shrink, + accounts.all_account_count_in_append_vec(shrink_slot) + ); + } } #[test] @@ -8647,7 +8677,7 @@ pub mod tests { ); // Now, do full-shrink. - accounts.shrink_all_slots(); + accounts.shrink_all_slots(false); assert_eq!( pubkey_count_after_shrink, accounts.all_account_count_in_append_vec(shrink_slot) @@ -8707,7 +8737,7 @@ pub mod tests { ); // Now, do full-shrink. - accounts.shrink_all_slots(); + accounts.shrink_all_slots(false); assert_eq!( pubkey_count_after_shrink, accounts.all_account_count_in_append_vec(shrink_slot) @@ -8909,7 +8939,7 @@ pub mod tests { } accounts.add_root(1); accounts.clean_accounts(None); - accounts.shrink_all_slots(); + accounts.shrink_all_slots(false); accounts.print_accounts_stats("post-shrink"); let num_stores = accounts.recycle_stores.read().unwrap().entry_count(); assert!(num_stores > 0); diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 9fe583450f899d..75fddab6d4fe90 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -1,4 +1,5 @@ use crate::{ + ancestors::Ancestors, contains::Contains, inline_spl_token_v2_0::{self, SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET}, secondary_index::*, @@ -15,7 +16,7 @@ use solana_sdk::{ use std::{ collections::{ btree_map::{self, BTreeMap}, - HashMap, HashSet, + HashSet, }, ops::{ Bound, @@ -32,7 +33,6 @@ pub const ITER_BATCH_SIZE: usize = 1000; pub type SlotList = Vec<(Slot, T)>; pub type SlotSlice<'s, T> = &'s [(Slot, T)]; -pub type Ancestors = HashMap; pub type RefCount = u64; pub type AccountMap = BTreeMap; @@ -179,6 +179,18 @@ impl WriteAccountMapEntry { &self.borrow_owned_entry_contents().ref_count } + // create an entry that is equivalent to this process: + // 1. new empty (refcount=0, slot_list={}) + // 2. update(slot, account_info) + // This code is called when the first entry [ie. (slot,account_info)] for a pubkey is inserted into the index. + pub fn new_entry_after_update(slot: Slot, account_info: &T) -> AccountMapEntry { + let ref_count = if account_info.is_cached() { 0 } else { 1 }; + Arc::new(AccountMapEntryInner { + ref_count: AtomicU64::new(ref_count), + slot_list: RwLock::new(vec![(slot, account_info.clone())]), + }) + } + // Try to update an item in the slot list the given `slot` If an item for the slot // already exists in the list, remove the older item, add it to `reclaims`, and insert // the new item. @@ -496,7 +508,7 @@ pub trait ZeroLamport { } type MapType = AccountMap>; -type ReadWriteLockMapType<'a, T> = RwLockWriteGuard<'a, AccountMap>>; +type AccountMapsWriteLock<'a, T> = RwLockWriteGuard<'a, AccountMap>>; #[derive(Debug)] pub struct AccountsIndex { @@ -842,47 +854,55 @@ impl AccountsIndex { .map(WriteAccountMapEntry::from_account_map_entry) } - fn new_entry() -> AccountMapEntry { - Arc::new(AccountMapEntryInner { - ref_count: AtomicU64::new(0), - slot_list: RwLock::new(SlotList::with_capacity(1)), - }) - } - - fn insert_new_entry_if_missing(&self, pubkey: &Pubkey) -> (WriteAccountMapEntry, bool) { - let new_entry = Self::new_entry(); - let mut w_account_maps = self.get_account_maps_write_lock(); - self.insert_new_entry_if_missing_with_lock(pubkey, &mut w_account_maps, new_entry) + fn insert_new_entry_if_missing( + &self, + pubkey: &Pubkey, + slot: Slot, + info: &T, + w_account_maps: Option<&mut AccountMapsWriteLock>, + ) -> Option> { + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, info); + match w_account_maps { + Some(w_account_maps) => { + self.insert_new_entry_if_missing_with_lock(pubkey, w_account_maps, new_entry) + } + None => { + let mut w_account_maps = self.get_account_maps_write_lock(); + self.insert_new_entry_if_missing_with_lock(pubkey, &mut w_account_maps, new_entry) + } + } } + // return None if item was created new + // if entry for pubkey already existed, return Some(entry). Caller needs to call entry.update. fn insert_new_entry_if_missing_with_lock( &self, pubkey: &Pubkey, - w_account_maps: &mut ReadWriteLockMapType, + w_account_maps: &mut AccountMapsWriteLock, new_entry: AccountMapEntry, - ) -> (WriteAccountMapEntry, bool) { + ) -> Option> { let mut is_newly_inserted = false; let account_entry = w_account_maps.entry(*pubkey).or_insert_with(|| { is_newly_inserted = true; new_entry }); - let w_account_entry = WriteAccountMapEntry::from_account_map_entry(account_entry.clone()); - (w_account_entry, is_newly_inserted) + if is_newly_inserted { + None + } else { + Some(WriteAccountMapEntry::from_account_map_entry( + account_entry.clone(), + )) + } } fn get_account_write_entry_else_create( &self, pubkey: &Pubkey, - ) -> (WriteAccountMapEntry, bool) { - let mut w_account_entry = self.get_account_write_entry(pubkey); - let mut is_newly_inserted = false; - if w_account_entry.is_none() { - let entry_is_new = self.insert_new_entry_if_missing(pubkey); - w_account_entry = Some(entry_is_new.0); - is_newly_inserted = entry_is_new.1; - } - - (w_account_entry.unwrap(), is_newly_inserted) + slot: Slot, + info: &T, + ) -> Option> { + let w_account_entry = self.get_account_write_entry(pubkey); + w_account_entry.or_else(|| self.insert_new_entry_if_missing(pubkey, slot, info, None)) } pub fn handle_dead_keys( @@ -1159,7 +1179,7 @@ impl AccountsIndex { } } - pub(crate) fn get_account_maps_write_lock(&self) -> ReadWriteLockMapType { + pub(crate) fn get_account_maps_write_lock(&self) -> AccountMapsWriteLock { self.account_maps.write().unwrap() } @@ -1173,15 +1193,16 @@ impl AccountsIndex { pubkey: &Pubkey, account_info: T, reclaims: &mut SlotList, - w_account_maps: &mut ReadWriteLockMapType, + w_account_maps: &mut AccountMapsWriteLock, ) { - let new_entry = Self::new_entry(); - let (mut w_account_entry, _is_new) = - self.insert_new_entry_if_missing_with_lock(pubkey, w_account_maps, new_entry); + let account_entry = + self.insert_new_entry_if_missing(pubkey, slot, &account_info, Some(w_account_maps)); if account_info.is_zero_lamport() { self.zero_lamport_pubkeys.insert(*pubkey); } - w_account_entry.update(slot, account_info, reclaims); + if let Some(mut w_account_entry) = account_entry { + w_account_entry.update(slot, account_info, reclaims); + } } // Updates the given pubkey at the given slot with the new account information. @@ -1198,8 +1219,8 @@ impl AccountsIndex { reclaims: &mut SlotList, ) -> bool { let is_newly_inserted = { - let (mut w_account_entry, is_newly_inserted) = - self.get_account_write_entry_else_create(pubkey); + let w_account_entry = + self.get_account_write_entry_else_create(pubkey, slot, &account_info); // We don't atomically update both primary index and secondary index together. // This certainly creates small time window with inconsistent state across the two indexes. // However, this is acceptable because: @@ -1214,8 +1235,12 @@ impl AccountsIndex { if account_info.is_zero_lamport() { self.zero_lamport_pubkeys.insert(*pubkey); } - w_account_entry.update(slot, account_info, reclaims); - is_newly_inserted + if let Some(mut w_account_entry) = w_account_entry { + w_account_entry.update(slot, account_info, reclaims); + false + } else { + true + } }; self.update_secondary_indexes(pubkey, account_owner, account_data, account_indexes); is_newly_inserted @@ -2269,29 +2294,154 @@ pub mod tests { assert_eq!(num, 1); } + #[test] + fn test_new_entry() { + let slot = 0; + // account_info type that IS cached + let account_info = AccountInfoTest::default(); + + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, &account_info); + assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 0); + assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); + assert_eq!( + new_entry.slot_list.read().unwrap().to_vec(), + vec![(slot, account_info)] + ); + + // account_info type that is NOT cached + let account_info = true; + + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, &account_info); + assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 1); + assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); + assert_eq!( + new_entry.slot_list.read().unwrap().to_vec(), + vec![(slot, account_info)] + ); + } + + fn test_new_entry_code_paths_helper< + T: 'static + Clone + IsCached + ZeroLamport + std::cmp::PartialEq + std::fmt::Debug, + >( + account_infos: [T; 2], + is_cached: bool, + upsert: bool, + ) { + let slot0 = 0; + let slot1 = 1; + let key = Keypair::new().pubkey(); + + let index = AccountsIndex::::default(); + let mut gc = Vec::new(); + + if upsert { + // insert first entry for pubkey. This will use new_entry_after_update and not call update. + index.upsert( + slot0, + &key, + &Pubkey::default(), + &[], + &AccountSecondaryIndexes::default(), + account_infos[0].clone(), + &mut gc, + ); + } else { + let mut lock = index.get_account_maps_write_lock(); + index.insert_new_if_missing_into_primary_index( + slot0, + &key, + account_infos[0].clone(), + &mut gc, + &mut lock, + ); + } + assert!(gc.is_empty()); + + // verify the added entry matches expected + { + let entry = index.get_account_read_entry(&key).unwrap(); + assert_eq!( + entry.ref_count().load(Ordering::Relaxed), + if is_cached { 0 } else { 1 } + ); + let expected = vec![(slot0, account_infos[0].clone())]; + assert_eq!(entry.slot_list().to_vec(), expected); + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot0, &account_infos[0]); + assert_eq!( + entry.slot_list().to_vec(), + new_entry.slot_list.read().unwrap().to_vec(), + ); + } + + // insert second entry for pubkey. This will use update and NOT use new_entry_after_update. + if upsert { + index.upsert( + slot1, + &key, + &Pubkey::default(), + &[], + &AccountSecondaryIndexes::default(), + account_infos[1].clone(), + &mut gc, + ); + } else { + let mut lock = index.get_account_maps_write_lock(); + index.insert_new_if_missing_into_primary_index( + slot1, + &key, + account_infos[1].clone(), + &mut gc, + &mut lock, + ); + } + assert!(gc.is_empty()); + + { + let entry = index.get_account_read_entry(&key).unwrap(); + assert_eq!( + entry.ref_count().load(Ordering::Relaxed), + if is_cached { 0 } else { 2 } + ); + assert_eq!( + entry.slot_list().to_vec(), + vec![ + (slot0, account_infos[0].clone()), + (slot1, account_infos[1].clone()) + ] + ); + + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot1, &account_infos[1]); + assert_eq!(entry.slot_list()[1], new_entry.slot_list.read().unwrap()[0],); + } + } + + #[test] + fn test_new_entry_and_update_code_paths() { + for is_upsert in &[false, true] { + // account_info type that IS cached + test_new_entry_code_paths_helper([1.0, 2.0], true, *is_upsert); + + // account_info type that is NOT cached + test_new_entry_code_paths_helper([true, false], false, *is_upsert); + } + } + #[test] fn test_insert_with_lock_no_ancestors() { let key = Keypair::new(); let index = AccountsIndex::::default(); - let mut gc = Vec::new(); let slot = 0; + let account_info = true; - let new_entry = AccountsIndex::new_entry(); - assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 0); - assert!(new_entry.slot_list.read().unwrap().is_empty()); - assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); + let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, &account_info); let mut w_account_maps = index.get_account_maps_write_lock(); - let (mut write, insert) = index.insert_new_entry_if_missing_with_lock( + let write = index.insert_new_entry_if_missing_with_lock( &key.pubkey(), &mut w_account_maps, new_entry, ); - assert!(insert); + assert!(write.is_none()); drop(w_account_maps); - let account_info = true; - write.update(slot, account_info, &mut gc); - assert!(gc.is_empty()); - drop(write); let mut ancestors = Ancestors::default(); assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none()); diff --git a/runtime/src/ancestors.rs b/runtime/src/ancestors.rs new file mode 100644 index 00000000000000..d4c167206b3523 --- /dev/null +++ b/runtime/src/ancestors.rs @@ -0,0 +1,4 @@ +use solana_sdk::clock::Slot; +use std::collections::HashMap; + +pub type Ancestors = HashMap; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 90317eedd50c47..709d77c7617afb 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -39,7 +39,8 @@ use crate::{ TransactionLoadResult, TransactionLoaders, }, accounts_db::{ErrorCounters, SnapshotStorages}, - accounts_index::{AccountSecondaryIndexes, Ancestors, IndexKey}, + accounts_index::{AccountSecondaryIndexes, IndexKey}, + ancestors::Ancestors, blockhash_queue::BlockhashQueue, builtins::{self, ActivationType}, epoch_stakes::{EpochStakes, NodeVoteAccounts}, @@ -2121,7 +2122,7 @@ impl Bank { clean.stop(); let mut shrink = Measure::start("shrink"); - self.shrink_all_slots(); + self.shrink_all_slots(false); shrink.stop(); info!( @@ -4526,7 +4527,7 @@ impl Bank { let mut shrink_all_slots_time = Measure::start("shrink_all_slots"); if self.slot() > 0 { - self.shrink_all_slots(); + self.shrink_all_slots(true); } shrink_all_slots_time.stop(); @@ -4815,8 +4816,8 @@ impl Bank { self.rc.accounts.accounts_db.clean_accounts(max_clean_slot); } - pub fn shrink_all_slots(&self) { - self.rc.accounts.accounts_db.shrink_all_slots(); + pub fn shrink_all_slots(&self, is_startup: bool) { + self.rc.accounts.accounts_db.shrink_all_slots(is_startup); } pub fn print_accounts_stats(&self) { @@ -5185,9 +5186,8 @@ pub(crate) mod tests { use super::*; use crate::{ accounts_db::SHRINK_RATIO, - accounts_index::{ - AccountIndex, AccountMap, AccountSecondaryIndexes, Ancestors, ITER_BATCH_SIZE, - }, + accounts_index::{AccountIndex, AccountMap, AccountSecondaryIndexes, ITER_BATCH_SIZE}, + ancestors::Ancestors, genesis_utils::{ activate_all_features, bootstrap_validator_stake_lamports, create_genesis_config_with_leader, create_genesis_config_with_vote_accounts, diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 825075b0bd8b7f..abc3f761785af7 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -6,6 +6,7 @@ pub mod accounts_cache; pub mod accounts_db; pub mod accounts_hash; pub mod accounts_index; +pub mod ancestors; pub mod append_vec; pub mod bank; pub mod bank_client; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 07dcfd1ddba218..2e5ef60034b4a5 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -1,5 +1,5 @@ use crate::{ - accounts::Accounts, accounts_index::Ancestors, instruction_recorder::InstructionRecorder, + accounts::Accounts, ancestors::Ancestors, instruction_recorder::InstructionRecorder, log_collector::LogCollector, native_loader::NativeLoader, rent_collector::RentCollector, }; use log::*; diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index 41044fbb84f75e..0cfc7b2b976ea5 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -2,7 +2,8 @@ use { crate::{ accounts::Accounts, accounts_db::{AccountStorageEntry, AccountsDb, AppendVecId, BankHashInfo}, - accounts_index::{AccountSecondaryIndexes, Ancestors}, + accounts_index::AccountSecondaryIndexes, + ancestors::Ancestors, append_vec::{AppendVec, StoredMetaWriteVersion}, bank::{Bank, BankFieldsToDeserialize, BankRc, Builtins}, blockhash_queue::BlockhashQueue, diff --git a/runtime/src/status_cache.rs b/runtime/src/status_cache.rs index 84cce738989d6e..e66537ae66e8d1 100644 --- a/runtime/src/status_cache.rs +++ b/runtime/src/status_cache.rs @@ -1,4 +1,4 @@ -use crate::accounts_index::Ancestors; +use crate::ancestors::Ancestors; use log::*; use rand::{thread_rng, Rng}; diff --git a/runtime/tests/accounts.rs b/runtime/tests/accounts.rs index c58a71445ffa7c..f04021aff05ec2 100644 --- a/runtime/tests/accounts.rs +++ b/runtime/tests/accounts.rs @@ -3,7 +3,7 @@ use rand::{thread_rng, Rng}; use rayon::prelude::*; use solana_runtime::{ accounts_db::{AccountsDb, LoadHint}, - accounts_index::Ancestors, + ancestors::Ancestors, }; use solana_sdk::genesis_config::ClusterType; use solana_sdk::{ diff --git a/sdk/bpf/c/inc/solana_sdk.h b/sdk/bpf/c/inc/solana_sdk.h index 3c91431cce693f..7dc5e5cfe62df3 100644 --- a/sdk/bpf/c/inc/solana_sdk.h +++ b/sdk/bpf/c/inc/solana_sdk.h @@ -457,6 +457,184 @@ uint64_t sol_keccak256( const uint8_t *result ); +/** + * 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 +); + /** * Account Meta */ diff --git a/sdk/bpf/scripts/install.sh b/sdk/bpf/scripts/install.sh index 2da6710be9b891..7bbabff3b35364 100755 --- a/sdk/bpf/scripts/install.sh +++ b/sdk/bpf/scripts/install.sh @@ -92,7 +92,7 @@ if [[ ! -e criterion-$version.md || ! -e criterion ]]; then fi # Install Rust-BPF -version=v1.8 +version=v1.9 if [[ ! -e bpf-tools-$version.md || ! -e bpf-tools ]]; then ( set -e diff --git a/sdk/cargo-build-bpf/src/main.rs b/sdk/cargo-build-bpf/src/main.rs index 0677517f7a2910..c60d1b7d7adced 100644 --- a/sdk/cargo-build-bpf/src/main.rs +++ b/sdk/cargo-build-bpf/src/main.rs @@ -360,7 +360,7 @@ fn build_bpf_package(config: &Config, target_directory: &Path, package: &cargo_m install_if_missing( &config, "bpf-tools", - "v1.8", + "v1.9", "https://github.com/solana-labs/bpf-tools/releases/download", &PathBuf::from(bpf_tools_filename), ) diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 3b42e9d14af6cf..1dda229f47d8a3 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -37,6 +37,8 @@ blake3 = "0.3.7" curve25519-dalek = "2.1.0" rand = "0.7.0" solana-logger = { path = "../../logger", version = "=1.7.0" } +num-bigint = "0.4.0" +openssl = "0.10.32" [dev-dependencies] static_assertions = "1.1.0" diff --git a/sdk/program/src/bignum.rs b/sdk/program/src/bignum.rs new file mode 100644 index 00000000000000..402daf29eda7ff --- /dev/null +++ b/sdk/program/src/bignum.rs @@ -0,0 +1,775 @@ +//! @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()); + } +} diff --git a/sdk/program/src/hash.rs b/sdk/program/src/hash.rs index cce0113ffd2c15..e39add147db6aa 100644 --- a/sdk/program/src/hash.rs +++ b/sdk/program/src/hash.rs @@ -135,7 +135,7 @@ pub fn hashv(vals: &[&[u8]]) -> Hash { { extern "C" { fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64; - }; + } let mut hash_result = [0; HASH_BYTES]; unsafe { sol_sha256( diff --git a/sdk/program/src/keccak.rs b/sdk/program/src/keccak.rs index ab0572b2fa291f..b3df920f15008a 100644 --- a/sdk/program/src/keccak.rs +++ b/sdk/program/src/keccak.rs @@ -133,7 +133,7 @@ pub fn hashv(vals: &[&[u8]]) -> Hash { { extern "C" { fn sol_keccak256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64; - }; + } let mut hash_result = [0; HASH_BYTES]; unsafe { sol_keccak256( 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/program/src/pubkey.rs b/sdk/program/src/pubkey.rs index 8718223bbccf04..eade06d999249b 100644 --- a/sdk/program/src/pubkey.rs +++ b/sdk/program/src/pubkey.rs @@ -227,7 +227,7 @@ impl Pubkey { program_id_addr: *const u8, address_bytes_addr: *const u8, ) -> u64; - }; + } let mut bytes = [0; 32]; let result = unsafe { sol_create_program_address( @@ -309,7 +309,7 @@ impl Pubkey { address_bytes_addr: *const u8, bump_seed_addr: *const u8, ) -> u64; - }; + } let mut bytes = [0; 32]; let mut bump_seed = std::u8::MAX; let result = unsafe { @@ -342,7 +342,7 @@ impl Pubkey { { extern "C" { fn sol_log_pubkey(pubkey_addr: *const u8); - }; + } unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; } diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index d591c84ac7477a..b396d48b8e699d 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -165,6 +165,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 { @@ -188,6 +214,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, } } }