Skip to content

Commit

Permalink
chore: integrate rust-secp256k1 (#1915)
Browse files Browse the repository at this point in the history
* chore: integrate rust-secp256k1

* parity version

* fmt

* dep

* restructure,cleanup and order of activation

* rm comments

---------

Co-authored-by: rakita <[email protected]>
  • Loading branch information
stevencartavia and rakita authored Dec 31, 2024
1 parent 60ce865 commit d2a3b5b
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 71 deletions.
105 changes: 96 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/precompile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ secp256k1 = { version = ">=0.28, <=0.29", default-features = false, features = [
"rand",
"global-context",
], optional = true }
libsecp256k1 = { version = "0.7", default-features = false, package = "libsecp256k1", features = [
"static-context",
], optional = true }

# SHA2-256 and RIPEMD-160
sha2 = { version = "0.10", default-features = false }
Expand Down Expand Up @@ -87,6 +90,7 @@ std = [
"sha2/std",
"c-kzg?/std",
"secp256k1?/std",
"libsecp256k1?/std",
]
hashbrown = ["primitives/hashbrown"]
asm-keccak = ["primitives/asm-keccak"]
Expand All @@ -107,6 +111,7 @@ portable = ["c-kzg"]
# The problem that `secp256k1` has is it fails to build for `wasm` target on Windows and Mac as it is c lib.
# In Linux it passes. If you don't require to build wasm on win/mac, it is safe to use it and it is enabled by default.
secp256k1 = ["dep:secp256k1"]
libsecp256k1 = ["dep:libsecp256k1"]

# Enables the BLS12-381 precompiles.
blst = ["dep:blst"]
Expand Down
79 changes: 17 additions & 62 deletions crates/precompile/src/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#[cfg(feature = "secp256k1")]
pub mod bitcoin_secp256k1;
pub mod k256;
#[cfg(feature = "libsecp256k1")]
pub mod parity_libsecp256k1;

use crate::{
utilities::right_pad, PrecompileError, PrecompileOutput, PrecompileResult,
PrecompileWithAddress,
Expand All @@ -7,65 +13,6 @@ use primitives::{alloy_primitives::B512, Bytes, B256};
pub const ECRECOVER: PrecompileWithAddress =
PrecompileWithAddress(crate::u64_to_address(1), ec_recover_run);

pub use self::secp256k1::ecrecover;

#[cfg(not(feature = "secp256k1"))]
#[allow(clippy::module_inception)]
mod secp256k1 {
use k256::ecdsa::{Error, RecoveryId, Signature, VerifyingKey};
use primitives::{alloy_primitives::B512, keccak256, B256};

pub fn ecrecover(sig: &B512, mut recid: u8, msg: &B256) -> Result<B256, Error> {
// Parse signature
let mut sig = Signature::from_slice(sig.as_slice())?;

// Normalize signature and flip recovery id if needed.
if let Some(sig_normalized) = sig.normalize_s() {
sig = sig_normalized;
recid ^= 1;
}
let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");

// Recover key
let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)?;
// Hash it
let mut hash = keccak256(
&recovered_key
.to_encoded_point(/* compress = */ false)
.as_bytes()[1..],
);

// Truncate to 20 bytes
hash[..12].fill(0);
Ok(hash)
}
}

#[cfg(feature = "secp256k1")]
#[allow(clippy::module_inception)]
mod secp256k1 {
use primitives::{alloy_primitives::B512, keccak256, B256};
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message, SECP256K1,
};

// Silence the unused crate dependency warning.
use k256 as _;

pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result<B256, secp256k1::Error> {
let recid = RecoveryId::from_i32(recid as i32).expect("recovery ID is valid");
let sig = RecoverableSignature::from_compact(sig.as_slice(), recid)?;

let msg = Message::from_digest(msg.0);
let public = SECP256K1.recover_ecdsa(&msg, &sig)?;

let mut hash = keccak256(&public.serialize_uncompressed()[1..]);
hash[..12].fill(0);
Ok(hash)
}
}

pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
const ECRECOVER_BASE: u64 = 3_000;

Expand All @@ -84,8 +31,16 @@ pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let recid = input[63] - 27;
let sig = <&B512>::try_from(&input[64..128]).unwrap();

let out = secp256k1::ecrecover(sig, recid, msg)
.map(|o| o.to_vec().into())
.unwrap_or_default();
cfg_if::cfg_if! {
if #[cfg(feature = "secp256k1")] {
let res = bitcoin_secp256k1::ecrecover(sig, recid, msg);
} else if #[cfg(feature = "libsecp256k1")] {
let res = parity_libsecp256k1::ecrecover(sig, recid, msg);
} else {
let res = k256::ecrecover(sig, recid, msg);
}
};

let out = res.map(|o| o.to_vec().into()).unwrap_or_default();
Ok(PrecompileOutput::new(ECRECOVER_BASE, out))
}
20 changes: 20 additions & 0 deletions crates/precompile/src/secp256k1/bitcoin_secp256k1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use primitives::{alloy_primitives::B512, keccak256, B256};
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message, SECP256K1,
};

// Silence the unused crate dependency warning.
use k256 as _;

pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result<B256, secp256k1::Error> {
let recid = RecoveryId::from_i32(recid as i32).expect("recovery ID is valid");
let sig = RecoverableSignature::from_compact(sig.as_slice(), recid)?;

let msg = Message::from_digest(msg.0);
let public = SECP256K1.recover_ecdsa(&msg, &sig)?;

let mut hash = keccak256(&public.serialize_uncompressed()[1..]);
hash[..12].fill(0);
Ok(hash)
}
27 changes: 27 additions & 0 deletions crates/precompile/src/secp256k1/k256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use k256::ecdsa::{Error, RecoveryId, Signature, VerifyingKey};
use primitives::{alloy_primitives::B512, keccak256, B256};

pub fn ecrecover(sig: &B512, mut recid: u8, msg: &B256) -> Result<B256, Error> {
// parse signature
let mut sig = Signature::from_slice(sig.as_slice())?;

// normalize signature and flip recovery id if needed.
if let Some(sig_normalized) = sig.normalize_s() {
sig = sig_normalized;
recid ^= 1;
}
let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");

// recover key
let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)?;
// hash it
let mut hash = keccak256(
&recovered_key
.to_encoded_point(/* compress = */ false)
.as_bytes()[1..],
);

// truncate to 20 bytes
hash[..12].fill(0);
Ok(hash)
}
Loading

0 comments on commit d2a3b5b

Please sign in to comment.