feat(applying): implement Alonzo phase-1 validations (#380)

This commit is contained in:
Maico Leberle 2024-01-19 22:19:46 -03:00 committed by GitHub
parent 0b1e5f0231
commit da3e636759
33 changed files with 4900 additions and 704 deletions

View file

@ -1,67 +1,41 @@
use std::{borrow::Cow, vec::Vec};
pub mod common;
use common::{cbor_to_bytes, minted_tx_payload_from_cbor, mk_utxo_for_byron_tx};
use pallas_applying::{
types::{
ByronError::*, ByronProtParams, Environment, FeePolicy, MultiEraProtParams,
ValidationError::*,
utils::{
ByronError, ByronProtParams, Environment, FeePolicy, MultiEraProtParams, ValidationError::*,
},
validate, UTxOs,
};
use pallas_codec::{
minicbor::{
bytes::ByteVec,
decode::{Decode, Decoder},
encode,
},
utils::{CborWrap, MaybeIndefArray, TagWrap},
utils::{CborWrap, MaybeIndefArray},
};
use pallas_primitives::byron::{Address, MintedTxPayload, Twit, Tx, TxIn, TxOut, Witnesses};
use pallas_traverse::{MultiEraInput, MultiEraOutput, MultiEraTx};
use pallas_primitives::byron::{MintedTxPayload, Twit, Tx, TxOut, Witnesses};
use pallas_traverse::MultiEraTx;
use std::vec::Vec;
#[cfg(test)]
mod byron_tests {
use super::*;
fn cbor_to_bytes(input: &str) -> Vec<u8> {
hex::decode(input).unwrap()
}
fn tx_from_cbor<'a>(tx_cbor: &'a Vec<u8>) -> MintedTxPayload<'a> {
pallas_codec::minicbor::decode::<MintedTxPayload>(&tx_cbor[..]).unwrap()
}
// Careful: this function assumes tx has exactly one input.
fn mk_utxo_for_single_input_tx<'a>(tx: &Tx, address_payload: String, amount: u64) -> UTxOs<'a> {
let mut tx_ins: Vec<TxIn> = tx.inputs.clone().to_vec();
assert_eq!(tx_ins.len(), 1, "Unexpected number of inputs");
let tx_in: TxIn = tx_ins.pop().unwrap();
let input_tx_out_addr: Address = match hex::decode(address_payload) {
Ok(addr_bytes) => Address {
payload: TagWrap(ByteVec::from(addr_bytes)),
crc: 3430631884,
},
_ => panic!("Unable to decode input address"),
};
let tx_out: TxOut = TxOut {
address: input_tx_out_addr,
amount,
};
let mut utxos: UTxOs = UTxOs::new();
add_to_utxo(&mut utxos, tx_in, tx_out);
utxos
}
#[test]
// Transaction hash:
// a9e4413a5fb61a7a43c7df006ffcaaf3f2ffc9541f54757023968c5a8f8294fd
fn successful_mainnet_tx_with_genesis_utxos() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron2.tx"));
let mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron2.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron2.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -86,12 +60,14 @@ mod byron_tests {
// a06e5a0150e09f8983be2deafab9e04afc60d92e7110999eb672c903343f1e26
fn successful_mainnet_tx() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -115,11 +91,13 @@ mod byron_tests {
// Identical to successful_mainnet_tx, except that all inputs are removed.
fn empty_ins() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mut mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let mut mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
// Clear the set of inputs in the transaction.
let mut tx: Tx = (*mtxp.transaction).clone();
@ -146,7 +124,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "Inputs set should not be empty"),
Err(err) => match err {
Byron(TxInsEmpty) => (),
Byron(ByronError::TxInsEmpty) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -156,7 +134,7 @@ mod byron_tests {
// Identical to successful_mainnet_tx, except that all outputs are removed.
fn empty_outs() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mut mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mut mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
// Clear the set of outputs in the transaction.
let mut tx: Tx = (*mtxp.transaction).clone();
tx.outputs = MaybeIndefArray::Def(Vec::new());
@ -167,10 +145,12 @@ mod byron_tests {
};
mtxp.transaction = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -187,7 +167,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "Outputs set should not be empty"),
Err(err) => match err {
Byron(TxOutsEmpty) => (),
Byron(ByronError::TxOutsEmpty) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -197,7 +177,7 @@ mod byron_tests {
// The transaction is valid, but the UTxO set is empty.
fn unfound_utxo() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = UTxOs::new();
let env: Environment = Environment {
@ -215,7 +195,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "All inputs must be within the UTxO set"),
Err(err) => match err {
Byron(InputNotInUTxO) => (),
Byron(ByronError::InputNotInUTxO) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -225,7 +205,7 @@ mod byron_tests {
// All lovelace in one of the outputs was removed.
fn output_without_lovelace() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mut mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mut mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
// Remove lovelace from output.
let mut tx: Tx = (*mtxp.transaction).clone();
let altered_tx_out: TxOut = TxOut {
@ -242,10 +222,12 @@ mod byron_tests {
};
mtxp.transaction = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -262,7 +244,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "All outputs must contain lovelace"),
Err(err) => match err {
Byron(OutputWithoutLovelace) => (),
Byron(ByronError::OutputWithoutLovelace) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -272,12 +254,14 @@ mod byron_tests {
// Expected fees are increased by increasing the protocol parameters.
fn not_enough_fees() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -294,7 +278,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "Fees should not be below minimum"),
Err(err) => match err {
Byron(FeesBelowMin) => (),
Byron(ByronError::FeesBelowMin) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -304,12 +288,14 @@ mod byron_tests {
// Tx size limit set by protocol parameters is established at 0.
fn tx_size_exceeds_max() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -326,7 +312,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "Transaction size cannot exceed protocol limit"),
Err(err) => match err {
Byron(MaxTxSizeExceeded) => (),
Byron(ByronError::MaxTxSizeExceeded) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -336,7 +322,7 @@ mod byron_tests {
// The input to the transaction does not have a corresponding witness.
fn missing_witness() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mut mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mut mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
// Remove witness
let new_witnesses: Witnesses = MaybeIndefArray::Def(Vec::new());
let mut tx_buf: Vec<u8> = Vec::new();
@ -346,10 +332,12 @@ mod byron_tests {
};
mtxp.witness = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -366,7 +354,7 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "All inputs must have a witness signature"),
Err(err) => match err {
Byron(MissingWitness) => (),
Byron(ByronError::MissingWitness) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
@ -377,7 +365,7 @@ mod byron_tests {
// wrong.
fn wrong_signature() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
let mut mtxp: MintedTxPayload = tx_from_cbor(&cbor_bytes);
let mut mtxp: MintedTxPayload = minted_tx_payload_from_cbor(&cbor_bytes);
// Modify signature in witness
let new_wit: Twit = match mtxp.witness[0].clone() {
Twit::PkWitness(CborWrap((pk, _))) => {
@ -395,10 +383,12 @@ mod byron_tests {
};
mtxp.witness = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_byron(&mtxp);
let utxos: UTxOs = mk_utxo_for_single_input_tx(
let utxos: UTxOs = mk_utxo_for_byron_tx(
&mtxp.transaction,
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
&[(
String::from(include_str!("../../test_data/byron1.address")),
19999000000,
)],
);
let env: Environment = Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
@ -415,16 +405,9 @@ mod byron_tests {
match validate(&metx, &utxos, &env) {
Ok(()) => assert!(false, "Witness signature should verify the transaction"),
Err(err) => match err {
Byron(WrongSignature) => (),
Byron(ByronError::WrongSignature) => (),
_ => panic!("Unexpected error ({:?})", err),
},
}
}
}
// Helper functions.
fn add_to_utxo<'a>(utxos: &mut UTxOs<'a>, tx_in: TxIn, tx_out: TxOut) {
let multi_era_in: MultiEraInput = MultiEraInput::Byron(Box::new(Cow::Owned(tx_in)));
let multi_era_out: MultiEraOutput = MultiEraOutput::Byron(Box::new(Cow::Owned(tx_out)));
utxos.insert(multi_era_in, multi_era_out);
}