Skip to content

Commit

Permalink
Implement keccak-secp256k1 instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
sakridge committed Aug 25, 2020
1 parent 53799c4 commit eb3ea51
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 9 deletions.
115 changes: 106 additions & 9 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ members = [
"net-shaper",
"notifier",
"poh-bench",
"programs/secp256k1",
"programs/bpf_loader",
"programs/budget",
"programs/config",
Expand Down
32 changes: 32 additions & 0 deletions programs/secp256k1/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "solana-secp256k1"
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]
bincode = "1.1.4"
solana-logger = { path = "../../logger", version = "1.4.0" }
solana-sdk = { path = "../../sdk", version = "1.4.0" }
libsecp256k1 = "0.3.5"
sha3 = "0.9.1"
digest = "0.9.0"
log = ""
rand = "0.7.0"

[dev-dependencies]
solana-runtime = { path = "../../runtime", version = "1.4.0" }

[lib]
crate-type = ["lib"]
name = "solana_secp256k1_program"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
2 changes: 2 additions & 0 deletions programs/secp256k1/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extern crate secp256k1;
mod secp256k1_instruction;
127 changes: 127 additions & 0 deletions programs/secp256k1/src/secp256k1_instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use solana_sdk::pubkey::Pubkey;
use solana_sdk::{account::KeyedAccount, instruction::InstructionError};

enum Secp256k1Error {
InvalidSignature,
InvalidPubkey,
}

const PUBKEY_SERIALIZED_SIZE: usize = 33;
const SIGNATURE_SERIALIZED_SIZE: usize = 64;

pub fn process_instruction(
_program_id: &Pubkey,
_keyed_accounts: &[KeyedAccount],
data: &[u8],
) -> Result<(), InstructionError> {
use digest::Digest;
use std::convert::TryInto;

if data.len() < (PUBKEY_SERIALIZED_SIZE + SIGNATURE_SERIALIZED_SIZE) {
return Err(InstructionError::InvalidInstructionData);
}
let pubkey_end = PUBKEY_SERIALIZED_SIZE;
let public_key = secp256k1::PublicKey::parse_slice(
&data[..pubkey_end],
Some(secp256k1::PublicKeyFormat::Compressed),
)
.map_err(|_| InstructionError::Custom(Secp256k1Error::InvalidPubkey as u32))?;
let sig = secp256k1::Signature::parse_slice(
&data[pubkey_end..pubkey_end + SIGNATURE_SERIALIZED_SIZE],
)
.map_err(|_| InstructionError::Custom(Secp256k1Error::InvalidSignature as u32))?;
let mut hasher = sha3::Keccak256::new();
hasher.update(&data[(pubkey_end + SIGNATURE_SERIALIZED_SIZE)..]);
let hash = hasher.finalize();
let hash_arr: [u8; 32] = hash.as_slice().try_into().expect("Wrong length");
let message = secp256k1::Message::parse(&hash_arr);
if secp256k1::verify(&message, &sig, &public_key) {
Ok(())
} else {
Err(InstructionError::Custom(
Secp256k1Error::InvalidSignature as u32,
))
}
}

solana_sdk::declare_program!(
"KeccakSecp256k11111111111111111111111111111",
solana_keccak_secp256k1_program,
process_instruction
);

#[cfg(test)]
pub mod test {
use crate::secp256k1_instruction::{PUBKEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE};
use digest::Digest;
use log::*;
use rand::thread_rng;
use solana_runtime::bank::Bank;
use solana_sdk::instruction::Instruction;
use solana_sdk::transaction::Transaction;
use solana_sdk::{genesis_config::create_genesis_config, signature::Signer};

#[test]
fn test_secp256k1() {
solana_logger::setup();

let (genesis_config, mint_keypair) = create_genesis_config(1_000_000_000);
let mut bank = Bank::new(&genesis_config);

let secp_id = crate::secp256k1_instruction::id();
let mut instruction_data = vec![];
let secp_privkey = secp256k1::SecretKey::random(&mut thread_rng());
let secp_pubkey = secp256k1::PublicKey::from_secret_key(&secp_privkey);
let pubkey_arr = secp_pubkey.serialize_compressed();
assert_eq!(pubkey_arr.len(), PUBKEY_SERIALIZED_SIZE);
let message_arr = b"hello";
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, &secp_privkey);
let signature_arr = signature.serialize();
assert_eq!(signature_arr.len(), SIGNATURE_SERIALIZED_SIZE);

instruction_data.resize(
pubkey_arr.len() + signature_arr.len() + message_arr.len(),
0,
);
instruction_data[..pubkey_arr.len()].copy_from_slice(&pubkey_arr);
instruction_data[pubkey_arr.len()..pubkey_arr.len() + signature_arr.len()]
.copy_from_slice(&signature_arr);
instruction_data[pubkey_arr.len() + signature_arr.len()..].copy_from_slice(message_arr);

let rec_pubkey = secp256k1::PublicKey::parse_slice(
&instruction_data[..33],
Some(secp256k1::PublicKeyFormat::Compressed),
)
.unwrap();
info!(
"recovered: {:?} from: {:?}",
rec_pubkey,
&instruction_data[..33]
);
let secp_instruction = Instruction {
program_id: secp_id,
accounts: vec![],
data: instruction_data,
};
let tx = Transaction::new_signed_with_payer(
&[secp_instruction],
Some(&mint_keypair.pubkey()),
&[&mint_keypair],
bank.last_blockhash(),
);
bank.add_builtin_program(
"secp256k1",
crate::secp256k1_instruction::id(),
crate::secp256k1_instruction::process_instruction,
);

info!("tx: {:?}", tx);
assert!(bank.process_transaction(&tx).is_ok());
}
}

0 comments on commit eb3ea51

Please sign in to comment.