Reject transactions with extra signatures (#18306)
* Reject transactions with extra signatures * fix tests * fix check * fix check * tx method * fix checks
This commit is contained in:
@ -791,8 +791,11 @@ pub fn confirm_slot(
|
||||
};
|
||||
|
||||
let check_start = Instant::now();
|
||||
let check_result =
|
||||
entries.verify_and_hash_transactions(skip_verification, bank.secp256k1_program_enabled());
|
||||
let check_result = entries.verify_and_hash_transactions(
|
||||
skip_verification,
|
||||
bank.secp256k1_program_enabled(),
|
||||
bank.verify_tx_signatures_len_enabled(),
|
||||
);
|
||||
if check_result.is_none() {
|
||||
warn!("Ledger proof of history failed at slot: {}", slot);
|
||||
return Err(BlockError::InvalidEntryHash.into());
|
||||
|
@ -360,6 +360,7 @@ pub trait EntrySlice {
|
||||
&self,
|
||||
skip_verification: bool,
|
||||
secp256k1_program_enabled: bool,
|
||||
verify_tx_signatures_len: bool,
|
||||
) -> Option<Vec<EntryType<'_>>>;
|
||||
}
|
||||
|
||||
@ -515,6 +516,7 @@ impl EntrySlice for [Entry] {
|
||||
&'a self,
|
||||
skip_verification: bool,
|
||||
secp256k1_program_enabled: bool,
|
||||
verify_tx_signatures_len: bool,
|
||||
) -> Option<Vec<EntryType<'a>>> {
|
||||
let verify_and_hash = |tx: &'a Transaction| -> Option<HashedTransaction<'a>> {
|
||||
let message_hash = if !skip_verification {
|
||||
@ -526,6 +528,9 @@ impl EntrySlice for [Entry] {
|
||||
// Verify tx precompiles if secp256k1 program is enabled.
|
||||
tx.verify_precompiles().ok()?;
|
||||
}
|
||||
if verify_tx_signatures_len && !tx.verify_signatures_len() {
|
||||
return None;
|
||||
}
|
||||
tx.verify_and_hash_message().ok()?
|
||||
} else {
|
||||
tx.message().hash()
|
||||
@ -885,6 +890,62 @@ mod tests {
|
||||
assert!(!bad_ticks.verify(&one)); // inductive step, bad
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_and_hash_transactions_sig_len() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let recent_blockhash = hash_new_rand(&mut rng);
|
||||
let from_keypair = Keypair::new();
|
||||
let to_keypair = Keypair::new();
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let to_pubkey = to_keypair.pubkey();
|
||||
|
||||
enum TestCase {
|
||||
AddSignature,
|
||||
RemoveSignature,
|
||||
}
|
||||
|
||||
let make_transaction = |case: TestCase| {
|
||||
let message = Message::new(
|
||||
&[system_instruction::transfer(&from_pubkey, &to_pubkey, 1)],
|
||||
Some(&from_pubkey),
|
||||
);
|
||||
let mut tx = Transaction::new(&[&from_keypair], message, recent_blockhash);
|
||||
assert_eq!(tx.message.header.num_required_signatures, 1);
|
||||
match case {
|
||||
TestCase::AddSignature => {
|
||||
let signature = to_keypair.sign_message(&tx.message.serialize());
|
||||
tx.signatures.push(signature);
|
||||
}
|
||||
TestCase::RemoveSignature => {
|
||||
tx.signatures.remove(0);
|
||||
}
|
||||
}
|
||||
tx
|
||||
};
|
||||
// No signatures.
|
||||
{
|
||||
let tx = make_transaction(TestCase::RemoveSignature);
|
||||
let entries = vec![next_entry(&recent_blockhash, 1, vec![tx])];
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false, false)
|
||||
.is_some());
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false, true)
|
||||
.is_none());
|
||||
}
|
||||
// Too many signatures.
|
||||
{
|
||||
let tx = make_transaction(TestCase::AddSignature);
|
||||
let entries = vec![next_entry(&recent_blockhash, 1, vec![tx])];
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false, false)
|
||||
.is_some());
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false, true)
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_and_hash_transactions_packet_data_size() {
|
||||
let mut rng = rand::thread_rng();
|
||||
@ -906,7 +967,7 @@ mod tests {
|
||||
let entries = vec![next_entry(&recent_blockhash, 1, vec![tx.clone()])];
|
||||
assert!(bincode::serialized_size(&tx).unwrap() <= PACKET_DATA_SIZE as u64);
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false)
|
||||
.verify_and_hash_transactions(false, false, false)
|
||||
.is_some());
|
||||
}
|
||||
// Big transaction.
|
||||
@ -915,7 +976,7 @@ mod tests {
|
||||
let entries = vec![next_entry(&recent_blockhash, 1, vec![tx.clone()])];
|
||||
assert!(bincode::serialized_size(&tx).unwrap() > PACKET_DATA_SIZE as u64);
|
||||
assert!(entries[..]
|
||||
.verify_and_hash_transactions(false, false)
|
||||
.verify_and_hash_transactions(false, false, false)
|
||||
.is_none());
|
||||
}
|
||||
// Assert that verify fails as soon as serialized
|
||||
@ -926,7 +987,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&tx).unwrap() <= PACKET_DATA_SIZE as u64,
|
||||
entries[..]
|
||||
.verify_and_hash_transactions(false, false)
|
||||
.verify_and_hash_transactions(false, false, false)
|
||||
.is_some(),
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user