add sha256 syscall (#12569)
This commit is contained in:
parent
89424b29bf
commit
058bca6632
|
@ -1912,6 +1912,13 @@ dependencies = [
|
|||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-sha256"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-sysval"
|
||||
version = "1.4.0"
|
||||
|
|
|
@ -54,6 +54,7 @@ members = [
|
|||
"rust/param_passing_dep",
|
||||
"rust/rand",
|
||||
"rust/sanity",
|
||||
"rust/sha256",
|
||||
"rust/sysval",
|
||||
]
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ fn main() {
|
|||
"param_passing",
|
||||
"rand",
|
||||
"sanity",
|
||||
"sha256",
|
||||
"sysval",
|
||||
];
|
||||
for program in rust_programs.iter() {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @brief SHA256 Syscall test
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
extern uint64_t entrypoint(const uint8_t *input) {
|
||||
|
||||
uint8_t result[SHA256_RESULT_LENGTH];
|
||||
uint8_t expected[] = {0x9f, 0xa2, 0x7e, 0x8f, 0x7b, 0xc1, 0xec, 0xe8,
|
||||
0xae, 0x7b, 0x9a, 0x91, 0x46, 0x53, 0x20, 0xf,
|
||||
0x1c, 0x22, 0x8e, 0x56, 0x10, 0x30, 0x59, 0xfd,
|
||||
0x35, 0x8d, 0x57, 0x54, 0x96, 0x47, 0x2c, 0xc9};
|
||||
|
||||
uint8_t bytes1[] = {'G', 'a', 'g', 'g', 'a', 'b', 'l', 'a',
|
||||
'g', 'h', 'b', 'l', 'a', 'g', 'h', '!'};
|
||||
uint8_t bytes2[] = {'f', 'l', 'u', 'r', 'b', 'o', 's'};
|
||||
const SolBytes bytes[] = {{bytes1, SOL_ARRAY_SIZE(bytes1)},
|
||||
{bytes2, SOL_ARRAY_SIZE(bytes2)}};
|
||||
|
||||
sol_sha256(bytes, SOL_ARRAY_SIZE(bytes), result);
|
||||
|
||||
sol_assert(0 == sol_memcmp(result, expected, SHA256_RESULT_LENGTH));
|
||||
|
||||
return SUCCESS;
|
||||
}
|
|
@ -18,6 +18,16 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||
*val = *val + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// // Uncomment for SHA256 syscall
|
||||
// {
|
||||
// uint8_t result[SHA256_RESULT_LENGTH];
|
||||
// uint8_t bytes1[1024];
|
||||
// const SolBytes bytes[] = {{bytes1, SOL_ARRAY_SIZE(bytes1)}};
|
||||
|
||||
// sol_sha256(bytes, SOL_ARRAY_SIZE(bytes), result);
|
||||
// *val = result[0];
|
||||
// }
|
||||
}
|
||||
return *val;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
# Note: This crate must be built using do.sh
|
||||
|
||||
[package]
|
||||
name = "solana-bpf-rust-sha256"
|
||||
version = "1.4.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/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../../../../sdk/", version = "1.4.0", default-features = false }
|
||||
|
||||
[features]
|
||||
program = ["solana-sdk/program"]
|
||||
default = ["program", "solana-sdk/default"]
|
||||
|
||||
[lib]
|
||||
name = "solana_bpf_rust_sha256"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,19 @@
|
|||
//! @brief SHA256 Syscall test
|
||||
|
||||
extern crate solana_sdk;
|
||||
use solana_sdk::{
|
||||
hash::{hashv, Hasher},
|
||||
info,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
|
||||
info!("sha256");
|
||||
|
||||
let vals = &["Gaggablaghblagh!".as_ref(), "flurbos".as_ref()];
|
||||
let mut hasher = Hasher::default();
|
||||
hasher.hashv(vals);
|
||||
assert_eq!(hashv(vals), hasher.result());
|
||||
|
||||
0
|
||||
}
|
|
@ -136,6 +136,7 @@ fn test_program_bpf_sanity() {
|
|||
("relative_call", true),
|
||||
("sanity", true),
|
||||
("sanity++", true),
|
||||
("sha256", true),
|
||||
("struct_pass", true),
|
||||
("struct_ret", true),
|
||||
]);
|
||||
|
@ -154,6 +155,7 @@ fn test_program_bpf_sanity() {
|
|||
("solana_bpf_rust_param_passing", true),
|
||||
("solana_bpf_rust_rand", true),
|
||||
("solana_bpf_rust_sanity", true),
|
||||
("solana_bpf_rust_sha256", true),
|
||||
("solana_bpf_rust_sysval", true),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -601,6 +601,8 @@ mod tests {
|
|||
create_program_address_units: 1500,
|
||||
invoke_units: 1000,
|
||||
max_invoke_depth: 2,
|
||||
sha256_base_cost: 85,
|
||||
sha256_byte_cost: 1,
|
||||
},
|
||||
Rc::new(RefCell::new(Executors::default())),
|
||||
None,
|
||||
|
|
|
@ -7,6 +7,7 @@ use solana_rbpf::{
|
|||
vm::{EbpfVm, SyscallObject},
|
||||
};
|
||||
use solana_runtime::{
|
||||
feature_set::sha256_syscall_enabled,
|
||||
message_processor::MessageProcessor,
|
||||
process_instruction::{ComputeMeter, InvokeContext, Logger},
|
||||
};
|
||||
|
@ -16,6 +17,7 @@ use solana_sdk::{
|
|||
account_info::AccountInfo,
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
hash::{Hasher, HASH_BYTES},
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
message::Message,
|
||||
program_error::ProgramError,
|
||||
|
@ -117,6 +119,18 @@ pub fn register_syscalls<'a>(
|
|||
}),
|
||||
)?;
|
||||
|
||||
if invoke_context.is_feature_active(&sha256_syscall_enabled::id()) {
|
||||
vm.register_syscall_with_context_ex(
|
||||
"sol_sha256",
|
||||
Box::new(SyscallSha256 {
|
||||
sha256_base_cost: compute_budget.sha256_base_cost,
|
||||
sha256_byte_cost: compute_budget.sha256_byte_cost,
|
||||
compute_meter: invoke_context.get_compute_meter(),
|
||||
loader_id,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
|
||||
vm.register_syscall_with_context_ex(
|
||||
"sol_create_program_address",
|
||||
Box::new(SyscallCreateProgramAddress {
|
||||
|
@ -460,6 +474,43 @@ impl<'a> SyscallObject<BPFError> for SyscallCreateProgramAddress<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// SHA256
|
||||
pub struct SyscallSha256<'a> {
|
||||
sha256_base_cost: u64,
|
||||
sha256_byte_cost: u64,
|
||||
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
||||
loader_id: &'a Pubkey,
|
||||
}
|
||||
impl<'a> SyscallObject<BPFError> for SyscallSha256<'a> {
|
||||
fn call(
|
||||
&mut self,
|
||||
vals_addr: u64,
|
||||
vals_len: u64,
|
||||
result_addr: u64,
|
||||
_arg4: u64,
|
||||
_arg5: u64,
|
||||
ro_regions: &[MemoryRegion],
|
||||
rw_regions: &[MemoryRegion],
|
||||
) -> Result<u64, EbpfError<BPFError>> {
|
||||
self.compute_meter.consume(self.sha256_base_cost)?;
|
||||
let hash_result =
|
||||
translate_slice_mut!(u8, result_addr, HASH_BYTES, rw_regions, self.loader_id)?;
|
||||
let mut hasher = Hasher::default();
|
||||
if vals_len > 0 {
|
||||
let vals = translate_slice!(&[u8], vals_addr, vals_len, ro_regions, self.loader_id)?;
|
||||
for val in vals.iter() {
|
||||
let bytes =
|
||||
translate_slice!(u8, val.as_ptr(), val.len(), ro_regions, self.loader_id)?;
|
||||
self.compute_meter
|
||||
.consume(self.sha256_byte_cost * (val.len() as u64 / 2))?;
|
||||
hasher.hash(bytes);
|
||||
}
|
||||
}
|
||||
hash_result.copy_from_slice(&hasher.result().to_bytes());
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-program invocation syscalls
|
||||
|
||||
struct AccountReferences<'a> {
|
||||
|
@ -1095,6 +1146,7 @@ fn call<'a>(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::{MockComputeMeter, MockLogger};
|
||||
use solana_sdk::hash::hashv;
|
||||
|
||||
macro_rules! assert_access_violation {
|
||||
($result:expr, $va:expr, $len:expr) => {
|
||||
|
@ -1449,4 +1501,115 @@ mod tests {
|
|||
check_alignment::<u64>();
|
||||
check_alignment::<u128>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_syscall_sha256() {
|
||||
let bytes1 = "Gaggablaghblagh!";
|
||||
let bytes2 = "flurbos";
|
||||
|
||||
struct MockSlice {
|
||||
pub addr: u64,
|
||||
pub len: usize,
|
||||
}
|
||||
let mock_slice1 = MockSlice {
|
||||
addr: 4096,
|
||||
len: bytes1.len(),
|
||||
};
|
||||
let mock_slice2 = MockSlice {
|
||||
addr: 8192,
|
||||
len: bytes2.len(),
|
||||
};
|
||||
let bytes_to_hash = [mock_slice1, mock_slice2]; // TODO
|
||||
let ro_len = bytes_to_hash.len() as u64;
|
||||
let ro_va = 96;
|
||||
let ro_regions = &mut [
|
||||
MemoryRegion {
|
||||
addr_host: bytes1.as_ptr() as *const _ as u64,
|
||||
addr_vm: 4096,
|
||||
len: bytes1.len() as u64,
|
||||
},
|
||||
MemoryRegion {
|
||||
addr_host: bytes2.as_ptr() as *const _ as u64,
|
||||
addr_vm: 8192,
|
||||
len: bytes2.len() as u64,
|
||||
},
|
||||
MemoryRegion {
|
||||
addr_host: bytes_to_hash.as_ptr() as *const _ as u64,
|
||||
addr_vm: 96,
|
||||
len: 32,
|
||||
},
|
||||
];
|
||||
ro_regions.sort_by(|a, b| a.addr_vm.cmp(&b.addr_vm));
|
||||
let hash_result = [0; HASH_BYTES];
|
||||
let rw_va = 192;
|
||||
let rw_regions = &[MemoryRegion {
|
||||
addr_host: hash_result.as_ptr() as *const _ as u64,
|
||||
addr_vm: rw_va,
|
||||
len: HASH_BYTES as u64,
|
||||
}];
|
||||
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
|
||||
Rc::new(RefCell::new(MockComputeMeter {
|
||||
remaining: (bytes1.len() + bytes2.len()) as u64,
|
||||
}));
|
||||
let mut syscall = SyscallSha256 {
|
||||
sha256_base_cost: 0,
|
||||
sha256_byte_cost: 2,
|
||||
compute_meter,
|
||||
loader_id: &bpf_loader_deprecated::id(),
|
||||
};
|
||||
|
||||
syscall
|
||||
.call(ro_va, ro_len, rw_va, 0, 0, ro_regions, rw_regions)
|
||||
.unwrap();
|
||||
|
||||
let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes();
|
||||
assert_eq!(hash_result, hash_local);
|
||||
|
||||
assert_access_violation!(
|
||||
syscall.call(
|
||||
ro_va - 1, // AccessViolation
|
||||
ro_len,
|
||||
rw_va,
|
||||
0,
|
||||
0,
|
||||
ro_regions,
|
||||
rw_regions
|
||||
),
|
||||
ro_va - 1,
|
||||
ro_len
|
||||
);
|
||||
assert_access_violation!(
|
||||
syscall.call(
|
||||
ro_va,
|
||||
ro_len + 1, // AccessViolation
|
||||
rw_va,
|
||||
0,
|
||||
0,
|
||||
ro_regions,
|
||||
rw_regions
|
||||
),
|
||||
ro_va,
|
||||
ro_len + 1
|
||||
);
|
||||
assert_access_violation!(
|
||||
syscall.call(
|
||||
ro_va,
|
||||
ro_len,
|
||||
rw_va - 1, // AccessViolation
|
||||
0,
|
||||
0,
|
||||
ro_regions,
|
||||
rw_regions
|
||||
),
|
||||
rw_va - 1,
|
||||
HASH_BYTES as u64
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Err(EbpfError::UserError(BPFError::SyscallError(
|
||||
SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded)
|
||||
))),
|
||||
syscall.call(ro_va, ro_len, rw_va, 0, 0, ro_regions, rw_regions)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ pub mod compute_budget_config2 {
|
|||
solana_sdk::declare_id!("HxvjqDSiF5sYdSYuCXsUnS8UeAoWsMT9iGoFP8pgV1mB");
|
||||
}
|
||||
|
||||
pub mod sha256_syscall_enabled {
|
||||
solana_sdk::declare_id!("D7KfP7bZxpkYtD4Pc38t9htgs1k5k47Yhxe4rp6WDVi8");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -43,6 +47,7 @@ lazy_static! {
|
|||
(spl_token_v2_multisig_fix::id(), "spl-token multisig fix"),
|
||||
(bpf_loader2_program::id(), "bpf_loader2 program"),
|
||||
(compute_budget_config2::id(), "1ms compute budget"),
|
||||
(sha256_syscall_enabled::id(), "sha256 syscall")
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
|
@ -427,6 +427,8 @@ impl MessageProcessor {
|
|||
create_program_address_units: 0,
|
||||
invoke_units: 0,
|
||||
max_invoke_depth: 2,
|
||||
sha256_base_cost: 0,
|
||||
sha256_byte_cost: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,10 @@ pub struct ComputeBudget {
|
|||
pub invoke_units: u64,
|
||||
/// Maximum cross-program invocation depth allowed including the orignal caller
|
||||
pub max_invoke_depth: usize,
|
||||
/// Base number of compute units consumed to call sha256
|
||||
pub sha256_base_cost: u64,
|
||||
/// Incremental number of units consumed by sha256 (based on bytes)
|
||||
pub sha256_byte_cost: u64,
|
||||
}
|
||||
impl Default for ComputeBudget {
|
||||
fn default() -> Self {
|
||||
|
@ -98,6 +102,8 @@ impl Default for ComputeBudget {
|
|||
create_program_address_units: 1500,
|
||||
invoke_units: 1000,
|
||||
max_invoke_depth: 2,
|
||||
sha256_base_cost: 85,
|
||||
sha256_byte_cost: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,6 +391,32 @@ static bool sol_deserialize(
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte array pointer and string
|
||||
*/
|
||||
typedef struct {
|
||||
const uint8_t *addr; /** bytes */
|
||||
uint64_t len; /** number of bytes*/
|
||||
} SolBytes;
|
||||
|
||||
/**
|
||||
* Length of a sha256 hash result
|
||||
*/
|
||||
#define SHA256_RESULT_LENGTH 32
|
||||
|
||||
/**
|
||||
* Sha256
|
||||
*
|
||||
* @param bytes Array of byte arrays
|
||||
* @param bytes_len Number of byte arrays
|
||||
* @param result 32 byte array to hold the result
|
||||
*/
|
||||
static uint64_t sol_sha256(
|
||||
const SolBytes *bytes,
|
||||
int bytes_len,
|
||||
const uint8_t *result
|
||||
);
|
||||
|
||||
/**
|
||||
* Account Meta
|
||||
*/
|
||||
|
@ -428,7 +454,7 @@ typedef struct {
|
|||
uint64_t len; /** Number of seeds */
|
||||
} SolSignerSeeds;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Create a program address
|
||||
*
|
||||
* @param seeds Seed bytes used to sign program accounts
|
||||
|
@ -448,7 +474,7 @@ static uint64_t sol_create_program_address(
|
|||
* * @{
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
* Invoke another program and sign for some of the keys
|
||||
*
|
||||
* @param instruction Instruction to process
|
||||
|
@ -480,7 +506,7 @@ static uint64_t sol_invoke_signed(
|
|||
signers_seeds_len
|
||||
);
|
||||
}
|
||||
/*
|
||||
/**
|
||||
* Invoke another program
|
||||
*
|
||||
* @param instruction Instruction to process
|
||||
|
|
|
@ -103,9 +103,27 @@ impl Hash {
|
|||
|
||||
/// Return a Sha256 hash for the given data.
|
||||
pub fn hashv(vals: &[&[u8]]) -> Hash {
|
||||
let mut hasher = Hasher::default();
|
||||
hasher.hashv(vals);
|
||||
hasher.result()
|
||||
#[cfg(not(feature = "program"))]
|
||||
{
|
||||
let mut hasher = Hasher::default();
|
||||
hasher.hashv(vals);
|
||||
hasher.result()
|
||||
}
|
||||
#[cfg(feature = "program")]
|
||||
{
|
||||
extern "C" {
|
||||
fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
|
||||
};
|
||||
let mut hash_result = [0; HASH_BYTES];
|
||||
unsafe {
|
||||
sol_sha256(
|
||||
vals as *const _ as *const u8,
|
||||
vals.len() as u64,
|
||||
&mut hash_result as *mut _ as *mut u8,
|
||||
);
|
||||
}
|
||||
Hash::new_from_array(hash_result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a Sha256 hash for the given data.
|
||||
|
|
Loading…
Reference in New Issue