feat: add ed25519 signature verify program
Solang requires a method for verify ed25519 signatures. Add a new
builtin program at address Ed25519SigVerify111111111111111111111111111
which takes any number of ed25519 signature, public key, and message.
If any of the signatures fails to verify, an error is returned.
The changes for the web3.js package will go into another commit, since
the tests test against a released solana node. Adding web3.js ed25519
testing will break CI.
(cherry picked from commit b491354e51
)
Conflicts:
Cargo.lock
Cargo.toml
programs/bpf/Cargo.lock
runtime/Cargo.toml
sdk/src/feature_set.rs
sdk/src/transaction.rs
sdk/src/transaction/sanitized.rs
This commit is contained in:
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -4689,6 +4689,17 @@ dependencies = [
|
||||
"tar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-ed25519-program"
|
||||
version = "1.8.1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"ed25519-dalek",
|
||||
"rand 0.7.3",
|
||||
"solana-logger 1.8.1",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-exchange-program"
|
||||
version = "1.8.1"
|
||||
@ -5424,6 +5435,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"solana-compute-budget-program",
|
||||
"solana-config-program",
|
||||
"solana-ed25519-program",
|
||||
"solana-frozen-abi 1.8.1",
|
||||
"solana-frozen-abi-macro 1.8.1",
|
||||
"solana-logger 1.8.1",
|
||||
@ -5460,6 +5472,7 @@ dependencies = [
|
||||
"borsh-derive",
|
||||
"bs58 0.4.0",
|
||||
"bv",
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"curve25519-dalek 2.1.0",
|
||||
|
@ -49,6 +49,7 @@ members = [
|
||||
"programs/compute-budget",
|
||||
"programs/config",
|
||||
"programs/exchange",
|
||||
"programs/ed25519",
|
||||
"programs/secp256k1",
|
||||
"programs/stake",
|
||||
"programs/vote",
|
||||
|
@ -65,6 +65,48 @@ to the BPF Upgradeable Loader to process the instruction.
|
||||
|
||||
[More information about deployment](cli/deploy-a-program.md)
|
||||
|
||||
## Ed25519 Program
|
||||
|
||||
Verify ed25519 signature program. This program takes an ed25519 signature, public key, and message.
|
||||
Multiple signatures can be verified. If any of the signatures fail to verify, an error is returned.
|
||||
|
||||
- Program id: `Ed25519SigVerify111111111111111111111111111`
|
||||
- Instructions: [new_ed25519_instruction](https://github.com/solana-labs/solana/blob/master/sdk/src/ed25519_instruction.rs#L31)
|
||||
|
||||
The ed25519 program processes an instruction. The first `u8` is a count of the number of
|
||||
signatures to check, which is followed by a single byte padding. After that, the
|
||||
following struct is serialized, one for each signature to check.
|
||||
|
||||
```
|
||||
struct Ed25519SignatureOffsets {
|
||||
signature_offset: u16, // offset to ed25519 signature of 64 bytes
|
||||
signature_instruction_index: u16, // instruction index to find signature
|
||||
public_key_offset: u16, // offset to public key of 32 bytes
|
||||
public_key_instruction_index: u16, // instruction index to find public key
|
||||
message_data_offset: u16, // offset to start of message data
|
||||
message_data_size: u16, // size of message data
|
||||
message_instruction_index: u16, // index of instruction data to get message data
|
||||
}
|
||||
```
|
||||
|
||||
Pseudo code of the operation:
|
||||
|
||||
```
|
||||
process_instruction() {
|
||||
for i in 0..count {
|
||||
// i'th index values referenced:
|
||||
instructions = &transaction.message().instructions
|
||||
signature = instructions[ed25519_signature_instruction_index].data[ed25519_signature_offset..ed25519_signature_offset + 64]
|
||||
pubkey = instructions[ed25519_pubkey_instruction_index].data[ed25519_pubkey_offset..ed25519_pubkey_offset + 32]
|
||||
message = instructions[ed25519_message_instruction_index].data[ed25519_message_data_offset..ed25519_message_data_offset + ed25519_message_data_size]
|
||||
if pubkey.verify(signature, message) != Success {
|
||||
return Error
|
||||
}
|
||||
}
|
||||
return Success
|
||||
}
|
||||
```
|
||||
|
||||
## Secp256k1 Program
|
||||
|
||||
Verify secp256k1 public key recovery operations (ecrecover).
|
||||
|
9
programs/bpf/Cargo.lock
generated
9
programs/bpf/Cargo.lock
generated
@ -3142,6 +3142,13 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-ed25519-program"
|
||||
version = "1.8.1"
|
||||
dependencies = [
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-faucet"
|
||||
version = "1.8.1"
|
||||
@ -3439,6 +3446,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"solana-compute-budget-program",
|
||||
"solana-config-program",
|
||||
"solana-ed25519-program",
|
||||
"solana-frozen-abi 1.8.1",
|
||||
"solana-frozen-abi-macro 1.8.1",
|
||||
"solana-logger 1.8.1",
|
||||
@ -3467,6 +3475,7 @@ dependencies = [
|
||||
"borsh-derive",
|
||||
"bs58 0.4.0",
|
||||
"bv",
|
||||
"bytemuck",
|
||||
"byteorder 1.3.4",
|
||||
"chrono",
|
||||
"derivation-path",
|
||||
|
26
programs/ed25519/Cargo.toml
Normal file
26
programs/ed25519/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "solana-ed25519-program"
|
||||
description = "Solana Ed25519 program"
|
||||
version = "1.8.1"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-ed25519-program"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../../sdk", version = "=1.8.1" }
|
||||
|
||||
[dev-dependencies]
|
||||
bytemuck = { version = "1.7.2", features = ["derive"] }
|
||||
ed25519-dalek = "=1.0.1"
|
||||
rand = "0.7.0"
|
||||
solana-logger = { path = "../../logger", version = "=1.8.1" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
name = "solana_ed25519_program"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
55
programs/ed25519/src/lib.rs
Normal file
55
programs/ed25519/src/lib.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use solana_sdk::{
|
||||
instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_data: &[u8],
|
||||
_invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
// Should be already checked by now.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_sdk::{
|
||||
ed25519_instruction::new_ed25519_instruction,
|
||||
feature_set::FeatureSet,
|
||||
hash::Hash,
|
||||
signature::{Keypair, Signer},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn test_ed25519() {
|
||||
solana_logger::setup();
|
||||
|
||||
let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
|
||||
let message_arr = b"hello";
|
||||
let mut instruction = new_ed25519_instruction(&privkey, message_arr);
|
||||
let mint_keypair = Keypair::new();
|
||||
let feature_set = Arc::new(FeatureSet::all_enabled());
|
||||
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[instruction.clone()],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
Hash::default(),
|
||||
);
|
||||
|
||||
assert!(tx.verify_precompiles(&feature_set).is_ok());
|
||||
|
||||
let index = thread_rng().gen_range(0, instruction.data.len());
|
||||
instruction.data[index] = instruction.data[index].wrapping_add(12);
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
Hash::default(),
|
||||
);
|
||||
assert!(tx.verify_precompiles(&feature_set).is_err());
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ serde = { version = "1.0.122", features = ["rc"] }
|
||||
serde_derive = "1.0.103"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.8.1" }
|
||||
solana-compute-budget-program = { path = "../programs/compute-budget", version = "=1.8.1" }
|
||||
solana-ed25519-program = { path = "../programs/ed25519", version = "=1.8.1" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.8.1" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.8.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.8.1" }
|
||||
|
@ -87,15 +87,26 @@ pub enum ActivationType {
|
||||
/// normal child Bank creation.
|
||||
/// https://github.com/solana-labs/solana/blob/84b139cc94b5be7c9e0c18c2ad91743231b85a0d/runtime/src/bank.rs#L1723
|
||||
fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> {
|
||||
vec![(
|
||||
Builtin::new(
|
||||
"compute_budget_program",
|
||||
solana_sdk::compute_budget::id(),
|
||||
solana_compute_budget_program::process_instruction,
|
||||
vec![
|
||||
(
|
||||
Builtin::new(
|
||||
"compute_budget_program",
|
||||
solana_sdk::compute_budget::id(),
|
||||
solana_compute_budget_program::process_instruction,
|
||||
),
|
||||
feature_set::tx_wide_compute_cap::id(),
|
||||
ActivationType::NewProgram,
|
||||
),
|
||||
feature_set::tx_wide_compute_cap::id(),
|
||||
ActivationType::NewProgram,
|
||||
)]
|
||||
(
|
||||
Builtin::new(
|
||||
"ed25519_program",
|
||||
solana_sdk::ed25519_program::id(),
|
||||
solana_ed25519_program::process_instruction,
|
||||
),
|
||||
feature_set::ed25519_program_enabled::id(),
|
||||
ActivationType::NewProgram,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn get() -> Builtins {
|
||||
|
@ -40,6 +40,7 @@ full = [
|
||||
[dependencies]
|
||||
assert_matches = { version = "1.5.0", optional = true }
|
||||
bincode = "1.3.3"
|
||||
bytemuck = { version = "1.7.2", features = ["derive"] }
|
||||
borsh = "0.9.0"
|
||||
base64 = "0.13"
|
||||
borsh-derive = "0.9.0"
|
||||
|
1
sdk/program/src/ed25519_program.rs
Normal file
1
sdk/program/src/ed25519_program.rs
Normal file
@ -0,0 +1 @@
|
||||
crate::declare_id!("Ed25519SigVerify111111111111111111111111111");
|
@ -1,5 +1,6 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use crate::clock::DEFAULT_MS_PER_SLOT;
|
||||
use crate::ed25519_program;
|
||||
use crate::message::Message;
|
||||
use crate::secp256k1_program;
|
||||
use log::*;
|
||||
@ -28,20 +29,22 @@ impl FeeCalculator {
|
||||
}
|
||||
|
||||
pub fn calculate_fee(&self, message: &Message) -> u64 {
|
||||
let mut num_secp256k1_signatures: u64 = 0;
|
||||
let mut num_signatures: u64 = 0;
|
||||
for instruction in &message.instructions {
|
||||
let program_index = instruction.program_id_index as usize;
|
||||
// Transaction may not be sanitized here
|
||||
if program_index < message.account_keys.len() {
|
||||
let id = message.account_keys[program_index];
|
||||
if secp256k1_program::check_id(&id) && !instruction.data.is_empty() {
|
||||
num_secp256k1_signatures += instruction.data[0] as u64;
|
||||
if (secp256k1_program::check_id(&id) || ed25519_program::check_id(&id))
|
||||
&& !instruction.data.is_empty()
|
||||
{
|
||||
num_signatures += instruction.data[0] as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.lamports_per_signature
|
||||
* (u64::from(message.header.num_required_signatures) + num_secp256k1_signatures)
|
||||
* (u64::from(message.header.num_required_signatures) + num_signatures)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ pub mod bpf_loader_deprecated;
|
||||
pub mod bpf_loader_upgradeable;
|
||||
pub mod clock;
|
||||
pub mod decode_error;
|
||||
pub mod ed25519_program;
|
||||
pub mod entrypoint;
|
||||
pub mod entrypoint_deprecated;
|
||||
pub mod epoch_schedule;
|
||||
|
328
sdk/src/ed25519_instruction.rs
Normal file
328
sdk/src/ed25519_instruction.rs
Normal file
@ -0,0 +1,328 @@
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
use crate::{decode_error::DecodeError, instruction::Instruction};
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use ed25519_dalek::{ed25519::signature::Signature, Signer, Verifier};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Clone, PartialEq)]
|
||||
pub enum Ed25519Error {
|
||||
#[error("ed25519 public key is not valid")]
|
||||
InvalidPublicKey,
|
||||
#[error("ed25519 signature is not valid")]
|
||||
InvalidSignature,
|
||||
#[error("offset not valid")]
|
||||
InvalidDataOffsets,
|
||||
#[error("instruction is incorrect size")]
|
||||
InvalidInstructionDataSize,
|
||||
}
|
||||
|
||||
impl<T> DecodeError<T> for Ed25519Error {
|
||||
fn type_of() -> &'static str {
|
||||
"Ed25519Error"
|
||||
}
|
||||
}
|
||||
|
||||
pub const PUBKEY_SERIALIZED_SIZE: usize = 32;
|
||||
pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
|
||||
pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
|
||||
// bytemuck requires structures to be aligned
|
||||
pub const SIGNATURE_OFFSETS_START: usize = 2;
|
||||
pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Ed25519SignatureOffsets {
|
||||
signature_offset: u16, // offset to ed25519 signature of 64 bytes
|
||||
signature_instruction_index: u16, // instruction index to find signature
|
||||
public_key_offset: u16, // offset to public key of 32 bytes
|
||||
public_key_instruction_index: u16, // instruction index to find public key
|
||||
message_data_offset: u16, // offset to start of message data
|
||||
message_data_size: u16, // size of message data
|
||||
message_instruction_index: u16, // index of instruction data to get message data
|
||||
}
|
||||
|
||||
pub fn new_ed25519_instruction(keypair: &ed25519_dalek::Keypair, message: &[u8]) -> Instruction {
|
||||
let signature = keypair.sign(message).to_bytes();
|
||||
let pubkey = keypair.public.to_bytes();
|
||||
|
||||
assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE);
|
||||
assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE);
|
||||
|
||||
let mut instruction_data = Vec::with_capacity(
|
||||
DATA_START
|
||||
.saturating_add(SIGNATURE_SERIALIZED_SIZE)
|
||||
.saturating_add(PUBKEY_SERIALIZED_SIZE)
|
||||
.saturating_add(message.len()),
|
||||
);
|
||||
|
||||
let num_signatures: u8 = 1;
|
||||
let public_key_offset = DATA_START;
|
||||
let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE);
|
||||
let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
|
||||
|
||||
// add padding byte so that offset structure is aligned
|
||||
instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
signature_offset: signature_offset as u16,
|
||||
signature_instruction_index: 0,
|
||||
public_key_offset: public_key_offset as u16,
|
||||
public_key_instruction_index: 0,
|
||||
message_data_offset: message_data_offset as u16,
|
||||
message_data_size: message.len() as u16,
|
||||
message_instruction_index: 0,
|
||||
};
|
||||
|
||||
instruction_data.extend_from_slice(bytes_of(&offsets));
|
||||
|
||||
debug_assert_eq!(instruction_data.len(), public_key_offset);
|
||||
|
||||
instruction_data.extend_from_slice(&pubkey);
|
||||
|
||||
debug_assert_eq!(instruction_data.len(), signature_offset);
|
||||
|
||||
instruction_data.extend_from_slice(&signature);
|
||||
|
||||
debug_assert_eq!(instruction_data.len(), message_data_offset);
|
||||
|
||||
instruction_data.extend_from_slice(message);
|
||||
|
||||
Instruction {
|
||||
program_id: solana_sdk::ed25519_program::id(),
|
||||
accounts: vec![],
|
||||
data: instruction_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_signatures(data: &[u8], instruction_datas: &[&[u8]]) -> Result<(), Ed25519Error> {
|
||||
if data.len() < SIGNATURE_OFFSETS_START {
|
||||
return Err(Ed25519Error::InvalidInstructionDataSize);
|
||||
}
|
||||
let num_signatures = data[0] as usize;
|
||||
if num_signatures == 0 && data.len() > SIGNATURE_OFFSETS_START {
|
||||
return Err(Ed25519Error::InvalidInstructionDataSize);
|
||||
}
|
||||
let expected_data_size = num_signatures
|
||||
.saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
|
||||
.saturating_add(SIGNATURE_OFFSETS_START);
|
||||
if data.len() < expected_data_size {
|
||||
return Err(Ed25519Error::InvalidInstructionDataSize);
|
||||
}
|
||||
for i in 0..num_signatures {
|
||||
let start = i
|
||||
.saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
|
||||
.saturating_add(SIGNATURE_OFFSETS_START);
|
||||
let end = start.saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE);
|
||||
|
||||
// bytemuck wants structures aligned
|
||||
let offsets: &Ed25519SignatureOffsets = bytemuck::try_from_bytes(&data[start..end])
|
||||
.map_err(|_| Ed25519Error::InvalidDataOffsets)?;
|
||||
|
||||
// Parse out signature
|
||||
let signature_index = offsets.signature_instruction_index as usize;
|
||||
if signature_index >= instruction_datas.len() {
|
||||
return Err(Ed25519Error::InvalidDataOffsets);
|
||||
}
|
||||
let signature_instruction = instruction_datas[signature_index];
|
||||
let sig_start = offsets.signature_offset as usize;
|
||||
let sig_end = sig_start.saturating_add(SIGNATURE_SERIALIZED_SIZE);
|
||||
if sig_end >= signature_instruction.len() {
|
||||
return Err(Ed25519Error::InvalidDataOffsets);
|
||||
}
|
||||
|
||||
let signature =
|
||||
ed25519_dalek::Signature::from_bytes(&signature_instruction[sig_start..sig_end])
|
||||
.map_err(|_| Ed25519Error::InvalidSignature)?;
|
||||
|
||||
// Parse out pubkey
|
||||
let pubkey = get_data_slice(
|
||||
instruction_datas,
|
||||
offsets.public_key_instruction_index,
|
||||
offsets.public_key_offset,
|
||||
PUBKEY_SERIALIZED_SIZE,
|
||||
)?;
|
||||
|
||||
let publickey = ed25519_dalek::PublicKey::from_bytes(pubkey)
|
||||
.map_err(|_| Ed25519Error::InvalidPublicKey)?;
|
||||
|
||||
// Parse out message
|
||||
let message = get_data_slice(
|
||||
instruction_datas,
|
||||
offsets.message_instruction_index,
|
||||
offsets.message_data_offset,
|
||||
offsets.message_data_size as usize,
|
||||
)?;
|
||||
|
||||
publickey
|
||||
.verify(message, &signature)
|
||||
.map_err(|_| Ed25519Error::InvalidSignature)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_data_slice<'a>(
|
||||
instruction_datas: &'a [&[u8]],
|
||||
instruction_index: u16,
|
||||
offset_start: u16,
|
||||
size: usize,
|
||||
) -> Result<&'a [u8], Ed25519Error> {
|
||||
let signature_index = instruction_index as usize;
|
||||
if signature_index >= instruction_datas.len() {
|
||||
return Err(Ed25519Error::InvalidDataOffsets);
|
||||
}
|
||||
let signature_instruction = &instruction_datas[signature_index];
|
||||
let start = offset_start as usize;
|
||||
let end = start.saturating_add(size);
|
||||
if end > signature_instruction.len() {
|
||||
return Err(Ed25519Error::InvalidDataOffsets);
|
||||
}
|
||||
|
||||
Ok(&instruction_datas[signature_index][start..end])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
fn test_case(
|
||||
num_signatures: u16,
|
||||
offsets: &Ed25519SignatureOffsets,
|
||||
) -> Result<(), Ed25519Error> {
|
||||
assert_eq!(
|
||||
bytemuck::bytes_of(offsets).len(),
|
||||
SIGNATURE_OFFSETS_SERIALIZED_SIZE
|
||||
);
|
||||
|
||||
let mut instruction_data = vec![0u8; DATA_START];
|
||||
instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&num_signatures));
|
||||
instruction_data[SIGNATURE_OFFSETS_START..DATA_START].copy_from_slice(bytes_of(offsets));
|
||||
|
||||
verify_signatures(&instruction_data, &[&[0u8; 100]])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_offsets() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mut instruction_data = vec![0u8; DATA_START];
|
||||
let offsets = Ed25519SignatureOffsets::default();
|
||||
instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&1u16));
|
||||
instruction_data[SIGNATURE_OFFSETS_START..DATA_START].copy_from_slice(bytes_of(&offsets));
|
||||
instruction_data.truncate(instruction_data.len() - 1);
|
||||
|
||||
assert_eq!(
|
||||
verify_signatures(&instruction_data, &[&[0u8; 100]]),
|
||||
Err(Ed25519Error::InvalidInstructionDataSize)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
signature_instruction_index: 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
message_instruction_index: 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
public_key_instruction_index: 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_data_offsets() {
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
message_data_offset: 99,
|
||||
message_data_size: 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(test_case(1, &offsets), Err(Ed25519Error::InvalidSignature));
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
message_data_offset: 100,
|
||||
message_data_size: 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
message_data_offset: 100,
|
||||
message_data_size: 1000,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
message_data_offset: std::u16::MAX,
|
||||
message_data_size: std::u16::MAX,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pubkey_offset() {
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
public_key_offset: std::u16::MAX,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
public_key_offset: 100 - PUBKEY_SERIALIZED_SIZE as u16 + 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_offset() {
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
signature_offset: std::u16::MAX,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
|
||||
let offsets = Ed25519SignatureOffsets {
|
||||
signature_offset: 100 - SIGNATURE_SERIALIZED_SIZE as u16 + 1,
|
||||
..Ed25519SignatureOffsets::default()
|
||||
};
|
||||
assert_eq!(
|
||||
test_case(1, &offsets),
|
||||
Err(Ed25519Error::InvalidDataOffsets)
|
||||
);
|
||||
}
|
||||
}
|
@ -239,6 +239,10 @@ pub mod sol_log_data_syscall_enabled {
|
||||
solana_sdk::declare_id!("HYPs7jyJ3KwQFdDpuSzMtVKf1MLJDaZRv3CSWvfUqdFo");
|
||||
}
|
||||
|
||||
pub mod ed25519_program_enabled {
|
||||
solana_sdk::declare_id!("E1TvTNipX8TKNHrhRC8SMuAwQmGY58TZ4drdztP3Gxwc");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
@ -299,6 +303,7 @@ lazy_static! {
|
||||
(remove_native_loader::id(), "Remove support for the native loader"),
|
||||
(return_data_syscall_enabled::id(), "enable sol_{set,get}_return_data syscall"),
|
||||
(sol_log_data_syscall_enabled::id(), "enable sol_log_data syscall"),
|
||||
(ed25519_program_enabled::id(), "enable builtin ed25519 signature verify program"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
@ -18,6 +18,7 @@ pub mod commitment_config;
|
||||
pub mod compute_budget;
|
||||
pub mod derivation_path;
|
||||
pub mod deserialize_utils;
|
||||
pub mod ed25519_instruction;
|
||||
pub mod entrypoint;
|
||||
pub mod entrypoint_deprecated;
|
||||
pub mod entrypoint_native;
|
||||
|
@ -5,6 +5,7 @@
|
||||
use crate::sanitize::{Sanitize, SanitizeError};
|
||||
use crate::secp256k1_instruction::verify_eth_addresses;
|
||||
use crate::{
|
||||
ed25519_instruction::verify_signatures,
|
||||
feature_set,
|
||||
hash::Hash,
|
||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
||||
@ -432,6 +433,18 @@ impl Transaction {
|
||||
feature_set.is_active(&feature_set::libsecp256k1_0_5_upgrade_enabled::id()),
|
||||
);
|
||||
e.map_err(|_| TransactionError::InvalidAccountIndex)?;
|
||||
} else if crate::ed25519_program::check_id(program_id)
|
||||
&& feature_set.is_active(&feature_set::ed25519_program_enabled::id())
|
||||
{
|
||||
let instruction_datas: Vec<_> = self
|
||||
.message()
|
||||
.instructions
|
||||
.iter()
|
||||
.map(|instruction| instruction.data.as_ref())
|
||||
.collect();
|
||||
let data = &instruction.data;
|
||||
let e = verify_signatures(data, &instruction_datas);
|
||||
e.map_err(|_| TransactionError::InvalidAccountIndex)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
Reference in New Issue
Block a user