Verify a ed25519-dalek
signature via this library: https://github.com/dalek-cryptography/ed25519-dalek
I copied one of the test cases: https://github.com/dalek-cryptography/ed25519-dalek/blob/master/tests/ed25519.rs#L83-L114
use ed25519_dalek::PublicKey;
use ed25519_dalek::PUBLIC_KEY_LENGTH;
use ed25519_dalek::SignatureError;
use ed25519_dalek::SIGNATURE_LENGTH;
use ed25519_dalek::Signature;
use ed25519_dalek::ed25519::signature::Signature as _;
use hex::FromHex;
use sha2::Sha512;
use ed25519_dalek::Digest;
fn verify_dalek() -> bool {
// copied from https://github.com/dalek-cryptography/ed25519-dalek/blob/925eb9ea56192053c9eb93b9d30d1b9419eee128/tests/ed25519.rs#L85-L115
let public_key: &[u8] = b"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf";
let message: &[u8] = b"616263";
let signature: &[u8] = b"98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406";
let pub_bytes: Vec<u8> = FromHex::from_hex(public_key).unwrap();
let msg_bytes: Vec<u8> = FromHex::from_hex(message).unwrap();
let sig_bytes: Vec<u8> = FromHex::from_hex(signature).unwrap();
sol_log_compute_units();
let public: PublicKey = PublicKey::from_bytes(&pub_bytes[..PUBLIC_KEY_LENGTH]).unwrap();
sol_log_compute_units();
let sig1: Signature = Signature::from_bytes(&sig_bytes[..]).unwrap();
sol_log_compute_units();
let mut prehash_for_verifying: Sha512 = Sha512::default();
sol_log_compute_units();
prehash_for_verifying.update(&msg_bytes[..]);
sol_log_compute_units();
public.verify_prehashed(prehash_for_verifying, None ,&sig1).is_ok();
sol_log_compute_units();
true
}
hello world Cargo.toml
[dependencies]
byteorder = "1.3"
solana-program = "=1.4.8"
#curve25519-dalek = { version = "3", default-features = false, features = ["u32_backend"] }
getrandom = { version = "0.1.14", features = ["dummy"] }
hex = "^0.4"
sha2 = { version = "0.9", default-features = false }
[dependencies.ed25519-dalek]
version = "1"
default-features = false
features = ["u32_backend"]
Solana Config:
impl BpfComputeBudget {
pub fn new() -> Self {
BpfComputeBudget {
max_units: 100_000_000,
log_units: 100,
log_64_units: 100,
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
sha256_base_cost: 85,
sha256_byte_cost: 1,
max_call_depth: 64,
stack_frame_size: 4_096,
log_pubkey_units: 100,
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
}
}
}
Solana Output:
Log Messages:
Program HiqRhc6gYu1uYHGU8gybi8qz9XcHnpPEEY3KWp2mgUzc invoke [1]
Program log: Helloworld Rust program entrypoint
Program consumption: 99984768 units remaining
Program consumption: 99857627 units remaining
Program consumption: 99857447 units remaining
Program consumption: 99856211 units remaining
Program consumption: 99856088 units remaining
Program consumption: 97861110 units remaining
Program log: it is valid!
So 100,000,000 - 97,861,110 = 2,138,890
Compute limit as of 02/24/2021 = 200,000
So this program is roughly 11x more expensive than the default limit.
It looks like the suggested alternative is to use the secp256k1 program: https://docs.solana.com/developing/builtins/programs#secp256k1-program
I tried replacing the ed25519_dalek library with https://docs.rs/libsecp256k1/0.3.5/secp256k1/index.html but ran into a program that I believe was too big to upload.
Error: failed to send transaction: Transaction simulation failed: Blockhash not found
final cross program invocation:
use serde::{Deserialize, Serialize};
use digest::Digest;
use secp256k1::{Message, PublicKey, Signature};
pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
#[derive(Default, Serialize, Deserialize, Debug)]
pub struct SecpSignatureOffsets {
pub signature_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes
pub signature_instruction_index: u8,
pub eth_address_offset: u16, // offset to eth_address of 20 bytes
pub eth_address_instruction_index: u8,
pub message_data_offset: u16, // offset to start of message data
pub message_data_size: u16, // size of message data
pub message_instruction_index: u8,
}
pub fn construct_eth_pubkey(pubkey: &PublicKey) -> [u8; HASHED_PUBKEY_SERIALIZED_SIZE] {
let mut addr = [0u8; HASHED_PUBKEY_SERIALIZED_SIZE];
addr.copy_from_slice(&sha3::Keccak256::digest(&pubkey.serialize()[1..])[12..]);
assert_eq!(addr.len(), HASHED_PUBKEY_SERIALIZED_SIZE);
addr
}
fn new_secp256k1_instruction_pubkey(pub_key: PublicKey, message: Message, signature: Signature) -> Instruction {
let recovery_id = [0u8; 0];
let eth_pubkey = construct_eth_pubkey(&pub_key);
let signature_arr = signature.serialize();
let message_arr = message.serialize();
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: secp256k1_program::id(),
accounts: vec![],
data: instruction_data,
}
}
fn cross_program_secp256k1(accounts: &[AccountInfo]) {
sol_log_compute_units();
let message_arr = [6u8; 32];
let pubkey_arr = [4, 102, 10, 239, 214, 37, 120, 219, 79, 58, 84, 203, 113, 194, 62, 227, 112, 168, 142, 224, 224, 173, 245, 220, 191, 205, 109, 182, 7, 207, 132, 135, 48, 20, 135, 134, 183, 48, 79, 239, 220, 43, 207, 121, 173, 7, 0, 61, 139, 187, 222, 74, 197, 102, 206, 223, 134, 249, 168, 14, 115, 157, 89, 232, 117];
// let bad_pubkey_arr = [40, 102, 10, 239, 214, 37, 120, 219, 79, 58, 84, 203, 113, 194, 62, 227, 112, 168, 142, 224, 224, 173, 245, 220, 191, 205, 109, 182, 7, 207, 132, 135, 48, 20, 135, 134, 183, 48, 79, 239, 220, 43, 207, 121, 173, 7, 0, 61, 139, 187, 222, 74, 197, 102, 206, 223, 134, 249, 168, 14, 115, 157, 89, 232, 117];
// let pubkey_arr = bad_pubkey_arr;
let signature_arr = [205, 115, 59, 14, 209, 162, 164, 174, 65, 104, 82, 199, 255, 20, 174, 19, 123, 123, 11, 57, 187, 255, 12, 177, 153, 192, 188, 115, 242, 39, 11, 77, 72, 128, 241, 126, 66, 49, 136, 66, 54, 75, 195, 174, 56, 254, 101, 216, 10, 224, 127, 239, 240, 1, 5, 87, 43, 13, 98, 195, 174, 29, 184, 66];
sol_log_compute_units();
let message = Message::parse(&message_arr);
let pubkey = PublicKey::parse(&pubkey_arr).unwrap();
let signature = Signature::parse(&signature_arr);
sol_log_compute_units();
let instruction = new_secp256k1_instruction_pubkey(pubkey, message, signature);
sol_log_compute_units();
let result = invoke(&instruction, accounts);
match result {
Ok(_result) => {
info!("it worked")
}
Err(_err) => {
info!("err it did not work")
}
}
info!("done")
}