Add keccak-secp256k1 instruction (#11839)
* Implement keccak-secp256k1 instruction Verifies eth addreses with ecrecover function * Move secp256k1 test
This commit is contained in:
96
programs/bpf/Cargo.lock
generated
96
programs/bpf/Cargo.lock
generated
@ -128,12 +128,22 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"block-padding 0.1.5",
|
||||
"byte-tools",
|
||||
"byteorder 1.3.4",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"block-padding 0.2.1",
|
||||
"generic-array 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
@ -143,6 +153,12 @@ dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.3.1"
|
||||
@ -340,6 +356,12 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
@ -742,6 +764,17 @@ dependencies = [
|
||||
"digest 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac-drbg"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b"
|
||||
dependencies = [
|
||||
"digest 0.8.1",
|
||||
"generic-array 0.12.3",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.1"
|
||||
@ -912,6 +945,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
@ -946,6 +985,22 @@ dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsecp256k1"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"crunchy",
|
||||
"digest 0.8.1",
|
||||
"hmac-drbg",
|
||||
"rand",
|
||||
"sha2",
|
||||
"subtle 2.2.2",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.4"
|
||||
@ -1148,6 +1203,12 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.9.0"
|
||||
@ -1626,10 +1687,22 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"digest 0.9.0",
|
||||
"keccak",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1940,6 +2013,7 @@ dependencies = [
|
||||
"solana-rayon-threadlimit",
|
||||
"solana-sdk",
|
||||
"solana-sdk-macro-frozen-abi",
|
||||
"solana-secp256k1-program",
|
||||
"solana-stake-program",
|
||||
"solana-vote-program",
|
||||
"symlink",
|
||||
@ -1960,11 +2034,13 @@ dependencies = [
|
||||
"byteorder 1.3.4",
|
||||
"chrono",
|
||||
"curve25519-dalek",
|
||||
"digest 0.9.0",
|
||||
"ed25519-dalek",
|
||||
"generic-array 0.14.3",
|
||||
"hex",
|
||||
"hmac",
|
||||
"itertools",
|
||||
"libsecp256k1",
|
||||
"log",
|
||||
"memmap",
|
||||
"num-derive 0.3.0",
|
||||
@ -1979,6 +2055,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"solana-crate-features",
|
||||
"solana-logger",
|
||||
"solana-sdk-macro",
|
||||
@ -2008,6 +2085,19 @@ dependencies = [
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-secp256k1-program"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"digest 0.9.0",
|
||||
"libsecp256k1",
|
||||
"rand",
|
||||
"sha3",
|
||||
"solana-logger",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-stake-program"
|
||||
version = "1.4.0"
|
||||
|
@ -576,10 +576,10 @@ fn assert_instruction_count() {
|
||||
("solana_bpf_rust_128bit", 543),
|
||||
("solana_bpf_rust_alloc", 19082),
|
||||
("solana_bpf_rust_dep_crate", 2),
|
||||
("solana_bpf_rust_external_spend", 465),
|
||||
("solana_bpf_rust_external_spend", 473),
|
||||
("solana_bpf_rust_iter", 723),
|
||||
("solana_bpf_rust_many_args", 231),
|
||||
("solana_bpf_rust_noop", 2209),
|
||||
("solana_bpf_rust_noop", 2217),
|
||||
("solana_bpf_rust_param_passing", 54),
|
||||
]);
|
||||
}
|
||||
|
28
programs/secp256k1/Cargo.toml
Normal file
28
programs/secp256k1/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "solana-secp256k1-program"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.4.0"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../../sdk", version = "1.4.0" }
|
||||
libsecp256k1 = "0.3.5"
|
||||
sha3 = "0.9.1"
|
||||
digest = "0.9.0"
|
||||
bincode = "1.3.1"
|
||||
rand = "0.7.0"
|
||||
solana-logger = { path = "../../logger", version = "1.4.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
name = "solana_secp256k1_program"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
127
programs/secp256k1/src/lib.rs
Normal file
127
programs/secp256k1/src/lib.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::{
|
||||
account::KeyedAccount,
|
||||
instruction::{Instruction, InstructionError},
|
||||
};
|
||||
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_keyed_accounts: &[KeyedAccount],
|
||||
_data: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
// Should be already checked by now.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
solana_sdk::declare_program!(
|
||||
solana_sdk::secp256k1_program::ID,
|
||||
solana_keccak_secp256k1_program,
|
||||
process_instruction
|
||||
);
|
||||
|
||||
pub fn new_secp256k1_instruction(
|
||||
priv_key: &secp256k1::SecretKey,
|
||||
message_arr: &[u8],
|
||||
) -> Instruction {
|
||||
use digest::Digest;
|
||||
use solana_sdk::secp256k1::{
|
||||
construct_eth_pubkey, SecpSignatureOffsets, SIGNATURE_OFFSETS_SERIALIZED_SIZE,
|
||||
SIGNATURE_SERIALIZED_SIZE,
|
||||
};
|
||||
|
||||
let secp_pubkey = secp256k1::PublicKey::from_secret_key(priv_key);
|
||||
let eth_pubkey = construct_eth_pubkey(&secp_pubkey);
|
||||
let mut hasher = sha3::Keccak256::new();
|
||||
hasher.update(&message_arr);
|
||||
let message_hash = hasher.finalize();
|
||||
let mut message_hash_arr = [0u8; 32];
|
||||
message_hash_arr.copy_from_slice(&message_hash.as_slice());
|
||||
let message = secp256k1::Message::parse(&message_hash_arr);
|
||||
let (signature, recovery_id) = secp256k1::sign(&message, priv_key);
|
||||
let signature_arr = signature.serialize();
|
||||
assert_eq!(signature_arr.len(), SIGNATURE_SERIALIZED_SIZE);
|
||||
|
||||
let mut instruction_data = vec![];
|
||||
let data_start = 1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE;
|
||||
instruction_data.resize(
|
||||
data_start + eth_pubkey.len() + signature_arr.len() + message_arr.len() + 1,
|
||||
0,
|
||||
);
|
||||
let eth_address_offset = data_start;
|
||||
instruction_data[eth_address_offset..eth_address_offset + eth_pubkey.len()]
|
||||
.copy_from_slice(ð_pubkey);
|
||||
|
||||
let signature_offset = data_start + eth_pubkey.len();
|
||||
instruction_data[signature_offset..signature_offset + signature_arr.len()]
|
||||
.copy_from_slice(&signature_arr);
|
||||
|
||||
instruction_data[signature_offset + signature_arr.len()] = recovery_id.serialize();
|
||||
|
||||
let message_data_offset = signature_offset + signature_arr.len() + 1;
|
||||
instruction_data[message_data_offset..].copy_from_slice(message_arr);
|
||||
|
||||
let num_signatures = 1;
|
||||
instruction_data[0] = num_signatures;
|
||||
let offsets = SecpSignatureOffsets {
|
||||
signature_offset: signature_offset as u16,
|
||||
signature_instruction_index: 0,
|
||||
eth_address_offset: eth_address_offset as u16,
|
||||
eth_address_instruction_index: 0,
|
||||
message_data_offset: message_data_offset as u16,
|
||||
message_data_size: message_arr.len() as u16,
|
||||
message_instruction_index: 0,
|
||||
};
|
||||
let writer = std::io::Cursor::new(&mut instruction_data[1..data_start]);
|
||||
bincode::serialize_into(writer, &offsets).unwrap();
|
||||
|
||||
Instruction {
|
||||
program_id: solana_sdk::secp256k1_program::id(),
|
||||
accounts: vec![],
|
||||
data: instruction_data,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_sdk::secp256k1::{SecpSignatureOffsets, SIGNATURE_OFFSETS_SERIALIZED_SIZE};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
signature::{Keypair, Signer},
|
||||
transaction::Transaction,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_secp256k1() {
|
||||
solana_logger::setup();
|
||||
let offsets = SecpSignatureOffsets::default();
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&offsets).unwrap() as usize,
|
||||
SIGNATURE_OFFSETS_SERIALIZED_SIZE
|
||||
);
|
||||
|
||||
let secp_privkey = secp256k1::SecretKey::random(&mut thread_rng());
|
||||
let message_arr = b"hello";
|
||||
let mut secp_instruction = super::new_secp256k1_instruction(&secp_privkey, message_arr);
|
||||
let mint_keypair = Keypair::new();
|
||||
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[secp_instruction.clone()],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
Hash::default(),
|
||||
);
|
||||
|
||||
assert!(tx.verify_precompiles().is_ok());
|
||||
|
||||
let index = thread_rng().gen_range(0, secp_instruction.data.len());
|
||||
secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12);
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[secp_instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
Hash::default(),
|
||||
);
|
||||
assert!(tx.verify_precompiles().is_err());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user