Add memory operation syscalls (#16447)
This commit is contained in:
parent
38d71c37da
commit
2b50529265
|
@ -2958,6 +2958,16 @@ name = "solana-bpf-rust-mem"
|
|||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"solana-program 1.8.0",
|
||||
"solana-program-test",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-membuiltins"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"solana-bpf-rust-mem",
|
||||
"solana-program 1.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -64,6 +64,7 @@ members = [
|
|||
"rust/many_args",
|
||||
"rust/many_args_dep",
|
||||
"rust/mem",
|
||||
"rust/membuiltins",
|
||||
"rust/noop",
|
||||
"rust/panic",
|
||||
"rust/param_passing",
|
||||
|
|
|
@ -78,6 +78,7 @@ fn main() {
|
|||
"iter",
|
||||
"many_args",
|
||||
"mem",
|
||||
"membuiltins",
|
||||
"noop",
|
||||
"panic",
|
||||
"param_passing",
|
||||
|
|
|
@ -9,11 +9,18 @@ homepage = "https://solana.com/"
|
|||
documentation = "https://docs.rs/solana-bpf-rust-mem"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = { path = "../../../../program-test", version = "=1.8.0" }
|
||||
solana-sdk = { path = "../../../../sdk", version = "=1.8.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
//! @brief Test mem functions
|
||||
|
||||
use crate::{run_mem_tests, MemOps};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
program_memory::{sol_memcmp, sol_memcpy, sol_memmove, sol_memset},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
// Via syscalls
|
||||
#[derive(Default)]
|
||||
struct MemOpSyscalls();
|
||||
impl MemOps for MemOpSyscalls {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) {
|
||||
sol_memcpy(dst, src, n)
|
||||
}
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) {
|
||||
sol_memmove(dst, src, n)
|
||||
}
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize) {
|
||||
sol_memset(s, c, n)
|
||||
}
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
sol_memcmp(s1, s2, n)
|
||||
}
|
||||
}
|
||||
run_mem_tests(MemOpSyscalls::default());
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,190 +1,165 @@
|
|||
//! @brief Test builtin mem functions
|
||||
//! @brief Test mem functions
|
||||
|
||||
#![cfg(target_arch = "bpf")]
|
||||
#![feature(rustc_private)]
|
||||
#[cfg(not(feature = "no-entrypoint"))]
|
||||
pub mod entrypoint;
|
||||
|
||||
extern crate compiler_builtins;
|
||||
use solana_program::{custom_panic_default, entrypoint::SUCCESS};
|
||||
pub trait MemOps {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize);
|
||||
/// # Safety
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize);
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize);
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
pub fn run_mem_tests<T: MemOps>(mem_ops: T) {
|
||||
// memcpy
|
||||
let src = &[1_u8; 18];
|
||||
let dst = &mut [0_u8; 1];
|
||||
mem_ops.memcpy(dst, src, 1);
|
||||
assert_eq!(&src[..1], dst);
|
||||
let dst = &mut [0_u8; 3];
|
||||
mem_ops.memcpy(dst, src, 3);
|
||||
assert_eq!(&src[..3], dst);
|
||||
let dst = &mut [0_u8; 8];
|
||||
mem_ops.memcpy(dst, src, 8);
|
||||
assert_eq!(&src[..8], dst);
|
||||
let dst = &mut [0_u8; 9];
|
||||
mem_ops.memcpy(dst, src, 9);
|
||||
assert_eq!(&src[..9], dst);
|
||||
let dst = &mut [0_u8; 16];
|
||||
mem_ops.memcpy(dst, src, 16);
|
||||
assert_eq!(&src[..16], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(dst, src, 18);
|
||||
assert_eq!(&src[..18], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(dst, &src[1..], 17);
|
||||
assert_eq!(&src[1..], &dst[..17]);
|
||||
let dst = &mut [0_u8; 18];
|
||||
mem_ops.memcpy(&mut dst[1..], &src[1..], 17);
|
||||
assert_eq!(&src[1..], &dst[1..]);
|
||||
|
||||
// memmove
|
||||
unsafe {
|
||||
// memcpy
|
||||
let src = &mut [1_u8; 18];
|
||||
let dst = &mut [0_u8; 1];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 1);
|
||||
assert_eq!(&src[..1], dst);
|
||||
let dst = &mut [0_u8; 3];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 3);
|
||||
assert_eq!(&src[..3], dst);
|
||||
let dst = &mut [0_u8; 8];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 8);
|
||||
assert_eq!(&src[..8], dst);
|
||||
let dst = &mut [0_u8; 9];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 9);
|
||||
assert_eq!(&src[..9], dst);
|
||||
let dst = &mut [0_u8; 16];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 16);
|
||||
assert_eq!(&src[..16], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 18);
|
||||
assert_eq!(&src[..18], dst);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[0] as *mut u8, 17);
|
||||
assert_eq!(&src[1..], &dst[1..]);
|
||||
let dst = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[1] as *mut u8, 17);
|
||||
assert_eq!(&src[1..], &dst[..17]);
|
||||
|
||||
// memmove
|
||||
let buf = &mut [1_u8, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1);
|
||||
assert_eq!(buf[0], buf[1]);
|
||||
let buf = &mut [1_u8, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1);
|
||||
assert_eq!(buf[0], buf[1]);
|
||||
let buf = &mut [1_u8, 1, 1, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3);
|
||||
assert_eq!(buf[..3], buf[3..]);
|
||||
let buf = &mut [1_u8, 1, 1, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3);
|
||||
mem_ops.memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3);
|
||||
assert_eq!(buf[..3], buf[3..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8);
|
||||
assert_eq!(buf[..8], buf[8..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8);
|
||||
mem_ops.memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8);
|
||||
assert_eq!(buf[..8], buf[8..]);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9);
|
||||
assert_eq!(buf[..9], buf[9..]);
|
||||
let buf = &mut [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
assert_eq!(&mut [0_u8, 0, 1, 2, 3, 4, 5, 6, 7, 8], buf);
|
||||
let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
compiler_builtins::mem::memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
mem_ops.memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9);
|
||||
assert_eq!(buf[..9], buf[9..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16);
|
||||
assert_eq!(buf[..16], buf[16..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16);
|
||||
mem_ops.memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16);
|
||||
assert_eq!(buf[..16], buf[16..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18);
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18);
|
||||
assert_eq!(buf[..18], buf[18..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18);
|
||||
mem_ops.memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18);
|
||||
assert_eq!(buf[..18], buf[18..]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17);
|
||||
mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17);
|
||||
assert_eq!(buf[1..17], buf[18..34]);
|
||||
let buf = &mut [
|
||||
1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
compiler_builtins::mem::memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17);
|
||||
mem_ops.memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17);
|
||||
assert_eq!(buf[..17], buf[19..]);
|
||||
|
||||
// memset
|
||||
let exp = &[1_u8; 18];
|
||||
let buf = &mut [0_u8; 18];
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 1);
|
||||
assert_eq!(exp[..1], buf[..1]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 3);
|
||||
assert_eq!(exp[..3], buf[..3]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 8);
|
||||
assert_eq!(exp[..8], buf[..8]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 9);
|
||||
assert_eq!(exp[..9], buf[..9]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 16);
|
||||
assert_eq!(exp[..16], buf[..16]);
|
||||
compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 18);
|
||||
assert_eq!(exp[..18], buf[..18]);
|
||||
compiler_builtins::mem::memset(&mut buf[1] as *mut u8, 1, 17);
|
||||
assert_eq!(exp[1..18], buf[1..18]);
|
||||
|
||||
// memcmp
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8] as *const u8, &[1_u8] as *const u8, 1)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 1] as *const u8,
|
||||
3
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
0,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8,
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8,
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8,
|
||||
10
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
0,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[0_u8; 8] as *const u8, 8)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[1_u8; 8] as *const u8, 8)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 16] as *const u8, &[1_u8; 16] as *const u8, 16)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&[0_u8; 18] as *const u8, &[1_u8; 18] as *const u8, 18)
|
||||
);
|
||||
let one = &[0_u8; 18];
|
||||
let two = &[1_u8; 18];
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[0] as *const u8, 17)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[1] as *const u8, 17)
|
||||
);
|
||||
let buf = &mut [0_u8, 0, 0, 1, 1, 1, 1, 1, 0];
|
||||
mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 5);
|
||||
assert_eq!(buf, &mut [1, 1, 1, 1, 1, 1, 1, 1, 0]);
|
||||
}
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
// memset
|
||||
let exp = &[1_u8; 18];
|
||||
let buf = &mut [0_u8; 18];
|
||||
mem_ops.memset(&mut buf[0..], 1, 1);
|
||||
assert_eq!(exp[..1], buf[..1]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 3);
|
||||
assert_eq!(exp[..3], buf[..3]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 8);
|
||||
assert_eq!(exp[..8], buf[..8]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 9);
|
||||
assert_eq!(exp[..9], buf[..9]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 16);
|
||||
assert_eq!(exp[..16], buf[..16]);
|
||||
mem_ops.memset(&mut buf[0..], 1, 18);
|
||||
assert_eq!(exp[..18], buf[..18]);
|
||||
mem_ops.memset(&mut buf[1..], 1, 17);
|
||||
assert_eq!(exp[1..18], buf[1..18]);
|
||||
|
||||
custom_panic_default!();
|
||||
// memcmp
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8], &[1_u8], 1));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8, 0, 0], &[0_u8, 0, 1], 3));
|
||||
assert_eq!(
|
||||
0,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
9
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
-1,
|
||||
mem_ops.memcmp(
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
&[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
10
|
||||
)
|
||||
);
|
||||
assert_eq!(0, mem_ops.memcmp(&[0_u8; 8], &[0_u8; 8], 8));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 8], &[1_u8; 8], 8));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 16], &[1_u8; 16], 16));
|
||||
assert_eq!(-1, mem_ops.memcmp(&[0_u8; 18], &[1_u8; 18], 18));
|
||||
let one = &[0_u8; 18];
|
||||
let two = &[1_u8; 18];
|
||||
assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[0..], 17));
|
||||
assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[1..], 17));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use solana_bpf_rust_mem::entrypoint::process_instruction;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{
|
||||
instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mem() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let program_test = ProgramTest::new(
|
||||
"solana_bpf_rust_mem",
|
||||
program_id,
|
||||
processor!(process_instruction),
|
||||
);
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&[Instruction::new_with_bincode(program_id, &(), vec![])],
|
||||
Some(&payer.pubkey()),
|
||||
);
|
||||
transaction.sign(&[&payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "solana-bpf-rust-membuiltins"
|
||||
version = "1.8.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-bpf-rust-mem"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-bpf-rust-mem = { path = "../mem", version = "=1.8.0", features = [ "no-entrypoint" ] }
|
||||
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -0,0 +1,39 @@
|
|||
//! @brief Test builtin mem functions
|
||||
|
||||
#![cfg(target_arch = "bpf")]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate compiler_builtins;
|
||||
use solana_bpf_rust_mem::{run_mem_tests, MemOps};
|
||||
use solana_program::{custom_panic_default, entrypoint::SUCCESS};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
#[derive(Default)]
|
||||
struct MemOpSyscalls();
|
||||
impl MemOps for MemOpSyscalls {
|
||||
fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) {
|
||||
unsafe {
|
||||
compiler_builtins::mem::memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
|
||||
}
|
||||
}
|
||||
unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) {
|
||||
compiler_builtins::mem::memmove(dst, src, n);
|
||||
}
|
||||
fn memset(&self, s: &mut [u8], c: u8, n: usize) {
|
||||
unsafe {
|
||||
compiler_builtins::mem::memset(s.as_mut_ptr(), c as i32, n);
|
||||
}
|
||||
}
|
||||
fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
unsafe { compiler_builtins::mem::memcmp(s1.as_ptr(), s2.as_ptr(), n) }
|
||||
}
|
||||
}
|
||||
let mem_ops = MemOpSyscalls::default();
|
||||
|
||||
run_mem_tests(mem_ops);
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
custom_panic_default!();
|
|
@ -12,7 +12,7 @@ use solana_sdk::{
|
|||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_noop() {
|
||||
async fn test_sysvars() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let program_test = ProgramTest::new(
|
||||
"solana_bpf_rust_sysvar",
|
||||
|
|
|
@ -454,7 +454,7 @@ fn test_program_bpf_sanity() {
|
|||
("solana_bpf_rust_external_spend", false),
|
||||
("solana_bpf_rust_iter", true),
|
||||
("solana_bpf_rust_many_args", true),
|
||||
("solana_bpf_rust_mem", true),
|
||||
("solana_bpf_rust_membuiltins", true),
|
||||
("solana_bpf_rust_noop", true),
|
||||
("solana_bpf_rust_panic", false),
|
||||
("solana_bpf_rust_param_passing", true),
|
||||
|
@ -1276,7 +1276,8 @@ fn assert_instruction_count() {
|
|||
("solana_bpf_rust_external_spend", 521),
|
||||
("solana_bpf_rust_iter", 724),
|
||||
("solana_bpf_rust_many_args", 237),
|
||||
("solana_bpf_rust_mem", 2297),
|
||||
("solana_bpf_rust_mem", 3143),
|
||||
("solana_bpf_rust_membuiltins", 4069),
|
||||
("solana_bpf_rust_noop", 495),
|
||||
("solana_bpf_rust_param_passing", 46),
|
||||
("solana_bpf_rust_rand", 498),
|
||||
|
|
|
@ -20,8 +20,8 @@ use solana_sdk::{
|
|||
epoch_schedule::EpochSchedule,
|
||||
feature_set::{
|
||||
cpi_data_cost, demote_sysvar_write_locks, enforce_aligned_host_addrs,
|
||||
keccak256_syscall_enabled, set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall,
|
||||
update_data_on_realloc,
|
||||
keccak256_syscall_enabled, memory_ops_syscalls, set_upgrade_authority_via_cpi_enabled,
|
||||
sysvar_via_syscall, update_data_on_realloc,
|
||||
},
|
||||
hash::{Hasher, HASH_BYTES},
|
||||
ic_msg,
|
||||
|
@ -74,6 +74,8 @@ pub enum SyscallError {
|
|||
InstructionTooLarge(usize, usize),
|
||||
#[error("Too many accounts passed to inner instruction")]
|
||||
TooManyAccounts,
|
||||
#[error("Overlapping copy")]
|
||||
CopyOverlapping,
|
||||
}
|
||||
impl From<SyscallError> for EbpfError<BpfError> {
|
||||
fn from(error: SyscallError) -> Self {
|
||||
|
@ -145,10 +147,20 @@ pub fn register_syscalls(
|
|||
.register_syscall_by_name(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?;
|
||||
}
|
||||
|
||||
if invoke_context.is_feature_active(&memory_ops_syscalls::id()) {
|
||||
syscall_registry.register_syscall_by_name(b"sol_memcpy_", SyscallMemcpy::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memmove_", SyscallMemmove::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memcmp_", SyscallMemcmp::call)?;
|
||||
syscall_registry.register_syscall_by_name(b"sol_memset_", SyscallMemset::call)?;
|
||||
}
|
||||
|
||||
// Cross-program invocation syscalls
|
||||
syscall_registry
|
||||
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
|
||||
syscall_registry
|
||||
.register_syscall_by_name(b"sol_invoke_signed_rust", SyscallInvokeSignedRust::call)?;
|
||||
|
||||
// Memory allocator
|
||||
syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?;
|
||||
|
||||
Ok(syscall_registry)
|
||||
|
@ -268,6 +280,43 @@ pub fn bind_syscall_context_objects<'a>(
|
|||
}),
|
||||
);
|
||||
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemcpy {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemmove {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemcmp {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
);
|
||||
bind_feature_gated_syscall_context_object!(
|
||||
vm,
|
||||
invoke_context.is_feature_active(&memory_ops_syscalls::id()),
|
||||
Box::new(SyscallMemset {
|
||||
cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit,
|
||||
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));
|
||||
|
@ -322,7 +371,6 @@ pub fn bind_syscall_context_objects<'a>(
|
|||
)?;
|
||||
|
||||
// Memory allocator
|
||||
|
||||
vm.bind_syscall_context_object(
|
||||
Box::new(SyscallAllocFree {
|
||||
aligned: *loader_id != bpf_loader_deprecated::id(),
|
||||
|
@ -1137,6 +1185,150 @@ impl<'a> SyscallObject<BpfError> for SyscallKeccak256<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// memcpy
|
||||
pub struct SyscallMemcpy<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemcpy<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
dst_addr: u64,
|
||||
src_addr: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
// cannot be overlapping
|
||||
if dst_addr + n > src_addr && src_addr > dst_addr {
|
||||
*result = Err(SyscallError::CopyOverlapping.into());
|
||||
return;
|
||||
}
|
||||
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let dst = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let src = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, src_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), n as usize);
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memcpy
|
||||
pub struct SyscallMemmove<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemmove<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
dst_addr: u64,
|
||||
src_addr: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let dst = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let src = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, src_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
unsafe {
|
||||
std::ptr::copy(src.as_ptr(), dst.as_mut_ptr(), n as usize);
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memcmp
|
||||
pub struct SyscallMemcmp<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemcmp<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
s1_addr: u64,
|
||||
s2_addr: u64,
|
||||
n: u64,
|
||||
cmp_result_addr: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let s1 = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, s1_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let s2 = question_mark!(
|
||||
translate_slice::<u8>(memory_mapping, s2_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let cmp_result = question_mark!(
|
||||
translate_type_mut::<i32>(memory_mapping, cmp_result_addr, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
let mut i = 0;
|
||||
while i < n as usize {
|
||||
let a = s1[i];
|
||||
let b = s2[i];
|
||||
if a != b {
|
||||
*cmp_result = a as i32 - b as i32;
|
||||
*result = Ok(0);
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
*cmp_result = 0;
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
/// memset
|
||||
pub struct SyscallMemset<'a> {
|
||||
cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BpfError> for SyscallMemset<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
s_addr: u64,
|
||||
c: u64,
|
||||
n: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
memory_mapping: &MemoryMapping,
|
||||
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||
) {
|
||||
question_mark!(self.compute_meter.consume(n / self.cost), result);
|
||||
let s = question_mark!(
|
||||
translate_slice_mut::<u8>(memory_mapping, s_addr, n, self.loader_id, true),
|
||||
result
|
||||
);
|
||||
for val in s.iter_mut().take(n as usize) {
|
||||
*val = c as u8;
|
||||
}
|
||||
*result = Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-program invocation syscalls
|
||||
|
||||
struct AccountReferences<'a> {
|
||||
|
|
|
@ -30,6 +30,7 @@ pub mod native_token;
|
|||
pub mod nonce;
|
||||
pub mod program;
|
||||
pub mod program_error;
|
||||
pub mod program_memory;
|
||||
pub mod program_option;
|
||||
pub mod program_pack;
|
||||
pub mod program_stubs;
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
//! @brief Solana Rust-based BPF memory operations
|
||||
|
||||
/// Memcpy
|
||||
///
|
||||
/// @param dst - Destination
|
||||
/// @param src - Source
|
||||
/// @param n - Number of bytes to copy
|
||||
#[inline]
|
||||
pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64);
|
||||
}
|
||||
unsafe {
|
||||
sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
|
||||
}
|
||||
|
||||
/// Memmove
|
||||
///
|
||||
/// @param dst - Destination
|
||||
/// @param src - Source
|
||||
/// @param n - Number of bytes to copy
|
||||
///
|
||||
/// # Safety
|
||||
#[inline]
|
||||
pub unsafe fn sol_memmove(dst: *mut u8, src: *mut u8, n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64);
|
||||
}
|
||||
sol_memmove_(dst, src, n as u64);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memmove(dst, src, n);
|
||||
}
|
||||
|
||||
/// Memcmp
|
||||
///
|
||||
/// @param s1 - Slice to be compared
|
||||
/// @param s2 - Slice to be compared
|
||||
/// @param n - Number of bytes to compare
|
||||
#[inline]
|
||||
pub fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
|
||||
let mut result = 0;
|
||||
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32);
|
||||
}
|
||||
unsafe {
|
||||
sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memcmp(s1.as_ptr(), s2.as_ptr(), n, &mut result as *mut i32);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Memset
|
||||
///
|
||||
/// @param s1 - Slice to be compared
|
||||
/// @param s2 - Slice to be compared
|
||||
/// @param n - Number of bytes to compare
|
||||
#[inline]
|
||||
pub fn sol_memset(s: &mut [u8], c: u8, n: usize) {
|
||||
#[cfg(target_arch = "bpf")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_memset_(s: *mut u8, c: u8, n: u64);
|
||||
}
|
||||
unsafe {
|
||||
sol_memset_(s.as_mut_ptr(), c, n as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
crate::program_stubs::sol_memset(s.as_mut_ptr(), c, n);
|
||||
}
|
|
@ -12,12 +12,13 @@ lazy_static::lazy_static! {
|
|||
static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {})));
|
||||
}
|
||||
|
||||
// The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in
|
||||
// alternatives
|
||||
// The default syscall stubs may not do much, but `set_syscalls()` can be used
|
||||
// to swap in alternatives
|
||||
pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
|
||||
std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
|
||||
}
|
||||
|
||||
#[allow(clippy::integer_arithmetic)]
|
||||
pub trait SyscallStubs: Sync + Send {
|
||||
fn sol_log(&self, message: &str) {
|
||||
println!("{}", message);
|
||||
|
@ -34,7 +35,6 @@ pub trait SyscallStubs: Sync + Send {
|
|||
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
|
@ -47,6 +47,39 @@ pub trait SyscallStubs: Sync + Send {
|
|||
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
// cannot be overlapping
|
||||
if dst as usize + n > src as usize && src as usize > dst as usize {
|
||||
panic!("memcpy does not support oveerlapping regions");
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(src, dst, n as usize);
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memmove(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
std::ptr::copy(src, dst, n as usize);
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memcmp(&self, s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let a = *s1.add(i);
|
||||
let b = *s2.add(i);
|
||||
if a != b {
|
||||
*result = a as i32 - b as i32;
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
*result = 0
|
||||
}
|
||||
/// # Safety
|
||||
unsafe fn sol_memset(&self, s: *mut u8, c: u8, n: usize) {
|
||||
let s = std::slice::from_raw_parts_mut(s, n);
|
||||
for val in s.iter_mut().take(n) {
|
||||
*val = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultSyscallStubs {}
|
||||
|
@ -96,3 +129,27 @@ pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
|
|||
pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
|
||||
SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memmove(dst, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memcmp(s1, s2, n, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sol_memset(s: *mut u8, c: u8, n: usize) {
|
||||
unsafe {
|
||||
SYSCALL_STUBS.read().unwrap().sol_memset(s, c, n);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,10 @@ pub mod stake_program_v4 {
|
|||
solana_sdk::declare_id!("Dc7djyhP9aLfdq2zktpvskeAjpG56msCU1yexpxXiWZb");
|
||||
}
|
||||
|
||||
pub mod memory_ops_syscalls {
|
||||
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -169,6 +173,7 @@ lazy_static! {
|
|||
(update_data_on_realloc::id(), "Retain updated data values modified after realloc via CPI"),
|
||||
(keccak256_syscall_enabled::id(), "keccak256 syscall"),
|
||||
(stake_program_v4::id(), "solana_stake_program v4"),
|
||||
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
Loading…
Reference in New Issue