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.
This commit is contained in:
Sean Young 2021-09-03 22:35:38 +01:00
parent d461a9ac10
commit 8b9e472a6c
17 changed files with 577 additions and 13 deletions

33
Cargo.lock generated
View File

@ -507,6 +507,26 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
[[package]]
name = "bytemuck"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.9",
"syn 1.0.67",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -4606,6 +4626,17 @@ dependencies = [
"tar",
]
[[package]]
name = "solana-ed25519-program"
version = "1.8.0"
dependencies = [
"bytemuck",
"ed25519-dalek",
"rand 0.7.3",
"solana-logger 1.8.0",
"solana-sdk",
]
[[package]]
name = "solana-entry"
version = "1.8.0"
@ -5455,6 +5486,7 @@ dependencies = [
"serde_derive",
"solana-compute-budget-program",
"solana-config-program",
"solana-ed25519-program",
"solana-frozen-abi 1.8.0",
"solana-frozen-abi-macro 1.8.0",
"solana-logger 1.8.0",
@ -5492,6 +5524,7 @@ dependencies = [
"borsh-derive",
"bs58 0.4.0",
"bv",
"bytemuck",
"byteorder",
"chrono",
"curve25519-dalek 3.2.0",

View File

@ -45,6 +45,7 @@ members = [
"programs/bpf_loader",
"programs/compute-budget",
"programs/config",
"programs/ed25519",
"programs/failure",
"programs/noop",
"programs/ownable",

View File

@ -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).

View File

@ -287,6 +287,26 @@ dependencies = [
"serde",
]
[[package]]
name = "bytemuck"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.6",
"syn 1.0.67",
]
[[package]]
name = "byteorder"
version = "0.5.3"
@ -2898,6 +2918,13 @@ dependencies = [
"winapi",
]
[[package]]
name = "solana-ed25519-program"
version = "1.8.0"
dependencies = [
"solana-sdk",
]
[[package]]
name = "solana-faucet"
version = "1.8.0"
@ -3209,6 +3236,7 @@ dependencies = [
"serde_derive",
"solana-compute-budget-program",
"solana-config-program",
"solana-ed25519-program",
"solana-frozen-abi 1.8.0",
"solana-frozen-abi-macro 1.8.0",
"solana-logger 1.8.0",
@ -3237,6 +3265,7 @@ dependencies = [
"borsh-derive",
"bs58 0.4.0",
"bv",
"bytemuck",
"byteorder 1.4.3",
"chrono",
"derivation-path",

View File

@ -0,0 +1,26 @@
[package]
name = "solana-ed25519-program"
description = "Solana Ed25519 program"
version = "1.8.0"
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.0" }
[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.0" }
[lib]
crate-type = ["lib"]
name = "solana_ed25519_program"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View 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());
}
}

View File

@ -36,6 +36,7 @@ serde = { version = "1.0.130", features = ["rc"] }
serde_derive = "1.0.103"
solana-config-program = { path = "../programs/config", version = "=1.8.0" }
solana-compute-budget-program = { path = "../programs/compute-budget", version = "=1.8.0" }
solana-ed25519-program = { path = "../programs/ed25519", version = "=1.8.0" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.8.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.8.0" }
solana-logger = { path = "../logger", version = "=1.8.0" }

View File

@ -135,15 +135,26 @@ fn genesis_builtins() -> Vec<Builtin> {
/// 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 {

View File

@ -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"
borsh-derive = "0.9.0"
bs58 = "0.4.0"

View File

@ -0,0 +1 @@
crate::declare_id!("Ed25519SigVerify111111111111111111111111111");

View File

@ -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::*;
@ -32,20 +33,22 @@ impl FeeCalculator {
note = "Please do not use, will no longer be available in the future"
)]
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;
// Message 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)
}
}

View File

@ -13,6 +13,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;

View 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)
);
}
}

View File

@ -195,6 +195,10 @@ pub mod demote_program_write_locks {
solana_sdk::declare_id!("3E3jV7v9VcdJL8iYZUMax9DiDno8j7EWUVbhm9RtShj2");
}
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> = [
@ -234,11 +238,12 @@ lazy_static! {
(gate_large_block::id(), "validator checks block cost against max limit in realtime, reject if exceeds."),
(mem_overlap_fix::id(), "memory overlap fix"),
(versioned_tx_message_enabled::id(), "enable versioned transaction message processing"),
(libsecp256k1_fail_on_bad_count::id(), "Fail libsec256k1_verify if count appears wrong"),
(libsecp256k1_fail_on_bad_count::id(), "fail libsec256k1_verify if count appears wrong"),
(instructions_sysvar_owned_by_sysvar::id(), "fix owner for instructions sysvar"),
(close_upgradeable_program_accounts::id(), "enable closing upgradeable program accounts"),
(stake_program_advance_activating_credits_observed::id(), "Enable advancing credits observed for activation epoch #19309"),
(demote_program_write_locks::id(), "demote program write locks to readonly #19593"),
(ed25519_program_enabled::id(), "enable builtin ed25519 signature verify program"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -17,6 +17,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;

View File

@ -4,6 +4,7 @@
use {
crate::{
ed25519_instruction::verify_signatures,
hash::Hash,
instruction::{CompiledInstruction, Instruction, InstructionError},
message::{Message, SanitizeMessageError},
@ -450,6 +451,18 @@ impl Transaction {
feature_set.is_active(&feature_set::libsecp256k1_fail_on_bad_count::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(())

View File

@ -2,6 +2,7 @@
use {
crate::{
ed25519_instruction::verify_signatures,
hash::Hash,
message::{v0, MappedAddresses, MappedMessage, SanitizedMessage, VersionedMessage},
nonce::NONCED_TX_MARKER_IX_INDEX,
@ -222,6 +223,18 @@ impl SanitizedTransaction {
feature_set.is_active(&feature_set::libsecp256k1_fail_on_bad_count::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(())