4
programs/bpf/rust/noop/.gitignore
vendored
Normal file
4
programs/bpf/rust/noop/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/out/
|
||||
/target/
|
||||
|
||||
Cargo.lock
|
@ -1,12 +1,17 @@
|
||||
|
||||
# Note: This crate must be built using the makefile, try `make help` instead of `cargo build`
|
||||
|
||||
[package]
|
||||
name = "solana-bpf-noop"
|
||||
name = "solana-bpf-rust-noop"
|
||||
version = "0.12.0"
|
||||
description = "Solana BPF noop program"
|
||||
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]
|
||||
rbpf = "0.1.0"
|
||||
solana-sdk = { path = "../../../../sdk", version = "0.12.0" }
|
||||
heapless = { version = "0.4.0", default-features = false }
|
||||
|
||||
[workspace]
|
||||
members = []
|
||||
|
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
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
# TODO building release flavor with rust produces a bunch of output .bc files
|
||||
INTERDIR=../../../target/release
|
||||
OUTDIR="${1:-../../../target/debug/}"
|
||||
mkdir -p "$OUTDIR"
|
||||
# cargo +nightly rustc --release -- -C panic=abort --emit=llvm-ir
|
||||
cargo +nightly rustc --release -- -C panic=abort --emit=llvm-bc
|
||||
cp "$INTERDIR"/deps/noop_rust-*.bc "$OUTDIR"/noop_rust.bc
|
||||
/usr/local/opt/llvm/bin/llc -march=bpf -filetype=obj -o "$OUTDIR"/noop_rust.o "$OUTDIR"/noop_rust.bc
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/local/opt/llvm/bin/llvm-objdump -color -source -disassemble target/release/noop_rust.o
|
145
programs/bpf/rust/noop/makefile
Executable file
145
programs/bpf/rust/noop/makefile
Executable file
@ -0,0 +1,145 @@
|
||||
LOCAL_PATH := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
SDK_PATH := $(abspath $(LOCAL_PATH)/../../../../sdk/bpf)
|
||||
INSTALL_SH := $(abspath $(SDK_PATH)/scripts/install.sh)
|
||||
|
||||
all:
|
||||
.PHONY: help all install dump clean
|
||||
|
||||
ifneq ($(V),1)
|
||||
_@ :=@
|
||||
endif
|
||||
|
||||
TARGET_NAME := solana_bpf_rust_noop
|
||||
SRC_DIR ?= ./src
|
||||
OUT_DIR ?= ./out
|
||||
INSTALL_DIR ?= ./out
|
||||
CARGO_OUT_DIR ?=$(LOCAL_PATH)target/release
|
||||
|
||||
ifeq ($(DOCKER),1)
|
||||
$(warning DOCKER=1 is experimential and may not work as advertised)
|
||||
LLVM_DIR = $(SDK_PATH)/llvm-docker
|
||||
LLVM_SYSTEM_INC_DIRS := /usr/local/lib/clang/8.0.0/include
|
||||
else
|
||||
LLVM_DIR = $(SDK_PATH)/llvm-native
|
||||
LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/8.0.0/include
|
||||
endif
|
||||
|
||||
CARGO := cargo
|
||||
ifdef LLVM_DIR
|
||||
LLC := $(LLVM_DIR)/bin/llc
|
||||
LLD := $(LLVM_DIR)/bin/ld.lld
|
||||
OBJ_COPY := $(LLVM_DIR)/bin/llvm-objcopy
|
||||
OBJ_DUMP := $(LLVM_DIR)/bin/llvm-objdump
|
||||
endif
|
||||
|
||||
CARGO_FLAGS := \
|
||||
+nightly \
|
||||
-vv rustc \
|
||||
-vv \
|
||||
--release \
|
||||
-- \
|
||||
--emit=llvm-ir \
|
||||
-C panic=abort \
|
||||
|
||||
LLC_FLAGS := \
|
||||
-march=bpf \
|
||||
-filetype=obj \
|
||||
|
||||
LLD_FLAGS := \
|
||||
-z notext \
|
||||
-shared \
|
||||
--Bdynamic \
|
||||
$(LOCAL_PATH)bpf.ld \
|
||||
|
||||
OBJ_COPY_FLAGS := \
|
||||
--remove-section .eh_frame \
|
||||
|
||||
OBJ_DUMP_FLAGS := \
|
||||
-color \
|
||||
-source \
|
||||
-disassemble \
|
||||
|
||||
help:
|
||||
@echo ''
|
||||
@echo 'solana-bpf-rust-noop makefile'
|
||||
@echo ''
|
||||
@echo 'This makefile will build the solana-bpf-rust-noop crate into a BPF shared object'
|
||||
@echo ''
|
||||
@echo 'This makefile is not run as part of the Solana workspace. Doing so'
|
||||
@echo 'would result in a cargo deadlock since this makefile also calls cargo with parameters'
|
||||
@echo 'required to build the BPF program from Rust.'
|
||||
@echo ''
|
||||
@echo 'Note: Rust BPF programs are tested as part of the Solana integration tests when'
|
||||
@echo ' feature "bpf_rust" is enabled. The solana-bpf-rust-noop crate must be built'
|
||||
@echo ' with this makefile first before bulding Solana.'
|
||||
@echo ''
|
||||
@echo ' Here is a sample command that will run this BPF program:'
|
||||
@echo ''
|
||||
@echo ' export RUST_LOG=solana_bpf_loader=info; cargo test --features="bpf_rust" -- --nocapture test_program_bpf_rust'
|
||||
@echo ''
|
||||
@echo 'User settings'
|
||||
@echo ' - The following setting are overridable on the command line, default values shown:'
|
||||
@echo ' - Show commands while building: V=1'
|
||||
@echo ' V=$(V)'
|
||||
@echo ' - Location to place output files:'
|
||||
@echo ' OUT_DIR=$(OUT_DIR)'
|
||||
@echo ' - Location to install the final shared object:'
|
||||
@echo ' INSTALL_DIR=$(INSTALL_DIR)'
|
||||
@echo ' - Location of LLVM:'
|
||||
@echo ' LLVM_DIR=$(LLVM_DIR)'
|
||||
@echo ''
|
||||
@echo 'Usage:'
|
||||
@echo ' - make help - This help message'
|
||||
@echo ' - make all - Build $(OUT_DIR)/$(TARGET_NAME).so'
|
||||
@echo ' - make dump - Dumps the contents of $(OUT_DIR)/$(TARGET_NAME).so to stdout, requires greadelf and rustfilt'
|
||||
@echo ''
|
||||
|
||||
.PHONY: $(INSTALL_SH)
|
||||
$(INSTALL_SH):
|
||||
$(_@)$(INSTALL_SH)
|
||||
|
||||
.PRECIOUS: $(OUT_DIR)/%.ll
|
||||
$(OUT_DIR)/%.ll: $(SRC_DIR)/*
|
||||
@echo "[cargo] $@ ($<)"
|
||||
$(_@)mkdir -p $(OUT_DIR)
|
||||
$(_@)rm -f $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll
|
||||
$(_@)$(CARGO) $(CARGO_FLAGS)
|
||||
$(_@)cp $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll $(OUT_DIR)/$(TARGET_NAME).ll
|
||||
|
||||
.PRECIOUS: $(OUT_DIR)/%.o
|
||||
$(OUT_DIR)/%.o: $(OUT_DIR)/%.ll $(INSTALL_SH)
|
||||
@echo "[llc] $@ ($<)"
|
||||
$(_@)$(LLC) $(LLC_FLAGS) -o $@ $<
|
||||
$(_@)$(OBJ_COPY) $(OBJ_COPY_FLAGS) $@
|
||||
|
||||
.PRECIOUS: $(OUT_DIR)/%.so
|
||||
$(OUT_DIR)/%.so: $(OUT_DIR)/%.o $(INSTALL_SH)
|
||||
@echo "[lld] $@ ($<)"
|
||||
$(_@)$(LLD) $(LLD_FLAGS) -o $@ $<
|
||||
|
||||
-include $(wildcard $(OUT_DIR)/$(TARGET_NAME).d)
|
||||
|
||||
define \n
|
||||
|
||||
|
||||
endef
|
||||
|
||||
all: $(OUT_DIR)/$(TARGET_NAME).so
|
||||
|
||||
# Warning: Do not build as part of install (e.g. install must not depend
|
||||
# on $(TARGET_NAME).so) doing so will deadlock cargo due to recrusive
|
||||
# calls to cargo
|
||||
install:
|
||||
$(_@)mkdir -p $(INSTALL_DIR)
|
||||
$(_@)cp $(OUT_DIR)/$(TARGET_NAME).so $(INSTALL_DIR)
|
||||
|
||||
dump: $(OUT_DIR)/$(TARGET_NAME).so
|
||||
$(_@)greadelf -aW $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
|
||||
$(_@)$(OBJ_DUMP) -disassemble -source $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
|
||||
|
||||
test:
|
||||
cargo test -- --test-threads 1
|
||||
|
||||
clean:
|
||||
$(_@)rm -rf $(OUT_DIR)
|
||||
cargo clean
|
@ -1,15 +1,22 @@
|
||||
extern crate rbpf;
|
||||
//! @brief Example Rust-based BPF program that prints out the parameters passed to it
|
||||
|
||||
use std::mem::transmute;
|
||||
#![cfg(not(test))]
|
||||
#![no_std]
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text,entrypoint"] // TODO platform independent needed
|
||||
pub extern "C" fn entrypoint(_raw: *mut u8) {
|
||||
let bpf_func_trace_printk = unsafe {
|
||||
transmute::<u64, extern "C" fn(u64, u64, u64, u64, u64)>(u64::from(
|
||||
rbpf::helpers::BPF_TRACE_PRINTK_IDX,
|
||||
))
|
||||
};
|
||||
mod solana_sdk;
|
||||
|
||||
bpf_func_trace_printk(0, 0, 1, 2, 3);
|
||||
use solana_sdk::*;
|
||||
|
||||
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);
|
||||
true
|
||||
}
|
||||
|
381
programs/bpf/rust/noop/src/solana_sdk.rs
Normal file
381
programs/bpf/rust/noop/src/solana_sdk.rs
Normal file
@ -0,0 +1,381 @@
|
||||
//! @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::slice::from_raw_parts;
|
||||
#[cfg(not(test))]
|
||||
use process;
|
||||
|
||||
extern "C" {
|
||||
fn sol_log_(message: *const u8);
|
||||
}
|
||||
/// Helper function that prints a string to stdout
|
||||
pub fn sol_log(message: &str) {
|
||||
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_(c_string.as_bytes().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("- Tokens");
|
||||
sol_log_64(0, 0, 0, 0, k.tokens);
|
||||
sol_log("- Userdata");
|
||||
sol_log_slice(k.userdata);
|
||||
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 tokens owned by this account
|
||||
pub tokens: u64,
|
||||
/// On-chain data within this account
|
||||
pub userdata: &'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 tokens = unsafe {
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let tokens_ptr: *const u64 = input.add(offset) as *const u64;
|
||||
*tokens_ptr
|
||||
};
|
||||
offset += size_of::<u64>();
|
||||
|
||||
let userdata_length = unsafe {
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let userdata_length_ptr: *const u64 = input.add(offset) as *const u64;
|
||||
*userdata_length_ptr
|
||||
} as usize;
|
||||
offset += size_of::<u64>();
|
||||
|
||||
let userdata = unsafe { from_raw_parts(input.add(offset), userdata_length) };
|
||||
offset += userdata_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,
|
||||
tokens,
|
||||
userdata,
|
||||
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].tokens);
|
||||
assert_eq!(1, ka[0].userdata.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);
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ edition = "2018"
|
||||
|
||||
[features]
|
||||
bpf_c = []
|
||||
bpf_rust = []
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.0.0"
|
||||
@ -17,7 +18,7 @@ byteorder = "1.2.1"
|
||||
elf = "0.0.10"
|
||||
libc = "0.2.45"
|
||||
log = "0.4.2"
|
||||
solana_rbpf = "=0.1.5"
|
||||
solana_rbpf = "=0.1.6"
|
||||
serde = "1.0.82"
|
||||
solana-logger = { path = "../../../logger", version = "0.12.0" }
|
||||
solana-sdk = { path = "../../../sdk", version = "0.12.0" }
|
||||
|
@ -6,7 +6,6 @@ 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()
|
||||
@ -40,4 +39,50 @@ fn main() {
|
||||
.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 = "INSTALL_DIR=../../../../target/".to_string()
|
||||
+ &env::var("PROFILE").unwrap()
|
||||
+ &"/bpf".to_string();
|
||||
|
||||
if !Path::new("../../bpf/rust/noop/out/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 \
|
||||
`make all` in programs/bpf/rust/noop to build it"
|
||||
);
|
||||
}
|
||||
|
||||
let rerun_if_changed_files = vec![
|
||||
"../../bpf/rust/noop/bpf.ld",
|
||||
"../../bpf/rust/noop/makefile",
|
||||
"../../bpf/rust/noop/out/solana_bpf_rust_noop.so",
|
||||
];
|
||||
|
||||
for file in rerun_if_changed_files {
|
||||
if !Path::new(file).is_file() {
|
||||
panic!("{} is not a file", file);
|
||||
}
|
||||
println!("cargo:rerun-if-changed={}", file);
|
||||
}
|
||||
|
||||
println!(
|
||||
"cargo:warning=(not a warning) Installing Rust-based BPF program: solana_bpf_rust_noop"
|
||||
);
|
||||
let status = Command::new("make")
|
||||
.current_dir("../../bpf/rust/noop")
|
||||
.arg("install")
|
||||
.arg("V=1")
|
||||
.arg("OUT_DIR=out")
|
||||
.arg(&install_dir)
|
||||
.status()
|
||||
.expect(
|
||||
"solana_bpf_rust_noop.so not found, you must manually run \
|
||||
`make all` in its program directory",
|
||||
);
|
||||
assert!(status.success());
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,9 @@ pub fn create_vm(prog: &[u8]) -> Result<EbpfVmRaw, Error> {
|
||||
vm.set_max_instruction_count(36000)?; // 36000 is a wag, need to tune
|
||||
vm.set_elf(&prog)?;
|
||||
vm.register_helper_ex("sol_log", Some(helper_sol_log_verify), helper_sol_log)?;
|
||||
vm.register_helper_ex("sol_log_", Some(helper_sol_log_verify), helper_sol_log)?;
|
||||
vm.register_helper_ex("sol_log_64", None, helper_sol_log_u64)?;
|
||||
vm.register_helper_ex("sol_log_64_", None, helper_sol_log_u64)?;
|
||||
Ok(vm)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user