diff --git a/web3.js/.travis.yml b/web3.js/.travis.yml index 6560e720f2..25c6d06fd1 100644 --- a/web3.js/.travis.yml +++ b/web3.js/.travis.yml @@ -33,7 +33,7 @@ script: - npm run lint - npm run codecov - make -C examples/bpf-c-noop/ - - ./examples/bpf-rust-noop/build.sh + - bpf-sdk/rust/build.sh examples/bpf-rust-noop - npm run localnet:update - npm run localnet:up - npm run examples || true diff --git a/web3.js/examples/bpf-c-noop/makefile b/web3.js/examples/bpf-c-noop/makefile index 24740ee5d5..467f9a6bbc 100644 --- a/web3.js/examples/bpf-c-noop/makefile +++ b/web3.js/examples/bpf-c-noop/makefile @@ -1 +1 @@ -include ../../bpf-sdk/bpf.mk +include ../../bpf-sdk/c/bpf.mk diff --git a/web3.js/examples/bpf-rust-noop/Cargo.toml b/web3.js/examples/bpf-rust-noop/Cargo.toml index 7a6473c5c8..5e6071a51b 100644 --- a/web3.js/examples/bpf-rust-noop/Cargo.toml +++ b/web3.js/examples/bpf-rust-noop/Cargo.toml @@ -3,23 +3,20 @@ [package] name = "solana-bpf-rust-noop" -version = "0.15.0" +version = "0.16.0" description = "Solana BPF noop program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" +edition = "2018" [dependencies] -# byteorder = { version = "1.3.1", default-features = false } -# heapless = { version = "0.4.0", default-features = false } -# byte = { version = "0.2", default-features = false } +solana-sdk-bpf-utils = { path = "../../bpf-sdk/rust/rust-utils", version = "0.16.0" } [workspace] members = [] [lib] -name = "solana_bpf_rust_noop" crate-type = ["cdylib"] - - +name = "solana_bpf_rust_noop" diff --git a/web3.js/examples/bpf-rust-noop/Xargo.toml b/web3.js/examples/bpf-rust-noop/Xargo.toml index db70306d58..330342622c 100644 --- a/web3.js/examples/bpf-rust-noop/Xargo.toml +++ b/web3.js/examples/bpf-rust-noop/Xargo.toml @@ -1,4 +1,6 @@ - - [dependencies.compiler_builtins] -path = "../../bpf-sdk/rust-bpf-sysroot/src/compiler-builtins" \ No newline at end of file +path = "../../bpf-sdk/dependencies/rust-bpf-sysroot/src/compiler-builtins" +features = ["c", "mem"] + +[target.bpfel-unknown-unknown.dependencies] +alloc = { path = "../../bpf-sdk/dependencies/rust-bpf-sysroot/src/liballoc" } \ No newline at end of file diff --git a/web3.js/examples/bpf-rust-noop/bpf.ld b/web3.js/examples/bpf-rust-noop/bpf.ld deleted file mode 100644 index 62a7170662..0000000000 --- a/web3.js/examples/bpf-rust-noop/bpf.ld +++ /dev/null @@ -1,19 +0,0 @@ -PHDRS -{ - text PT_LOAD ; - rodata PT_LOAD ; - dynamic PT_DYNAMIC ; -} - -SECTIONS -{ - . = SIZEOF_HEADERS; - .text : { *(.text) } :text - .rodata : { *(.rodata) } :rodata - .dynamic : { *(.dynamic) } :dynamic - .dynsym : { *(.dynsym) } :dynamic - .dynstr : { *(.dynstr) } :dynamic - .gnu.hash : { *(.gnu.hash) } :dynamic - .rel.dyn : { *(.rel.dyn) } :dynamic - .hash : { *(.hash) } :dynamic -} diff --git a/web3.js/examples/bpf-rust-noop/build.sh b/web3.js/examples/bpf-rust-noop/build.sh deleted file mode 100755 index abfd7e6264..0000000000 --- a/web3.js/examples/bpf-rust-noop/build.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -cargo install xargo - -set -ex - -# Ensure the sdk is installed -../../bpf-sdk/scripts/install.sh -rustup override set bpf - -export RUSTFLAGS="$RUSTFLAGS \ - -C lto=no \ - -C opt-level=2 \ - -C link-arg=-Tbpf.ld \ - -C link-arg=-z -C link-arg=notext \ - -C link-arg=--Bdynamic \ - -C link-arg=-shared \ - -C link-arg=--entry=entrypoint \ - -C linker=../../bpf-sdk/llvm-native/bin/ld.lld" -export XARGO_HOME="$PWD/target/xargo" -export XARGO_RUST_SRC="../../bpf-sdk/rust-bpf-sysroot/src" -# export XARGO_RUST_SRC="../../../../../rust-bpf-sysroot/src" -xargo build --target bpfel-unknown-unknown --release -v - -{ { set +x; } 2>/dev/null; echo Success; } diff --git a/web3.js/examples/bpf-rust-noop/clean.sh b/web3.js/examples/bpf-rust-noop/clean.sh deleted file mode 100755 index ea95016e3d..0000000000 --- a/web3.js/examples/bpf-rust-noop/clean.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -cargo clean diff --git a/web3.js/examples/bpf-rust-noop/dump.sh b/web3.js/examples/bpf-rust-noop/dump.sh deleted file mode 100755 index 8108047d5b..0000000000 --- a/web3.js/examples/bpf-rust-noop/dump.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -cp dump.txt dump_last.txt 2>/dev/null - -set -x -set -e - -./clean.sh -./build.sh -ls -la ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so > dump.txt -greadelf -aW ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so | rustfilt >> dump.txt -llvm-objdump -print-imm-hex --source --disassemble ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so >> dump.txt diff --git a/web3.js/examples/bpf-rust-noop/src/lib.rs b/web3.js/examples/bpf-rust-noop/src/lib.rs index 7846c529ad..05060a411b 100644 --- a/web3.js/examples/bpf-rust-noop/src/lib.rs +++ b/web3.js/examples/bpf-rust-noop/src/lib.rs @@ -1,11 +1,13 @@ //! @brief Example Rust-based BPF program that prints out the parameters passed to it -#![cfg(not(test))] #![no_std] +#![allow(unreachable_code)] -mod solana_sdk; +extern crate solana_sdk_bpf_utils; -use solana_sdk::*; +use solana_sdk_bpf_utils::entrypoint; +use solana_sdk_bpf_utils::entrypoint::*; +use solana_sdk_bpf_utils::log::*; struct SStruct { x: u64, @@ -18,7 +20,12 @@ fn return_sstruct() -> SStruct { SStruct { x: 1, y: 2, z: 3 } } -fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bool { +entrypoint!(process_instruction); +fn process_instruction( + ka: &mut [Option; MAX_ACCOUNTS], + info: &SolClusterInfo, + data: &[u8], +) -> bool { sol_log("Program identifier:"); sol_log_key(&info.program_id); @@ -28,23 +35,27 @@ fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bo sol_log("Account keys and instruction input data:"); sol_log_params(ka, data); + { + // Test - arch config + #[cfg(not(target_arch = "bpf"))] + panic!(); + } + { // Test - use core methods, unwrap // valid bytes, in a stack-allocated array let sparkle_heart = [240, 159, 146, 150]; - let result_str = core::str::from_utf8(&sparkle_heart).unwrap(); - - sol_log_64(0, 0, 0, 0, result_str.len() as u64); - sol_log(result_str); + assert_eq!(4, result_str.len()); assert_eq!("💖", result_str); + sol_log(result_str); } { // Test - struct return + let s = return_sstruct(); - sol_log_64(0, 0, s.x, s.y, s.z); assert_eq!(s.x + s.y + s.z, 6); } diff --git a/web3.js/examples/bpf-rust-noop/src/solana_sdk.rs b/web3.js/examples/bpf-rust-noop/src/solana_sdk.rs deleted file mode 100644 index b1672eaec1..0000000000 --- a/web3.js/examples/bpf-rust-noop/src/solana_sdk.rs +++ /dev/null @@ -1,409 +0,0 @@ -//! @brief Solana Rust-based BPF program utility functions and types - -// extern crate heapless; - -// use self::heapless::consts::*; -// use self::heapless::String; // fixed capacity `std::Vec` // type level integer used to specify capacity -#[cfg(test)] -use self::tests::process; -use core::mem::size_of; -use core::panic::PanicInfo; -use core::slice::from_raw_parts; - -#[cfg(not(test))] -use process; - -// Panic handling -extern "C" { - pub fn sol_panic_() -> !; -} -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - sol_log("Panic!"); - // TODO rashes! sol_log(_info.payload().downcast_ref::<&str>().unwrap()); - if let Some(location) = _info.location() { - if !location.file().is_empty() { - // TODO location.file() returns empty str, if we get here its been fixed - sol_log(location.file()); - sol_log("location.file() is fixed!!"); - unsafe { - sol_panic_(); - } - } - sol_log_64(0, 0, 0, location.line() as u64, location.column() as u64); - } else { - sol_log("Panic! but could not get location information"); - } - unsafe { - sol_panic_(); - } -} - -extern "C" { - fn sol_log_(message: *const u8); -} -/// Helper function that prints a string to stdout -#[inline(never)] // stack intensive, block inline so everyone does not incur -pub fn sol_log(message: &str) { - // TODO This is extremely slow, do something better - let mut buf: [u8; 128] = [0; 128]; - for (i, b) in message.as_bytes().iter().enumerate() { - if i >= 126 { - break; - } - buf[i] = *b; - } - unsafe { - sol_log_(buf.as_ptr()); - } - - // let mut c_string: String = String::new(); - // if message.len() < 256 { - // if c_string.push_str(message).is_err() { - // c_string - // .push_str("Attempted to log a malformed string\0") - // .is_ok(); - // } - // if c_string.push('\0').is_err() { - // c_string.push_str("Failed to log string\0").is_ok(); - // }; - // } else { - // c_string - // .push_str("Attempted to log a string that is too long\0") - // .is_ok(); - // } - // unsafe { - // sol_log_(message.as_ptr()); - // } -} - -extern "C" { - fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); -} -/// Helper function that prints a 64 bit values represented in hexadecimal -/// to stdout -pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { - unsafe { - sol_log_64_(arg1, arg2, arg3, arg4, arg5); - } -} - -/// Prints the hexadecimal representation of a public key -/// -/// @param key The public key to print -#[allow(dead_code)] -pub fn sol_log_key(key: &SolPubkey) { - for (i, k) in key.key.iter().enumerate() { - sol_log_64(0, 0, 0, i as u64, u64::from(*k)); - } -} - -/// Prints the hexadecimal representation of a slice -/// -/// @param slice The array to print -#[allow(dead_code)] -pub fn sol_log_slice(slice: &[u8]) { - for (i, s) in slice.iter().enumerate() { - sol_log_64(0, 0, 0, i as u64, u64::from(*s)); - } -} - -/// Prints the hexadecimal representation of the program's input parameters -/// -/// @param ka A pointer to an array of SolKeyedAccount to print -/// @param data A pointer to the instruction data to print -#[allow(dead_code)] -pub fn sol_log_params(ka: &[SolKeyedAccount], data: &[u8]) { - sol_log("- Number of KeyedAccounts"); - sol_log_64(0, 0, 0, 0, ka.len() as u64); - for k in ka.iter() { - sol_log("- Is signer"); - sol_log_64(0, 0, 0, 0, k.is_signer as u64); - sol_log("- Key"); - sol_log_key(&k.key); - sol_log("- Lamports"); - sol_log_64(0, 0, 0, 0, k.lamports); - sol_log("- AccountData"); - sol_log_slice(k.data); - sol_log("- Owner"); - sol_log_key(&k.owner); - } - sol_log("- Instruction data"); - sol_log_slice(data); -} - -pub const SIZE_PUBKEY: usize = 32; - -/// Public key -pub struct SolPubkey<'a> { - pub key: &'a [u8], -} - -/// Keyed Account -pub struct SolKeyedAccount<'a> { - /// Public key of the account - pub key: SolPubkey<'a>, - /// Public key of the account - pub is_signer: u64, - /// Number of lamports owned by this account - pub lamports: u64, - /// On-chain data within this account - pub data: &'a [u8], - /// Program that owns this account - pub owner: SolPubkey<'a>, -} - -/// Information about the state of the cluster immediately before the program -/// started executing the current instruction -pub struct SolClusterInfo<'a> { - ///program_id of the currently executing program - pub program_id: SolPubkey<'a>, -} - -#[no_mangle] -pub extern "C" fn entrypoint(input: *mut u8) -> bool { - const NUM_KA: usize = 1; // Number of KeyedAccounts expected - let mut offset: usize = 0; - - // Number of KeyedAccounts present - - let num_ka = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let num_ka_ptr: *const u64 = input.add(offset) as *const u64; - *num_ka_ptr - }; - offset += 8; - - if num_ka != NUM_KA as u64 { - return false; - } - - // KeyedAccounts - - let is_signer = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let is_signer_ptr: *const u64 = input.add(offset) as *const u64; - *is_signer_ptr - }; - offset += size_of::(); - - let key_slice = unsafe { from_raw_parts(input.add(offset), SIZE_PUBKEY) }; - let key = SolPubkey { key: &key_slice }; - offset += SIZE_PUBKEY; - - let lamports = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let lamports_ptr: *const u64 = input.add(offset) as *const u64; - *lamports_ptr - }; - offset += size_of::(); - - let data_length = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let data_length_ptr: *const u64 = input.add(offset) as *const u64; - *data_length_ptr - } as usize; - offset += size_of::(); - - let data = unsafe { from_raw_parts(input.add(offset), data_length) }; - offset += data_length; - - let owner_slice = unsafe { from_raw_parts(input.add(offset), SIZE_PUBKEY) }; - let owner = SolPubkey { key: &owner_slice }; - offset += SIZE_PUBKEY; - - let mut ka = [SolKeyedAccount { - key, - is_signer, - lamports, - data, - owner, - }]; - - // Instruction data - - let data_length = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let data_length_ptr: *const u64 = input.add(offset) as *const u64; - *data_length_ptr - } as usize; - offset += size_of::(); - - let data = unsafe { from_raw_parts(input.add(offset), data_length) }; - offset += data_length; - - // Id - - let program_id_slice = unsafe { from_raw_parts(input.add(offset), SIZE_PUBKEY) }; - let program_id: SolPubkey = SolPubkey { - key: &program_id_slice, - }; - - let info = SolClusterInfo { - program_id, - }; - - // Call user implementable function - process(&mut ka, &data, &info) -} - -#[cfg(test)] -mod tests { - extern crate std; - - use self::std::ffi::CStr; - use self::std::println; - use self::std::string::String; - use super::*; - - static mut _LOG_SCENARIO: u64 = 0; - fn get_log_scenario() -> u64 { - unsafe { _LOG_SCENARIO } - } - fn set_log_scenario(test: u64) { - unsafe { _LOG_SCENARIO = test }; - } - - #[no_mangle] - fn sol_log_(message: *const u8) { - let scenario = get_log_scenario(); - let c_str = unsafe { CStr::from_ptr(message as *const i8) }; - let string = c_str.to_str().unwrap(); - match scenario { - 1 => assert_eq!(string, "This is a test message"), - 2 => assert_eq!(string, "Attempted to log a string that is too long"), - 3 => { - let s: String = ['a'; 255].iter().collect(); - assert_eq!(string, s); - } - 4 => println!("{:?}", string), - _ => panic!("Unkown sol_log test"), - } - } - - static mut _LOG_64_SCENARIO: u64 = 0; - fn get_log_64_scenario() -> u64 { - unsafe { _LOG_64_SCENARIO } - } - fn set_log_64_scenario(test: u64) { - unsafe { _LOG_64_SCENARIO = test }; - } - - #[no_mangle] - fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { - let scenario = get_log_64_scenario(); - match scenario { - 1 => { - assert_eq!(1, arg1); - assert_eq!(2, arg2); - assert_eq!(3, arg3); - assert_eq!(4, arg4); - assert_eq!(5, arg5); - } - 2 => { - assert_eq!(0, arg1); - assert_eq!(0, arg2); - assert_eq!(0, arg3); - assert_eq!(arg4 + 1, arg5); - } - 3 => { - assert_eq!(0, arg1); - assert_eq!(0, arg2); - assert_eq!(0, arg3); - assert_eq!(arg4 + 1, arg5); - } - 4 => println!("{:?} {:?} {:?} {:?} {:?}", arg1, arg2, arg3, arg4, arg5), - _ => panic!("Unknown sol_log_64 test"), - } - } - - #[test] - fn test_sol_log() { - set_log_scenario(1); - sol_log("This is a test message"); - } - - #[test] - fn test_sol_log_long() { - set_log_scenario(2); - let s: String = ['a'; 256].iter().collect(); - sol_log(&s); - } - - #[test] - fn test_sol_log_max_length() { - set_log_scenario(3); - let s: String = ['a'; 255].iter().collect(); - sol_log(&s); - } - - #[test] - fn test_sol_log_64() { - set_log_64_scenario(1); - sol_log_64(1, 2, 3, 4, 5); - } - - #[test] - fn test_sol_log_key() { - set_log_64_scenario(2); - let key_array = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let key = SolPubkey { key: &key_array }; - sol_log_key(&key); - } - - #[test] - fn test_sol_log_slice() { - set_log_64_scenario(3); - let array = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - ]; - sol_log_slice(&array); - } - - pub fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bool { - assert_eq!(1, ka.len()); - assert_eq!(1, ka[0].is_signer); - let key = [ - 151, 116, 3, 85, 181, 39, 151, 99, 155, 29, 208, 191, 255, 191, 11, 161, 4, 43, 104, - 189, 202, 240, 231, 111, 146, 255, 199, 71, 67, 34, 254, 68, - ]; - assert_eq!(SIZE_PUBKEY, ka[0].key.key.len()); - assert_eq!(key, ka[0].key.key); - assert_eq!(48, ka[0].lamports); - assert_eq!(1, ka[0].data.len()); - let owner = [0; 32]; - assert_eq!(SIZE_PUBKEY, ka[0].owner.key.len()); - assert_eq!(owner, ka[0].owner.key); - let d = [1, 0, 0, 0, 0, 0, 0, 0, 1]; - assert_eq!(9, data.len()); - assert_eq!(d, data); - let program_id = [ - 190, 103, 191, 69, 193, 202, 38, 193, 95, 62, 131, 135, 105, 13, 142, 240, 155, 120, - 177, 90, 212, 54, 10, 118, 40, 33, 192, 8, 54, 141, 187, 63, - ]; - assert_eq!(program_id, info.program_id.key); - - true - } - - #[test] - fn test_entrypoint() { - set_log_scenario(4); - set_log_64_scenario(4); - let mut input: [u8; 154] = [ - 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 151, 116, 3, 85, 181, 39, 151, 99, 155, - 29, 208, 191, 255, 191, 11, 161, 4, 43, 104, 189, 202, 240, 231, 111, 146, 255, 199, - 71, 67, 34, 254, 68, 48, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 190, 103, 191, - 69, 193, 202, 38, 193, 95, 62, 131, 135, 105, 13, 142, 240, 155, 120, 177, 90, 212, 54, - 10, 118, 40, 33, 192, 8, 54, 141, 187, 63, - ]; - - entrypoint(&mut input[0] as *mut u8); - } -} diff --git a/web3.js/test/fixtures/noop-c/noop.so b/web3.js/test/fixtures/noop-c/noop.so index df281c1485..19a22edd73 100755 Binary files a/web3.js/test/fixtures/noop-c/noop.so and b/web3.js/test/fixtures/noop-c/noop.so differ diff --git a/web3.js/test/fixtures/noop-rust/build.sh b/web3.js/test/fixtures/noop-rust/build.sh index 0d795078fa..a466e6c269 100755 --- a/web3.js/test/fixtures/noop-rust/build.sh +++ b/web3.js/test/fixtures/noop-rust/build.sh @@ -3,5 +3,5 @@ set -ex cd "$(dirname "$0")" -../../../examples/bpf-rust-noop/build.sh +../../../bpf-sdk/rust/build.sh ../../../examples/bpf-rust-noop cp ../../../examples/bpf-rust-noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so . diff --git a/web3.js/test/fixtures/noop-rust/solana_bpf_rust_noop.so b/web3.js/test/fixtures/noop-rust/solana_bpf_rust_noop.so index 7a307f576e..72ab3bee7a 100755 Binary files a/web3.js/test/fixtures/noop-rust/solana_bpf_rust_noop.so and b/web3.js/test/fixtures/noop-rust/solana_bpf_rust_noop.so differ