Add ecrecover syscall (#17720)

Co-authored-by: Anton Lisanin <lisanin.anton@gmail.com>
This commit is contained in:
s-medvedev
2021-07-07 23:15:14 +03:00
committed by GitHub
parent 92c5cdab62
commit 1f288ce527
16 changed files with 381 additions and 3 deletions

View File

@ -2752,6 +2752,7 @@ version = "1.8.0"
dependencies = [
"bincode",
"byteorder 1.4.3",
"libsecp256k1",
"log",
"num-derive",
"num-traits",
@ -3018,6 +3019,13 @@ dependencies = [
"solana-program 1.8.0",
]
[[package]]
name = "solana-bpf-rust-secp256k1-recover"
version = "1.8.0"
dependencies = [
"solana-program 1.8.0",
]
[[package]]
name = "solana-bpf-rust-sha"
version = "1.8.0"
@ -3370,6 +3378,7 @@ dependencies = [
"hex",
"itertools 0.10.1",
"lazy_static",
"libsecp256k1",
"log",
"num-derive",
"num-traits",

View File

@ -73,6 +73,7 @@ members = [
"rust/ro_modify",
"rust/ro_account_modify",
"rust/sanity",
"rust/secp256k1_recover",
"rust/sha",
"rust/spoof1",
"rust/spoof1_system",

View File

@ -86,6 +86,7 @@ fn main() {
"ro_modify",
"ro_account_modify",
"sanity",
"secp256k1_recover",
"sha",
"spoof1",
"spoof1_system",

View File

@ -0,0 +1,38 @@
/**
* @brief Secp256k1Recover Syscall test
*/
#include <solana_sdk.h>
extern uint64_t entrypoint(const uint8_t *input) {
uint8_t result[SECP256K1_RECOVER_RESULT_LENGTH];
uint8_t expected[] = { 0x42, 0xcd, 0x27, 0xe4, 0x0f, 0xdf, 0x7c, 0x97,
0x0a, 0xa2, 0xca, 0x0b, 0x88, 0x5b, 0x96, 0x0f,
0x8b, 0x62, 0x8a, 0x41, 0xa1, 0x81, 0xe7, 0xe6,
0x8e, 0x03, 0xea, 0x0b, 0x84, 0x20, 0x58, 0x9b,
0x32, 0x06, 0xbd, 0x66, 0x2f, 0x75, 0x65, 0xd6,
0x9d, 0xbd, 0x1d, 0x34, 0x29, 0x6a, 0xd9, 0x35,
0x38, 0xed, 0x86, 0x9e, 0x99, 0x20, 0x43, 0xc3,
0xeb, 0xad, 0x65, 0x50, 0xa0, 0x11, 0x6e, 0x5d};
uint8_t hash[] = { 0xde, 0xa5, 0x66, 0xb6, 0x94, 0x3b, 0xe0, 0xe9,
0x62, 0x53, 0xc2, 0x21, 0x5b, 0x1b, 0xac, 0x69,
0xe7, 0xa8, 0x1e, 0xdb, 0x41, 0xc5, 0x02, 0x8b,
0x4f, 0x5c, 0x45, 0xc5, 0x3b, 0x49, 0x54, 0xd0};
uint8_t signature[] = { 0x97, 0xa4, 0xee, 0x31, 0xfe, 0x82, 0x65, 0x72,
0x9f, 0x4a, 0xa6, 0x7d, 0x24, 0xd4, 0xa7, 0x27,
0xf8, 0xc3, 0x15, 0xa4, 0xc8, 0xf9, 0x80, 0xeb,
0x4c, 0x4d, 0x4a, 0xfa, 0x6e, 0xc9, 0x42, 0x41,
0x5d, 0x10, 0xd9, 0xc2, 0x8a, 0x90, 0xe9, 0x92,
0x9c, 0x52, 0x4b, 0x2c, 0xfb, 0x65, 0xdf, 0xbc,
0xf6, 0x8c, 0xfd, 0x68, 0xdb, 0x17, 0xf9, 0x5d,
0x23, 0x5f, 0x96, 0xd8, 0xf0, 0x72, 0x01, 0x2d};
uint64_t recovery_id = 1;
uint64_t result_code = sol_secp256k1_recover(hash, recovery_id, signature, result);
sol_assert(0 == result_code);
sol_assert(0 == sol_memcmp(result, expected, SHA256_RESULT_LENGTH));
return SUCCESS;
}

View File

@ -0,0 +1,19 @@
[package]
name = "solana-bpf-rust-secp256k1-recover"
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-secp256k1-recover"
edition = "2018"
[dependencies]
solana-program = { path = "../../../../sdk/program", version = "=1.8.0" }
[lib]
crate-type = ["cdylib"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,44 @@
//! @brief Secp256k1Recover Syscall test
extern crate solana_program;
use solana_program::{custom_panic_default, msg};
fn test_secp256k1_recover() {
use solana_program::secp256k1_recover::secp256k1_recover;
let expected: [u8; 64] = [
0x42, 0xcd, 0x27, 0xe4, 0x0f, 0xdf, 0x7c, 0x97, 0x0a, 0xa2, 0xca, 0x0b, 0x88, 0x5b, 0x96,
0x0f, 0x8b, 0x62, 0x8a, 0x41, 0xa1, 0x81, 0xe7, 0xe6, 0x8e, 0x03, 0xea, 0x0b, 0x84, 0x20,
0x58, 0x9b, 0x32, 0x06, 0xbd, 0x66, 0x2f, 0x75, 0x65, 0xd6, 0x9d, 0xbd, 0x1d, 0x34, 0x29,
0x6a, 0xd9, 0x35, 0x38, 0xed, 0x86, 0x9e, 0x99, 0x20, 0x43, 0xc3, 0xeb, 0xad, 0x65, 0x50,
0xa0, 0x11, 0x6e, 0x5d,
];
let hash: [u8; 32] = [
0xde, 0xa5, 0x66, 0xb6, 0x94, 0x3b, 0xe0, 0xe9, 0x62, 0x53, 0xc2, 0x21, 0x5b, 0x1b, 0xac,
0x69, 0xe7, 0xa8, 0x1e, 0xdb, 0x41, 0xc5, 0x02, 0x8b, 0x4f, 0x5c, 0x45, 0xc5, 0x3b, 0x49,
0x54, 0xd0,
];
let recovery_id: u8 = 1;
let signature: [u8; 64] = [
0x97, 0xa4, 0xee, 0x31, 0xfe, 0x82, 0x65, 0x72, 0x9f, 0x4a, 0xa6, 0x7d, 0x24, 0xd4, 0xa7,
0x27, 0xf8, 0xc3, 0x15, 0xa4, 0xc8, 0xf9, 0x80, 0xeb, 0x4c, 0x4d, 0x4a, 0xfa, 0x6e, 0xc9,
0x42, 0x41, 0x5d, 0x10, 0xd9, 0xc2, 0x8a, 0x90, 0xe9, 0x92, 0x9c, 0x52, 0x4b, 0x2c, 0xfb,
0x65, 0xdf, 0xbc, 0xf6, 0x8c, 0xfd, 0x68, 0xdb, 0x17, 0xf9, 0x5d, 0x23, 0x5f, 0x96, 0xd8,
0xf0, 0x72, 0x01, 0x2d,
];
let public_key = secp256k1_recover(&hash[..], recovery_id, &signature[..]).unwrap();
assert_eq!(public_key.to_bytes(), expected);
}
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
msg!("secp256k1_recover");
test_secp256k1_recover();
0
}
custom_panic_default!();

View File

@ -432,6 +432,7 @@ fn test_program_bpf_sanity() {
("relative_call", true),
("sanity", true),
("sanity++", true),
("secp256k1_recover", true),
("sha", true),
("struct_pass", true),
("struct_ret", true),
@ -453,6 +454,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_secp256k1_recover", true),
("solana_bpf_rust_sha", true),
]);
}
@ -1283,6 +1285,7 @@ fn assert_instruction_count() {
("relative_call", 10),
("sanity", 169),
("sanity++", 168),
("secp256k1_recover", 357),
("sha", 1040),
("struct_pass", 8),
("struct_ret", 22),
@ -1303,8 +1306,9 @@ fn assert_instruction_count() {
("solana_bpf_rust_noop", 478),
("solana_bpf_rust_param_passing", 46),
("solana_bpf_rust_rand", 481),
("solana_bpf_rust_sanity", 873),
("solana_bpf_rust_sha", 32301),
("solana_bpf_rust_sanity", 900),
("solana_bpf_rust_secp256k1_recover", 301),
("solana_bpf_rust_sha", 32333),
]);
}

View File

@ -19,6 +19,7 @@ log = "0.4.14"
num-derive = "0.3"
num-traits = "0.2"
rand_core = "0.6.3"
libsecp256k1 = "0.5.0"
sha3 = "0.9.1"
solana-measure = { path = "../../measure", version = "=1.8.0" }
solana-runtime = { path = "../../runtime", version = "=1.8.0" }

View File

@ -20,7 +20,8 @@ use solana_sdk::{
epoch_schedule::EpochSchedule,
feature_set::{
blake3_syscall_enabled, cpi_data_cost, enforce_aligned_host_addrs,
keccak256_syscall_enabled, memory_ops_syscalls, sysvar_via_syscall, update_data_on_realloc,
keccak256_syscall_enabled, memory_ops_syscalls, secp256k1_recover_syscall_enabled,
sysvar_via_syscall, update_data_on_realloc,
},
hash::{Hasher, HASH_BYTES},
ic_msg,
@ -31,6 +32,9 @@ use solana_sdk::{
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
rent::Rent,
secp256k1_recover::{
Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
},
sysvar::{self, fees::Fees, Sysvar, SysvarId},
};
use std::{
@ -133,6 +137,11 @@ pub fn register_syscalls(
syscall_registry.register_syscall_by_name(b"sol_keccak256", SyscallKeccak256::call)?;
}
if invoke_context.is_feature_active(&secp256k1_recover_syscall_enabled::id()) {
syscall_registry
.register_syscall_by_name(b"sol_secp256k1_recover", SyscallSecp256k1Recover::call)?;
}
if invoke_context.is_feature_active(&blake3_syscall_enabled::id()) {
syscall_registry.register_syscall_by_name(b"sol_blake3", SyscallBlake3::call)?;
}
@ -330,6 +339,16 @@ pub fn bind_syscall_context_objects<'a>(
}),
);
bind_feature_gated_syscall_context_object!(
vm,
invoke_context.is_feature_active(&secp256k1_recover_syscall_enabled::id()),
Box::new(SyscallSecp256k1Recover {
cost: bpf_compute_budget.secp256k1_recover_cost,
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));
@ -1342,6 +1361,92 @@ impl<'a> SyscallObject<BpfError> for SyscallMemset<'a> {
}
}
/// secp256k1_recover
pub struct SyscallSecp256k1Recover<'a> {
cost: u64,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallSecp256k1Recover<'a> {
fn call(
&mut self,
hash_addr: u64,
recovery_id_val: u64,
signature_addr: u64,
result_addr: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
question_mark!(self.compute_meter.consume(self.cost), result);
let hash = question_mark!(
translate_slice::<u8>(
memory_mapping,
hash_addr,
keccak::HASH_BYTES as u64,
self.loader_id,
true,
),
result
);
let signature = question_mark!(
translate_slice::<u8>(
memory_mapping,
signature_addr,
SECP256K1_SIGNATURE_LENGTH as u64,
self.loader_id,
true,
),
result
);
let secp256k1_recover_result = question_mark!(
translate_slice_mut::<u8>(
memory_mapping,
result_addr,
SECP256K1_PUBLIC_KEY_LENGTH as u64,
self.loader_id,
true,
),
result
);
let message = match libsecp256k1::Message::parse_slice(hash) {
Ok(msg) => msg,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidHash.into());
return;
}
};
let recovery_id = match libsecp256k1::RecoveryId::parse(recovery_id_val as u8) {
Ok(id) => id,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
return;
}
};
let signature = match libsecp256k1::Signature::parse_standard_slice(signature) {
Ok(sig) => sig,
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidSignature.into());
return;
}
};
let public_key = match libsecp256k1::recover(&message, &signature, &recovery_id) {
Ok(key) => key.serialize(),
Err(_) => {
*result = Ok(Secp256k1RecoverError::InvalidSignature.into());
return;
}
};
secp256k1_recover_result.copy_from_slice(&public_key[1..65]);
*result = Ok(SUCCESS);
}
}
// Blake3
pub struct SyscallBlake3<'a> {
base_cost: u64,