Add ecrecover syscall (#17720)
Co-authored-by: Anton Lisanin <lisanin.anton@gmail.com>
This commit is contained in:
parent
92c5cdab62
commit
1f288ce527
|
@ -4344,6 +4344,7 @@ version = "1.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"libsecp256k1",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -5258,6 +5259,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"itertools 0.10.1",
|
"itertools 0.10.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libsecp256k1",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|
|
@ -2752,6 +2752,7 @@ version = "1.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder 1.4.3",
|
"byteorder 1.4.3",
|
||||||
|
"libsecp256k1",
|
||||||
"log",
|
"log",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -3018,6 +3019,13 @@ dependencies = [
|
||||||
"solana-program 1.8.0",
|
"solana-program 1.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solana-bpf-rust-secp256k1-recover"
|
||||||
|
version = "1.8.0"
|
||||||
|
dependencies = [
|
||||||
|
"solana-program 1.8.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "solana-bpf-rust-sha"
|
name = "solana-bpf-rust-sha"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -3370,6 +3378,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"itertools 0.10.1",
|
"itertools 0.10.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libsecp256k1",
|
||||||
"log",
|
"log",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|
|
@ -73,6 +73,7 @@ members = [
|
||||||
"rust/ro_modify",
|
"rust/ro_modify",
|
||||||
"rust/ro_account_modify",
|
"rust/ro_account_modify",
|
||||||
"rust/sanity",
|
"rust/sanity",
|
||||||
|
"rust/secp256k1_recover",
|
||||||
"rust/sha",
|
"rust/sha",
|
||||||
"rust/spoof1",
|
"rust/spoof1",
|
||||||
"rust/spoof1_system",
|
"rust/spoof1_system",
|
||||||
|
|
|
@ -86,6 +86,7 @@ fn main() {
|
||||||
"ro_modify",
|
"ro_modify",
|
||||||
"ro_account_modify",
|
"ro_account_modify",
|
||||||
"sanity",
|
"sanity",
|
||||||
|
"secp256k1_recover",
|
||||||
"sha",
|
"sha",
|
||||||
"spoof1",
|
"spoof1",
|
||||||
"spoof1_system",
|
"spoof1_system",
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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"]
|
|
@ -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!();
|
|
@ -432,6 +432,7 @@ fn test_program_bpf_sanity() {
|
||||||
("relative_call", true),
|
("relative_call", true),
|
||||||
("sanity", true),
|
("sanity", true),
|
||||||
("sanity++", true),
|
("sanity++", true),
|
||||||
|
("secp256k1_recover", true),
|
||||||
("sha", true),
|
("sha", true),
|
||||||
("struct_pass", true),
|
("struct_pass", true),
|
||||||
("struct_ret", true),
|
("struct_ret", true),
|
||||||
|
@ -453,6 +454,7 @@ fn test_program_bpf_sanity() {
|
||||||
("solana_bpf_rust_param_passing", true),
|
("solana_bpf_rust_param_passing", true),
|
||||||
("solana_bpf_rust_rand", true),
|
("solana_bpf_rust_rand", true),
|
||||||
("solana_bpf_rust_sanity", true),
|
("solana_bpf_rust_sanity", true),
|
||||||
|
("solana_bpf_rust_secp256k1_recover", true),
|
||||||
("solana_bpf_rust_sha", true),
|
("solana_bpf_rust_sha", true),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -1283,6 +1285,7 @@ fn assert_instruction_count() {
|
||||||
("relative_call", 10),
|
("relative_call", 10),
|
||||||
("sanity", 169),
|
("sanity", 169),
|
||||||
("sanity++", 168),
|
("sanity++", 168),
|
||||||
|
("secp256k1_recover", 357),
|
||||||
("sha", 1040),
|
("sha", 1040),
|
||||||
("struct_pass", 8),
|
("struct_pass", 8),
|
||||||
("struct_ret", 22),
|
("struct_ret", 22),
|
||||||
|
@ -1303,8 +1306,9 @@ fn assert_instruction_count() {
|
||||||
("solana_bpf_rust_noop", 478),
|
("solana_bpf_rust_noop", 478),
|
||||||
("solana_bpf_rust_param_passing", 46),
|
("solana_bpf_rust_param_passing", 46),
|
||||||
("solana_bpf_rust_rand", 481),
|
("solana_bpf_rust_rand", 481),
|
||||||
("solana_bpf_rust_sanity", 873),
|
("solana_bpf_rust_sanity", 900),
|
||||||
("solana_bpf_rust_sha", 32301),
|
("solana_bpf_rust_secp256k1_recover", 301),
|
||||||
|
("solana_bpf_rust_sha", 32333),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ log = "0.4.14"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
|
libsecp256k1 = "0.5.0"
|
||||||
sha3 = "0.9.1"
|
sha3 = "0.9.1"
|
||||||
solana-measure = { path = "../../measure", version = "=1.8.0" }
|
solana-measure = { path = "../../measure", version = "=1.8.0" }
|
||||||
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
|
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
|
||||||
|
|
|
@ -20,7 +20,8 @@ use solana_sdk::{
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
feature_set::{
|
feature_set::{
|
||||||
blake3_syscall_enabled, cpi_data_cost, enforce_aligned_host_addrs,
|
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},
|
hash::{Hasher, HASH_BYTES},
|
||||||
ic_msg,
|
ic_msg,
|
||||||
|
@ -31,6 +32,9 @@ use solana_sdk::{
|
||||||
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
|
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
|
||||||
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
|
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
|
secp256k1_recover::{
|
||||||
|
Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
|
||||||
|
},
|
||||||
sysvar::{self, fees::Fees, Sysvar, SysvarId},
|
sysvar::{self, fees::Fees, Sysvar, SysvarId},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -133,6 +137,11 @@ pub fn register_syscalls(
|
||||||
syscall_registry.register_syscall_by_name(b"sol_keccak256", SyscallKeccak256::call)?;
|
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()) {
|
if invoke_context.is_feature_active(&blake3_syscall_enabled::id()) {
|
||||||
syscall_registry.register_syscall_by_name(b"sol_blake3", SyscallBlake3::call)?;
|
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 is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id());
|
||||||
|
|
||||||
let invoke_context = Rc::new(RefCell::new(invoke_context));
|
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
|
// Blake3
|
||||||
pub struct SyscallBlake3<'a> {
|
pub struct SyscallBlake3<'a> {
|
||||||
base_cost: u64,
|
base_cost: u64,
|
||||||
|
|
|
@ -457,6 +457,37 @@ uint64_t sol_keccak256(
|
||||||
uint8_t *result
|
uint8_t *result
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Length of a secp256k1 recover input hash */
|
||||||
|
#define SECP256K1_RECOVER_HASH_LENGTH 32
|
||||||
|
/** Length of a secp256k1 input signature */
|
||||||
|
#define SECP256K1_RECOVER_SIGNATURE_LENGTH 64
|
||||||
|
/** Length of a secp256k1 recover result */
|
||||||
|
#define SECP256K1_RECOVER_RESULT_LENGTH 64
|
||||||
|
|
||||||
|
/** The hash provided to a sol_secp256k1_recover is invalid */
|
||||||
|
#define SECP256K1_RECOVER_ERROR_INVALID_HASH 1
|
||||||
|
/** The recovery_id provided to a sol_secp256k1_recover is invalid */
|
||||||
|
#define SECP256K1_RECOVER_ERROR_INVALID_RECOVERY_ID 2
|
||||||
|
/** The signature provided to a sol_secp256k1_recover is invalid */
|
||||||
|
#define SECP256K1_RECOVER_ERROR_INVALID_SIGNATURE 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recover public key from a signed message.
|
||||||
|
*
|
||||||
|
* @param hash Hashed message
|
||||||
|
* @param recovery_id Tag used for public key recovery from signatures. Can be 0 or 1
|
||||||
|
* @param signature An ECDSA signature
|
||||||
|
* @param result 64 byte array to hold the result. A recovered public key
|
||||||
|
* @return 0 if executed successfully
|
||||||
|
*/
|
||||||
|
uint64_t sol_secp256k1_recover(
|
||||||
|
const uint8_t *hash,
|
||||||
|
uint64_t recovery_id,
|
||||||
|
const uint8_t *signature,
|
||||||
|
uint8_t *result
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length of a Blake3 hash result
|
* Length of a Blake3 hash result
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,6 +36,7 @@ thiserror = "1.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
|
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
|
||||||
curve25519-dalek = "3.0.0"
|
curve25519-dalek = "3.0.0"
|
||||||
|
libsecp256k1 = "0.5.0"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
solana-logger = { path = "../../logger", version = "=1.8.0" }
|
solana-logger = { path = "../../logger", version = "=1.8.0" }
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub mod pubkey;
|
||||||
pub mod rent;
|
pub mod rent;
|
||||||
pub mod sanitize;
|
pub mod sanitize;
|
||||||
pub mod secp256k1_program;
|
pub mod secp256k1_program;
|
||||||
|
pub mod secp256k1_recover;
|
||||||
pub mod serialize_utils;
|
pub mod serialize_utils;
|
||||||
pub mod short_vec;
|
pub mod short_vec;
|
||||||
pub mod slot_hashes;
|
pub mod slot_hashes;
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
pub enum Secp256k1RecoverError {
|
||||||
|
#[error("The hash provided to a secp256k1_recover is invalid")]
|
||||||
|
InvalidHash,
|
||||||
|
#[error("The recovery_id provided to a secp256k1_recover is invalid")]
|
||||||
|
InvalidRecoveryId,
|
||||||
|
#[error("The signature provided to a secp256k1_recover is invalid")]
|
||||||
|
InvalidSignature,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for Secp256k1RecoverError {
|
||||||
|
fn from(v: u64) -> Secp256k1RecoverError {
|
||||||
|
match v {
|
||||||
|
1 => Secp256k1RecoverError::InvalidHash,
|
||||||
|
2 => Secp256k1RecoverError::InvalidRecoveryId,
|
||||||
|
3 => Secp256k1RecoverError::InvalidSignature,
|
||||||
|
_ => panic!("Unsupported Secp256k1RecoverError"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Secp256k1RecoverError> for u64 {
|
||||||
|
fn from(v: Secp256k1RecoverError) -> u64 {
|
||||||
|
match v {
|
||||||
|
Secp256k1RecoverError::InvalidHash => 1,
|
||||||
|
Secp256k1RecoverError::InvalidRecoveryId => 2,
|
||||||
|
Secp256k1RecoverError::InvalidSignature => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SECP256K1_SIGNATURE_LENGTH: usize = 64;
|
||||||
|
pub const SECP256K1_PUBLIC_KEY_LENGTH: usize = 64;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(
|
||||||
|
BorshSerialize,
|
||||||
|
BorshDeserialize,
|
||||||
|
BorshSchema,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Ord,
|
||||||
|
PartialOrd,
|
||||||
|
Hash,
|
||||||
|
AbiExample,
|
||||||
|
)]
|
||||||
|
pub struct Secp256k1Pubkey(pub [u8; SECP256K1_PUBLIC_KEY_LENGTH]);
|
||||||
|
|
||||||
|
impl Secp256k1Pubkey {
|
||||||
|
pub fn new(pubkey_vec: &[u8]) -> Self {
|
||||||
|
Self(
|
||||||
|
<[u8; SECP256K1_PUBLIC_KEY_LENGTH]>::try_from(<&[u8]>::clone(&pubkey_vec))
|
||||||
|
.expect("Slice must be the same length as a Pubkey"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(self) -> [u8; 64] {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn secp256k1_recover(
|
||||||
|
hash: &[u8],
|
||||||
|
recovery_id: u8,
|
||||||
|
signature: &[u8],
|
||||||
|
) -> Result<Secp256k1Pubkey, Secp256k1RecoverError> {
|
||||||
|
#[cfg(target_arch = "bpf")]
|
||||||
|
{
|
||||||
|
extern "C" {
|
||||||
|
fn sol_secp256k1_recover(
|
||||||
|
hash: *const u8,
|
||||||
|
recovery_id: u64,
|
||||||
|
signature: *const u8,
|
||||||
|
result: *mut u8,
|
||||||
|
) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pubkey_buffer = [0u8; SECP256K1_PUBLIC_KEY_LENGTH];
|
||||||
|
let result = unsafe {
|
||||||
|
sol_secp256k1_recover(
|
||||||
|
hash.as_ptr(),
|
||||||
|
recovery_id as u64,
|
||||||
|
signature.as_ptr(),
|
||||||
|
pubkey_buffer.as_mut_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
0 => Ok(Secp256k1Pubkey::new(&pubkey_buffer)),
|
||||||
|
error => Err(Secp256k1RecoverError::from(error)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
{
|
||||||
|
let message = libsecp256k1::Message::parse_slice(hash)
|
||||||
|
.map_err(|_| Secp256k1RecoverError::InvalidHash)?;
|
||||||
|
let recovery_id = libsecp256k1::RecoveryId::parse(recovery_id)
|
||||||
|
.map_err(|_| Secp256k1RecoverError::InvalidRecoveryId)?;
|
||||||
|
let signature = libsecp256k1::Signature::parse_standard_slice(signature)
|
||||||
|
.map_err(|_| Secp256k1RecoverError::InvalidSignature)?;
|
||||||
|
|
||||||
|
let secp256k1_key = libsecp256k1::recover(&message, &signature, &recovery_id)
|
||||||
|
.map_err(|_| Secp256k1RecoverError::InvalidSignature)?;
|
||||||
|
Ok(Secp256k1Pubkey::new(&secp256k1_key.serialize()[1..65]))
|
||||||
|
}
|
||||||
|
}
|
|
@ -127,6 +127,10 @@ pub mod memory_ops_syscalls {
|
||||||
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod secp256k1_recover_syscall_enabled {
|
||||||
|
solana_sdk::declare_id!("6RvdSWHh8oh72Dp7wMTS2DBkf3fRPtChfNrAo3cZZoXJ");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod add_missing_program_error_mappings {
|
pub mod add_missing_program_error_mappings {
|
||||||
solana_sdk::declare_id!("3QEUpjhgPEt92nz3Mqf6pABkHPGCQwSvKtyGMq4SuQyL");
|
solana_sdk::declare_id!("3QEUpjhgPEt92nz3Mqf6pABkHPGCQwSvKtyGMq4SuQyL");
|
||||||
}
|
}
|
||||||
|
@ -187,6 +191,7 @@ lazy_static! {
|
||||||
(keccak256_syscall_enabled::id(), "keccak256 syscall"),
|
(keccak256_syscall_enabled::id(), "keccak256 syscall"),
|
||||||
(stake_program_v4::id(), "solana_stake_program v4"),
|
(stake_program_v4::id(), "solana_stake_program v4"),
|
||||||
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
||||||
|
(secp256k1_recover_syscall_enabled::id(), "secp256k1_recover syscall"),
|
||||||
(add_missing_program_error_mappings::id(), "add missing program error mappings"),
|
(add_missing_program_error_mappings::id(), "add missing program error mappings"),
|
||||||
(system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"),
|
(system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"),
|
||||||
(blake3_syscall_enabled::id(), "blake3 syscall"),
|
(blake3_syscall_enabled::id(), "blake3 syscall"),
|
||||||
|
|
|
@ -179,6 +179,8 @@ pub struct BpfComputeBudget {
|
||||||
pub cpi_bytes_per_unit: u64,
|
pub cpi_bytes_per_unit: u64,
|
||||||
/// Base number of compute units consumed to get a sysvar
|
/// Base number of compute units consumed to get a sysvar
|
||||||
pub sysvar_base_cost: u64,
|
pub sysvar_base_cost: u64,
|
||||||
|
/// Number of compute units consumed to call secp256k1_recover
|
||||||
|
pub secp256k1_recover_cost: u64,
|
||||||
}
|
}
|
||||||
impl Default for BpfComputeBudget {
|
impl Default for BpfComputeBudget {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -202,6 +204,7 @@ impl BpfComputeBudget {
|
||||||
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
|
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
|
||||||
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
|
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
|
||||||
sysvar_base_cost: 100,
|
sysvar_base_cost: 100,
|
||||||
|
secp256k1_recover_cost: 25_000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue