This reverts commit 34344982a9
.
This commit is contained in:
31
programs/bpf/Cargo.toml
Normal file
31
programs/bpf/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "solana-bpf-programs"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "0.14.0"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
bpf_c = []
|
||||
bpf_rust = []
|
||||
|
||||
[build-dependencies]
|
||||
walkdir = "2"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.1.3"
|
||||
byteorder = "1.3.1"
|
||||
elf = "0.0.10"
|
||||
solana_rbpf = "=0.1.10"
|
||||
solana-bpfloader = { path = "../bpf_loader", version = "0.14.0" }
|
||||
solana-logger = { path = "../../logger", version = "0.14.0" }
|
||||
solana-runtime = { path = "../../runtime", version = "0.14.0" }
|
||||
solana-sdk = { path = "../../sdk", version = "0.14.0" }
|
||||
|
||||
[[bench]]
|
||||
name = "bpf_loader"
|
128
programs/bpf/benches/bpf_loader.rs
Normal file
128
programs/bpf/benches/bpf_loader.rs
Normal file
@ -0,0 +1,128 @@
|
||||
#![feature(test)]
|
||||
#![cfg(feature = "bpf_c")]
|
||||
extern crate test;
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||
use solana_rbpf::EbpfVmRaw;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Error;
|
||||
use std::io::Read;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use test::Bencher;
|
||||
|
||||
/// BPF program file extension
|
||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||
/// Create a BPF program file name
|
||||
fn create_bpf_path(name: &str) -> PathBuf {
|
||||
let mut pathbuf = {
|
||||
let current_exe = env::current_exe().unwrap();
|
||||
PathBuf::from(current_exe.parent().unwrap().parent().unwrap())
|
||||
};
|
||||
pathbuf.push("bpf/");
|
||||
pathbuf.push(name);
|
||||
pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF);
|
||||
pathbuf
|
||||
}
|
||||
|
||||
fn empty_check(_prog: &[u8]) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_elf() -> Result<Vec<u8>, std::io::Error> {
|
||||
let path = create_bpf_path("bench_alu");
|
||||
let mut file = File::open(&path).expect(&format!("Unable to open {:?}", path));
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
Ok(elf)
|
||||
}
|
||||
|
||||
const ARMSTRONG_LIMIT: u64 = 500;
|
||||
const ARMSTRONG_EXPECTED: u64 = 5;
|
||||
|
||||
#[bench]
|
||||
fn bench_program_load_elf(bencher: &mut Bencher) {
|
||||
let elf = load_elf().unwrap();
|
||||
let mut vm = EbpfVmRaw::new(None).unwrap();
|
||||
vm.set_verifier(empty_check).unwrap();
|
||||
|
||||
bencher.iter(|| {
|
||||
vm.set_elf(&elf).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_program_verify(bencher: &mut Bencher) {
|
||||
let elf = load_elf().unwrap();
|
||||
let mut vm = EbpfVmRaw::new(None).unwrap();
|
||||
vm.set_verifier(empty_check).unwrap();
|
||||
vm.set_elf(&elf).unwrap();
|
||||
|
||||
bencher.iter(|| {
|
||||
vm.set_verifier(solana_bpf_loader::bpf_verifier::check)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_program_alu(bencher: &mut Bencher) {
|
||||
let ns_per_s = 1000000000;
|
||||
let one_million = 1000000;
|
||||
let mut inner_iter = vec![];
|
||||
inner_iter
|
||||
.write_u64::<LittleEndian>(ARMSTRONG_LIMIT)
|
||||
.unwrap();
|
||||
inner_iter.write_u64::<LittleEndian>(0).unwrap();
|
||||
|
||||
let elf = load_elf().unwrap();
|
||||
let mut vm = solana_bpf_loader::create_vm(&elf).unwrap();
|
||||
|
||||
println!("Interpreted:");
|
||||
assert_eq!(
|
||||
1, /*true*/
|
||||
vm.execute_program(&mut inner_iter).unwrap()
|
||||
);
|
||||
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
|
||||
assert_eq!(
|
||||
ARMSTRONG_EXPECTED,
|
||||
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
|
||||
);
|
||||
|
||||
bencher.iter(|| {
|
||||
vm.execute_program(&mut inner_iter).unwrap();
|
||||
});
|
||||
let instructions = vm.get_last_instruction_count();
|
||||
let summary = bencher.bench(|_bencher| {}).unwrap();
|
||||
println!(" {:?} instructions", instructions);
|
||||
println!(" {:?} ns/iter median", summary.median as u64);
|
||||
assert!(0f64 != summary.median);
|
||||
let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
|
||||
println!(" {:?} MIPS", mips);
|
||||
println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
|
||||
|
||||
println!("JIT to native:");
|
||||
vm.jit_compile().unwrap();
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
1, /*true*/
|
||||
vm.execute_program_jit(&mut inner_iter).unwrap()
|
||||
);
|
||||
}
|
||||
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
|
||||
assert_eq!(
|
||||
ARMSTRONG_EXPECTED,
|
||||
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
|
||||
);
|
||||
|
||||
bencher.iter(|| unsafe {
|
||||
vm.execute_program_jit(&mut inner_iter).unwrap();
|
||||
});
|
||||
let summary = bencher.bench(|_bencher| {}).unwrap();
|
||||
println!(" {:?} instructions", instructions);
|
||||
println!(" {:?} ns/iter median", summary.median as u64);
|
||||
assert!(0f64 != summary.median);
|
||||
let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
|
||||
println!(" {:?} MIPS", mips);
|
||||
println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_jit_to_native_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
|
||||
}
|
99
programs/bpf/build.rs
Normal file
99
programs/bpf/build.rs
Normal file
@ -0,0 +1,99 @@
|
||||
extern crate walkdir;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn rerun_if_changed(files: &[&str], directories: &[&str]) {
|
||||
let mut all_files: Vec<_> = files.iter().map(|f| f.to_string()).collect();
|
||||
|
||||
for directory in directories {
|
||||
let files_in_directory: Vec<_> = WalkDir::new(directory)
|
||||
.into_iter()
|
||||
.map(|entry| entry.unwrap())
|
||||
.filter(|entry| entry.file_type().is_file())
|
||||
.map(|f| f.path().to_str().unwrap().to_owned())
|
||||
.collect();
|
||||
all_files.extend_from_slice(&files_in_directory[..]);
|
||||
}
|
||||
|
||||
for file in all_files {
|
||||
if !Path::new(&file).is_file() {
|
||||
panic!("{} is not a file", file);
|
||||
}
|
||||
println!("cargo:rerun-if-changed={}", file);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
let bpf_c = !env::var("CARGO_FEATURE_BPF_C").is_err();
|
||||
if bpf_c {
|
||||
let out_dir = "OUT_DIR=../../../target/".to_string()
|
||||
+ &env::var("PROFILE").unwrap()
|
||||
+ &"/bpf".to_string();
|
||||
|
||||
rerun_if_changed(
|
||||
&["../../sdk/bpf/bpf.ld", "../../sdk/bpf/bpf.mk", "c/makefile"],
|
||||
&["../../sdk/bpf/inc", "../../sdk/bpf/scripts", "c/src"],
|
||||
);
|
||||
|
||||
println!("cargo:warning=(not a warning) Compiling C-based BPF programs");
|
||||
let status = Command::new("make")
|
||||
.current_dir("c")
|
||||
.arg("programs")
|
||||
.arg(&out_dir)
|
||||
.status()
|
||||
.expect("Failed to build C-based BPF programs");
|
||||
assert!(status.success());
|
||||
}
|
||||
|
||||
let bpf_rust = !env::var("CARGO_FEATURE_BPF_RUST").is_err();
|
||||
if bpf_rust {
|
||||
let install_dir =
|
||||
"../../../../target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
||||
|
||||
if !Path::new("rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so")
|
||||
.is_file()
|
||||
{
|
||||
// Cannot build Rust BPF programs as part of main build because
|
||||
// to build it requires calling Cargo with different parameters which
|
||||
// would deadlock due to recursive cargo calls
|
||||
panic!(
|
||||
"solana_bpf_rust_noop.so not found, you must manually run \
|
||||
`programs/bpf/rust/noop/build.sh` to build it"
|
||||
);
|
||||
}
|
||||
|
||||
rerun_if_changed(
|
||||
&[
|
||||
"rust/noop/bpf.ld",
|
||||
"rust/noop/build.sh",
|
||||
"rust/noop/Cargo.toml",
|
||||
"rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so",
|
||||
],
|
||||
&["rust/noop/src"],
|
||||
);
|
||||
|
||||
println!(
|
||||
"cargo:warning=(not a warning) Installing Rust-based BPF program: solana_bpf_rust_noop"
|
||||
);
|
||||
let status = Command::new("mkdir")
|
||||
.current_dir("rust/noop")
|
||||
.arg("-p")
|
||||
.arg(&install_dir)
|
||||
.status()
|
||||
.expect("Unable to create BPF install directory");
|
||||
assert!(status.success());
|
||||
|
||||
let status = Command::new("cp")
|
||||
.current_dir("rust/noop")
|
||||
.arg("target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so")
|
||||
.arg(&install_dir)
|
||||
.status()
|
||||
.expect("Failed to copy solana_rust_bpf_noop.so to install directory");
|
||||
assert!(status.success());
|
||||
}
|
||||
}
|
1
programs/bpf/c/.gitignore
vendored
Normal file
1
programs/bpf/c/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/out/
|
2
programs/bpf/c/makefile
Normal file
2
programs/bpf/c/makefile
Normal file
@ -0,0 +1,2 @@
|
||||
BPF_SDK := ../../../sdk/bpf
|
||||
include $(BPF_SDK)/bpf.mk
|
30
programs/bpf/c/src/bench_alu/bench_alu.c
Normal file
30
programs/bpf/c/src/bench_alu/bench_alu.c
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @brief Benchmark program that does work
|
||||
*
|
||||
* Counts Armstrong Numbers between 1 and x
|
||||
*/
|
||||
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
uint64_t x = *(uint64_t *) input;
|
||||
uint64_t *result = (uint64_t *) input + 1;
|
||||
uint64_t count = 0;
|
||||
|
||||
for (int i = 1; i <= x; i++) {
|
||||
uint64_t temp = i;
|
||||
uint64_t num = 0;
|
||||
while (temp != 0) {
|
||||
uint64_t rem = (temp % 10);
|
||||
num += rem * rem * rem;
|
||||
temp /= 10;
|
||||
}
|
||||
if (i == num) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sol_log_64(x, count, 0, 0, 0);
|
||||
*result = count;
|
||||
return true;
|
||||
}
|
11
programs/bpf/c/src/bench_alu/test_bench_alu.c
Normal file
11
programs/bpf/c/src/bench_alu/test_bench_alu.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <criterion/criterion.h>
|
||||
#include "bench_alu.c"
|
||||
|
||||
Test(bench_alu, sanity) {
|
||||
uint64_t input[] = {500, 0};
|
||||
|
||||
cr_assert(entrypoint((uint8_t *) input));
|
||||
|
||||
cr_assert_eq(input[0], 500);
|
||||
cr_assert_eq(input[1], 5);
|
||||
}
|
14
programs/bpf/c/src/bpf_to_bpf/entrypoint.c
Normal file
14
programs/bpf/c/src/bpf_to_bpf/entrypoint.c
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that prints out the parameters
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
sol_log(__FILE__);
|
||||
helper_function();
|
||||
sol_log(__FILE__);
|
||||
return true;
|
||||
}
|
11
programs/bpf/c/src/bpf_to_bpf/helper.c
Normal file
11
programs/bpf/c/src/bpf_to_bpf/helper.c
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that prints out the parameters
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
void helper_function(void) {
|
||||
sol_log(__FILE__);
|
||||
}
|
7
programs/bpf/c/src/bpf_to_bpf/helper.h
Normal file
7
programs/bpf/c/src/bpf_to_bpf/helper.h
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that prints out the parameters
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
void helper_function(void);
|
36
programs/bpf/c/src/move_funds/move_funds.c
Normal file
36
programs/bpf/c/src/move_funds/move_funds.c
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that moves funds from one account to
|
||||
* another
|
||||
*/
|
||||
|
||||
#include <solana_sdk.h>
|
||||
|
||||
/**
|
||||
* Number of SolKeyedAccount expected. The program should bail if an
|
||||
* unexpected number of accounts are passed to the program's entrypoint
|
||||
*/
|
||||
#define NUM_KA 3
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[NUM_KA];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!params.ka[0].is_signer) {
|
||||
sol_log("Transaction not signed by key 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t lamports = *(int64_t *)params.data;
|
||||
if (*params.ka[0].lamports >= lamports) {
|
||||
*params.ka[0].lamports -= lamports;
|
||||
*params.ka[2].lamports += lamports;
|
||||
// sol_log_64(0, 0, *ka[0].lamports, *ka[2].lamports, lamports);
|
||||
} else {
|
||||
// sol_log_64(0, 0, 0xFF, *ka[0].lamports, lamports);
|
||||
}
|
||||
return true;
|
||||
}
|
10
programs/bpf/c/src/multiple_static/multiple_static.c
Normal file
10
programs/bpf/c/src/multiple_static/multiple_static.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <solana_sdk.h>
|
||||
|
||||
static const char msg[] = "This is a message";
|
||||
static const char msg2[] = "This is a different message";
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
sol_log((char*)msg);
|
||||
sol_log((char*)msg2);
|
||||
return true;
|
||||
}
|
22
programs/bpf/c/src/noop++/noop++.cc
Normal file
22
programs/bpf/c/src/noop++/noop++.cc
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @brief Example C++-based BPF program that prints out the parameters
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[1];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
sol_log(__FILE__);
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log the provided input parameters. In the case of the no-op
|
||||
// program, no account keys or input data are expected but real
|
||||
// programs will have specific requirements so they can do their work.
|
||||
sol_log_params(¶ms);
|
||||
return true;
|
||||
}
|
23
programs/bpf/c/src/noop/noop.c
Normal file
23
programs/bpf/c/src/noop/noop.c
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that prints out the parameters
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
SolKeyedAccount ka[1];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
sol_log(__FILE__);
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log the provided input parameters. In the case of the no-op
|
||||
// program, no account keys or input data are expected but real
|
||||
// programs will have specific requirements so they can do their work.
|
||||
sol_log_params(¶ms);
|
||||
return true;
|
||||
}
|
||||
|
16
programs/bpf/c/src/relative_call/relative_call.c
Normal file
16
programs/bpf/c/src/relative_call/relative_call.c
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @brief test program that generates BPF PC relative call instructions
|
||||
*/
|
||||
|
||||
#include <solana_sdk.h>
|
||||
|
||||
void __attribute__ ((noinline)) helper() {
|
||||
sol_log(__func__);
|
||||
}
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
sol_log(__func__);
|
||||
helper();
|
||||
return true;
|
||||
}
|
||||
|
17
programs/bpf/c/src/struct_pass/struct_pass.c
Normal file
17
programs/bpf/c/src/struct_pass/struct_pass.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <solana_sdk.h>
|
||||
|
||||
struct foo {const uint8_t *input;};
|
||||
void foo(const uint8_t *input, struct foo foo) ;
|
||||
|
||||
extern bool entrypoint(const uint8_t *input) {
|
||||
struct foo f;
|
||||
f.input = input;
|
||||
foo(input, f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void foo(const uint8_t *input, struct foo foo) {
|
||||
sol_log_64(0, 0, 0, (uint64_t)input, (uint64_t)foo.input);
|
||||
sol_assert(input == foo.input);
|
||||
}
|
||||
|
21
programs/bpf/c/src/struct_ret/struct_ret.c
Normal file
21
programs/bpf/c/src/struct_ret/struct_ret.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <solana_sdk.h>
|
||||
|
||||
struct test_struct { uint64_t x; uint64_t y; uint64_t z;};
|
||||
|
||||
static struct test_struct __attribute__ ((noinline)) test_function(void) {
|
||||
struct test_struct s;
|
||||
s.x = 3;
|
||||
s.y = 4;
|
||||
s.z = 5;
|
||||
return s;
|
||||
}
|
||||
|
||||
extern bool entrypoint(const uint8_t* input) {
|
||||
struct test_struct s = test_function();
|
||||
sol_log("foobar");
|
||||
if (s.x + s.y + s.z == 12 ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
3
programs/bpf/rust/noop/.gitignore
vendored
Normal file
3
programs/bpf/rust/noop/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target/
|
||||
|
||||
Cargo.lock
|
25
programs/bpf/rust/noop/Cargo.toml
Normal file
25
programs/bpf/rust/noop/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
# Note: This crate must be built using build.sh
|
||||
|
||||
[package]
|
||||
name = "solana-bpf-rust-noop"
|
||||
version = "0.14.0"
|
||||
description = "Solana BPF noop program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
# byteorder = { version = "1.3.1", default-features = false }
|
||||
# heapless = { version = "0.4.0", default-features = false }
|
||||
# byte = { version = "0.2", default-features = false }
|
||||
|
||||
[workspace]
|
||||
members = []
|
||||
|
||||
[lib]
|
||||
name = "solana_bpf_rust_noop"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
4
programs/bpf/rust/noop/Xargo.toml
Normal file
4
programs/bpf/rust/noop/Xargo.toml
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
[dependencies.compiler_builtins]
|
||||
path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins"
|
19
programs/bpf/rust/noop/bpf.ld
Normal file
19
programs/bpf/rust/noop/bpf.ld
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
}
|
27
programs/bpf/rust/noop/build.sh
Executable file
27
programs/bpf/rust/noop/build.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
cargo install xargo
|
||||
|
||||
set -e
|
||||
|
||||
# Ensure the sdk is installed
|
||||
../../../../sdk/bpf/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=../../../../sdk/bpf/llvm-native/bin/ld.lld"
|
||||
export XARGO_HOME="$PWD/target/xargo"
|
||||
export XARGO_RUST_SRC="../../../../sdk/bpf/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; }
|
5
programs/bpf/rust/noop/clean.sh
Executable file
5
programs/bpf/rust/noop/clean.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cargo clean
|
12
programs/bpf/rust/noop/dump.sh
Executable file
12
programs/bpf/rust/noop/dump.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/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
|
55
programs/bpf/rust/noop/src/lib.rs
Normal file
55
programs/bpf/rust/noop/src/lib.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! @brief Example Rust-based BPF program that prints out the parameters passed to it
|
||||
|
||||
#![cfg(not(test))]
|
||||
#![no_std]
|
||||
|
||||
mod solana_sdk;
|
||||
|
||||
use solana_sdk::*;
|
||||
|
||||
struct SStruct {
|
||||
x: u64,
|
||||
y: u64,
|
||||
z: u64,
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn return_sstruct() -> SStruct {
|
||||
SStruct { x: 1, y: 2, z: 3 }
|
||||
}
|
||||
|
||||
fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bool {
|
||||
sol_log("Tick height:");
|
||||
sol_log_64(info.tick_height, 0, 0, 0, 0);
|
||||
sol_log("Program identifier:");
|
||||
sol_log_key(&info.program_id);
|
||||
|
||||
// Log the provided account keys and instruction input data. In the case of
|
||||
// the no-op program, no account keys or input data are expected but real
|
||||
// programs will have specific requirements so they can do their work.
|
||||
sol_log("Account keys and instruction input data:");
|
||||
sol_log_params(ka, data);
|
||||
|
||||
{
|
||||
// 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!("💖", 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);
|
||||
}
|
||||
|
||||
sol_log("Success");
|
||||
true
|
||||
}
|
422
programs/bpf/rust/noop/src/solana_sdk.rs
Normal file
422
programs/bpf/rust/noop/src/solana_sdk.rs
Normal file
@ -0,0 +1,422 @@
|
||||
//! @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<U256> = 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> {
|
||||
/// Current ledger tick
|
||||
pub tick_height: u64,
|
||||
///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::<u64>();
|
||||
|
||||
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::<u64>();
|
||||
|
||||
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::<u64>();
|
||||
|
||||
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::<u64>();
|
||||
|
||||
let data = unsafe { from_raw_parts(input.add(offset), data_length) };
|
||||
offset += data_length;
|
||||
|
||||
// Tick height
|
||||
|
||||
let tick_height = unsafe {
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let tick_height_ptr: *const u64 = input.add(offset) as *const u64;
|
||||
*tick_height_ptr
|
||||
};
|
||||
offset += size_of::<u64>();
|
||||
|
||||
// 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 {
|
||||
tick_height,
|
||||
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);
|
||||
assert_eq!(1, info.tick_height);
|
||||
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);
|
||||
}
|
||||
}
|
140
programs/bpf/tests/programs.rs
Normal file
140
programs/bpf/tests/programs.rs
Normal file
@ -0,0 +1,140 @@
|
||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||
mod bpf {
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_runtime::loader_utils::{create_invoke_instruction, load_program};
|
||||
use solana_sdk::genesis_block::GenesisBlock;
|
||||
use solana_sdk::native_loader;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// BPF program file extension
|
||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||
|
||||
/// Create a BPF program file name
|
||||
fn create_bpf_path(name: &str) -> PathBuf {
|
||||
let mut pathbuf = {
|
||||
let current_exe = env::current_exe().unwrap();
|
||||
PathBuf::from(current_exe.parent().unwrap().parent().unwrap())
|
||||
};
|
||||
pathbuf.push("bpf/");
|
||||
pathbuf.push(name);
|
||||
pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF);
|
||||
pathbuf
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_c")]
|
||||
mod bpf_c {
|
||||
use super::*;
|
||||
use solana_sdk::bpf_loader;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn test_program_bpf_c_noop() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mut file = File::open(create_bpf_path("noop")).expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
let (genesis_block, alice_keypair) = GenesisBlock::new(50);
|
||||
let bank = Bank::new(&genesis_block);
|
||||
let bank_client = BankClient::new(bank);
|
||||
|
||||
// Call user program
|
||||
let program_id = load_program(&bank_client, &alice_keypair, &bpf_loader::id(), elf);
|
||||
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
bank_client
|
||||
.send_instruction(&alice_keypair, instruction)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_program_bpf_c() {
|
||||
solana_logger::setup();
|
||||
|
||||
let programs = [
|
||||
"bpf_to_bpf",
|
||||
"multiple_static",
|
||||
"noop",
|
||||
"noop++",
|
||||
"relative_call",
|
||||
"struct_pass",
|
||||
"struct_ret",
|
||||
];
|
||||
for program in programs.iter() {
|
||||
println!("Test program: {:?}", program);
|
||||
let mut file = File::open(create_bpf_path(program)).expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
let (genesis_block, alice_keypair) = GenesisBlock::new(50);
|
||||
let bank = Bank::new(&genesis_block);
|
||||
let bank_client = BankClient::new(bank);
|
||||
|
||||
let loader_id = load_program(
|
||||
&bank_client,
|
||||
&alice_keypair,
|
||||
&native_loader::id(),
|
||||
"solana_bpf_loader".as_bytes().to_vec(),
|
||||
);
|
||||
|
||||
// Call user program
|
||||
let program_id = load_program(&bank_client, &alice_keypair, &loader_id, elf);
|
||||
let instruction =
|
||||
create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
bank_client
|
||||
.send_instruction(&alice_keypair, instruction)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot currently build the Rust BPF program as part
|
||||
// of the rest of the build due to recursive `cargo build` causing
|
||||
// a build deadlock. Therefore you must build the Rust programs
|
||||
// yourself first by calling `make all` in the Rust BPF program's directory
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
mod bpf_rust {
|
||||
use super::*;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn test_program_bpf_rust() {
|
||||
solana_logger::setup();
|
||||
|
||||
let programs = ["solana_bpf_rust_noop"];
|
||||
for program in programs.iter() {
|
||||
let filename = create_bpf_path(program);
|
||||
println!("Test program: {:?} from {:?}", program, filename);
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
let (genesis_block, alice_keypair) = GenesisBlock::new(50);
|
||||
let bank = Bank::new(&genesis_block);
|
||||
let bank_client = BankClient::new(bank);
|
||||
|
||||
let loader_id = load_program(
|
||||
&bank_client,
|
||||
&alice_keypair,
|
||||
&native_loader::id(),
|
||||
"solana_bpf_loader".as_bytes().to_vec(),
|
||||
);
|
||||
|
||||
// Call user program
|
||||
let program_id = load_program(&bank_client, &alice_keypair, &loader_id, elf);
|
||||
let instruction =
|
||||
create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
bank_client
|
||||
.send_instruction(&alice_keypair, instruction)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user