From 1f288ce527d3ef755b13a6ddd322dcee49a1f256 Mon Sep 17 00:00:00 2001 From: s-medvedev <40623263+s-medvedev@users.noreply.github.com> Date: Wed, 7 Jul 2021 23:15:14 +0300 Subject: [PATCH] Add ecrecover syscall (#17720) Co-authored-by: Anton Lisanin --- Cargo.lock | 2 + programs/bpf/Cargo.lock | 9 ++ programs/bpf/Cargo.toml | 1 + programs/bpf/build.rs | 1 + .../src/secp256k1_recover/secp256k1_recover.c | 38 ++++++ .../bpf/rust/secp256k1_recover/Cargo.toml | 19 +++ .../bpf/rust/secp256k1_recover/src/lib.rs | 44 +++++++ programs/bpf/tests/programs.rs | 8 +- programs/bpf_loader/Cargo.toml | 1 + programs/bpf_loader/src/syscalls.rs | 107 ++++++++++++++++- sdk/bpf/c/inc/solana_sdk.h | 31 +++++ sdk/program/Cargo.toml | 1 + sdk/program/src/lib.rs | 1 + sdk/program/src/secp256k1_recover.rs | 113 ++++++++++++++++++ sdk/src/feature_set.rs | 5 + sdk/src/process_instruction.rs | 3 + 16 files changed, 381 insertions(+), 3 deletions(-) create mode 100644 programs/bpf/c/src/secp256k1_recover/secp256k1_recover.c create mode 100644 programs/bpf/rust/secp256k1_recover/Cargo.toml create mode 100644 programs/bpf/rust/secp256k1_recover/src/lib.rs create mode 100644 sdk/program/src/secp256k1_recover.rs diff --git a/Cargo.lock b/Cargo.lock index 2e2e41dda3..5d99e1430e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4344,6 +4344,7 @@ version = "1.8.0" dependencies = [ "bincode", "byteorder", + "libsecp256k1", "log 0.4.14", "num-derive", "num-traits", @@ -5258,6 +5259,7 @@ dependencies = [ "hex", "itertools 0.10.1", "lazy_static", + "libsecp256k1", "log 0.4.14", "num-derive", "num-traits", diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index bccdb89378..48a65f0ead 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -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", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index a3e6285e46..74881704c7 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -73,6 +73,7 @@ members = [ "rust/ro_modify", "rust/ro_account_modify", "rust/sanity", + "rust/secp256k1_recover", "rust/sha", "rust/spoof1", "rust/spoof1_system", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 8d16b204a7..63f77b4b2b 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -86,6 +86,7 @@ fn main() { "ro_modify", "ro_account_modify", "sanity", + "secp256k1_recover", "sha", "spoof1", "spoof1_system", diff --git a/programs/bpf/c/src/secp256k1_recover/secp256k1_recover.c b/programs/bpf/c/src/secp256k1_recover/secp256k1_recover.c new file mode 100644 index 0000000000..f4cd076f7a --- /dev/null +++ b/programs/bpf/c/src/secp256k1_recover/secp256k1_recover.c @@ -0,0 +1,38 @@ +/** + * @brief Secp256k1Recover Syscall test + */ +#include + +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; +} diff --git a/programs/bpf/rust/secp256k1_recover/Cargo.toml b/programs/bpf/rust/secp256k1_recover/Cargo.toml new file mode 100644 index 0000000000..dd21f9def0 --- /dev/null +++ b/programs/bpf/rust/secp256k1_recover/Cargo.toml @@ -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 "] +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"] diff --git a/programs/bpf/rust/secp256k1_recover/src/lib.rs b/programs/bpf/rust/secp256k1_recover/src/lib.rs new file mode 100644 index 0000000000..f452bbbf75 --- /dev/null +++ b/programs/bpf/rust/secp256k1_recover/src/lib.rs @@ -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!(); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 96f2be9f95..b00c80f16d 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -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), ]); } diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 69d0b0ce08..a4cff625c9 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -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" } diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 2b56ae30eb..3af6c2f806 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -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 for SyscallMemset<'a> { } } +/// secp256k1_recover +pub struct SyscallSecp256k1Recover<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} + +impl<'a> SyscallObject 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>, + ) { + question_mark!(self.compute_meter.consume(self.cost), result); + + let hash = question_mark!( + translate_slice::( + memory_mapping, + hash_addr, + keccak::HASH_BYTES as u64, + self.loader_id, + true, + ), + result + ); + let signature = question_mark!( + translate_slice::( + memory_mapping, + signature_addr, + SECP256K1_SIGNATURE_LENGTH as u64, + self.loader_id, + true, + ), + result + ); + let secp256k1_recover_result = question_mark!( + translate_slice_mut::( + 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, diff --git a/sdk/bpf/c/inc/solana_sdk.h b/sdk/bpf/c/inc/solana_sdk.h index feaf569604..93d3ddfb6a 100644 --- a/sdk/bpf/c/inc/solana_sdk.h +++ b/sdk/bpf/c/inc/solana_sdk.h @@ -457,6 +457,37 @@ uint64_t sol_keccak256( 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 */ diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 0ae2f1b85b..76477a1d72 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -36,6 +36,7 @@ thiserror = "1.0" [target.'cfg(not(target_arch = "bpf"))'.dependencies] curve25519-dalek = "3.0.0" +libsecp256k1 = "0.5.0" rand = "0.7.0" solana-logger = { path = "../../logger", version = "=1.8.0" } diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 190a5d1585..498a29ed81 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -39,6 +39,7 @@ pub mod pubkey; pub mod rent; pub mod sanitize; pub mod secp256k1_program; +pub mod secp256k1_recover; pub mod serialize_utils; pub mod short_vec; pub mod slot_hashes; diff --git a/sdk/program/src/secp256k1_recover.rs b/sdk/program/src/secp256k1_recover.rs new file mode 100644 index 0000000000..4a6433d5da --- /dev/null +++ b/sdk/program/src/secp256k1_recover.rs @@ -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 for Secp256k1RecoverError { + fn from(v: u64) -> Secp256k1RecoverError { + match v { + 1 => Secp256k1RecoverError::InvalidHash, + 2 => Secp256k1RecoverError::InvalidRecoveryId, + 3 => Secp256k1RecoverError::InvalidSignature, + _ => panic!("Unsupported Secp256k1RecoverError"), + } + } +} + +impl From 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 { + #[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])) + } +} diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index de94de0a48..30c95c7392 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -127,6 +127,10 @@ pub mod memory_ops_syscalls { solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU"); } +pub mod secp256k1_recover_syscall_enabled { + solana_sdk::declare_id!("6RvdSWHh8oh72Dp7wMTS2DBkf3fRPtChfNrAo3cZZoXJ"); +} + pub mod add_missing_program_error_mappings { solana_sdk::declare_id!("3QEUpjhgPEt92nz3Mqf6pABkHPGCQwSvKtyGMq4SuQyL"); } @@ -187,6 +191,7 @@ lazy_static! { (keccak256_syscall_enabled::id(), "keccak256 syscall"), (stake_program_v4::id(), "solana_stake_program v4"), (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"), (system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"), (blake3_syscall_enabled::id(), "blake3 syscall"), diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 9258495d03..ff0781c654 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -179,6 +179,8 @@ pub struct BpfComputeBudget { pub cpi_bytes_per_unit: u64, /// Base number of compute units consumed to get a sysvar pub sysvar_base_cost: u64, + /// Number of compute units consumed to call secp256k1_recover + pub secp256k1_recover_cost: u64, } impl Default for BpfComputeBudget { fn default() -> Self { @@ -202,6 +204,7 @@ impl BpfComputeBudget { max_cpi_instruction_size: 1280, // IPv6 Min MTU size cpi_bytes_per_unit: 250, // ~50MB at 200,000 units sysvar_base_cost: 100, + secp256k1_recover_cost: 25_000, } } }