diff --git a/pallas-applying/docs/byron-validation-rules.md b/pallas-applying/docs/byron-validation-rules.md
index 59b86b37..2aa01c15 100644
--- a/pallas-applying/docs/byron-validation-rules.md
+++ b/pallas-applying/docs/byron-validation-rules.md
@@ -20,7 +20,6 @@ Refer to the [Byron's ledger white paper](https://github.com/input-output-hk/car
- ***fees: Tx → ℕ*** gives the fees paid by a transaction, defined as follows:
- ***fees(tx) := balance (txIns(tx) ◁ utxo) − balance (txOuts(tx))***, where
- ***balance : P(TxOut) → ℕ*** gives the summation of all the lovelaces in a set of transaction outputs.
-
- **Serialization**:
- ***Bytes*** is the set of byte arrays (a.k.a. data, upon which signatures are built).
- ***⟦_⟧A : A -> Bytes*** takes an element of type ***A*** and returns a byte array resulting from serializing it.
diff --git a/pallas-applying/src/byron.rs b/pallas-applying/src/byron.rs
index 1b646ef1..ef604cdf 100644
--- a/pallas-applying/src/byron.rs
+++ b/pallas-applying/src/byron.rs
@@ -1,14 +1,30 @@
//! Utilities required for Byron-era transaction validation.
-use crate::types::{ByronProtParams, UTxOs, ValidationResult};
+use crate::types::{ByronProtParams, UTxOs, ValidationError, ValidationResult};
-use pallas_primitives::byron::MintedTxPayload;
+use pallas_primitives::byron::{MintedTxPayload, Tx};
-// TODO: implement each of the validation rules.
+// TODO: implement missing validation rules.
pub fn validate_byron_tx(
- _mtxp: &MintedTxPayload,
+ mtxp: &MintedTxPayload,
_utxos: &UTxOs,
_prot_pps: &ByronProtParams,
) -> ValidationResult {
+ let tx: &Tx = &mtxp.transaction;
+ check_ins_not_empty(tx)?;
+ check_outs_not_empty(tx)
+}
+
+fn check_ins_not_empty(tx: &Tx) -> ValidationResult {
+ if tx.inputs.clone().to_vec().is_empty() {
+ return Err(ValidationError::TxInsEmpty);
+ }
+ Ok(())
+}
+
+fn check_outs_not_empty(tx: &Tx) -> ValidationResult {
+ if tx.outputs.clone().to_vec().is_empty() {
+ return Err(ValidationError::TxOutsEmpty);
+ }
Ok(())
}
diff --git a/pallas-applying/src/types.rs b/pallas-applying/src/types.rs
index 52eb0418..017e7df4 100644
--- a/pallas-applying/src/types.rs
+++ b/pallas-applying/src/types.rs
@@ -21,7 +21,8 @@ pub enum MultiEraProtParams<'b> {
#[derive(Debug)]
#[non_exhaustive]
pub enum ValidationError {
- ValidationError,
+ TxInsEmpty,
+ TxOutsEmpty,
}
pub type ValidationResult = Result<(), ValidationError>;
diff --git a/pallas-applying/tests/byron.rs b/pallas-applying/tests/byron.rs
index d41b54df..70afe974 100644
--- a/pallas-applying/tests/byron.rs
+++ b/pallas-applying/tests/byron.rs
@@ -2,7 +2,7 @@ use rand::Rng;
use std::{borrow::Cow, vec::Vec};
use pallas_applying::{
- types::{ByronProtParams, MultiEraProtParams},
+ types::{ByronProtParams, MultiEraProtParams, ValidationError},
validate, UTxOs, ValidationResult,
};
use pallas_codec::{
@@ -29,32 +29,92 @@ mod byron_tests {
// i) the transaction input contains 100000 lovelace,
// ii) the minimum_fee_constant protocol parameter is 7,
// iii) the minimum_fee_factor protocol parameter is 11, and
- // iv) the size of the transaction is 82 bytes—it is easy to verify that
- // 82 == pallas_applying::get_byron_tx_size(tx)
- // The expected fees are therefore 7 + 11 * 82 = 909 lovelace, which is why the output contains
- // 100000 - 909 = 99091 lovelace.
+ // iv) the size of the transaction is 82 bytes—it is easy to verify
+ // that 82 == pallas_applying::get_byron_tx_size(tx).
+ // The expected fees are therefore 7 + 11 * 82 = 909 lovelace, which is why
+ // the output contains 100000 - 909 = 99091 lovelace.
fn successful_case() {
let protocol_params: ByronProtParams = ByronProtParams;
- let mut tx_ins: ByronTxIns = empty_byron_tx_ins();
- let tx_in: ByronTxIn = new_tx_in(random_tx_id(), 3);
+ let mut tx_ins: ByronTxIns = empty_tx_ins();
+ let tx_in: ByronTxIn = new_tx_in(rand_tx_id(), 3);
add_byron_tx_in(&mut tx_ins, &tx_in);
- let mut tx_outs: ByronTxOuts = new_byron_tx_outs();
- let tx_out: ByronTxOut = new_byron_tx_out(new_address(random_address_payload(), 0), 99091);
- add_byron_tx_out(&mut tx_outs, &tx_out);
+ let mut tx_outs: ByronTxOuts = new_tx_outs();
+ let tx_out_addr: Address = new_addr(rand_addr_payload(), 0);
+ let tx_out: ByronTxOut = new_tx_out(tx_out_addr, 99091);
+ add_tx_out(&mut tx_outs, &tx_out);
let mut utxos: UTxOs = new_utxos();
// input_tx_out is the ByronTxOut associated with tx_in.
- let input_tx_out: ByronTxOut =
- new_byron_tx_out(new_address(random_address_payload(), 0), 100000);
+ let input_tx_out_addr: Address = new_addr(rand_addr_payload(), 0);
+ let input_tx_out: ByronTxOut = new_tx_out(input_tx_out_addr, 100000);
add_to_utxo(&mut utxos, tx_in, input_tx_out);
let validation_result = mk_byron_tx_and_validate(
- &new_byron_tx(tx_ins, tx_outs, empty_byron_attributes()),
- &empty_byron_witnesses(),
+ &new_tx(tx_ins, tx_outs, empty_attributes()),
+ &empty_witnesses(),
&utxos,
&protocol_params,
);
match validation_result {
Ok(()) => (),
- Err(err) => assert!(false, "Unexpected error (sucessful_case - {:?}).", err),
+ Err(err) => assert!(false, "Unexpected error ({:?}).", err),
+ }
+ }
+
+ #[test]
+ // Similar to successful_case, except that no inputs are added to the
+ // transaction, which should raise a ValidationError:TxInsEmpty error.
+ fn empty_ins() {
+ let protocol_params: ByronProtParams = ByronProtParams;
+ let tx_ins: ByronTxIns = empty_tx_ins();
+ // Note: tx_in is not added to tx_ins, it is only added to the UTxOs set
+ let tx_in: ByronTxIn = new_tx_in(rand_tx_id(), 3);
+ let mut tx_outs: ByronTxOuts = new_tx_outs();
+ let tx_out_addr: Address = new_addr(rand_addr_payload(), 0);
+ let tx_out: ByronTxOut = new_tx_out(tx_out_addr, 99091);
+ add_tx_out(&mut tx_outs, &tx_out);
+ let mut utxos: UTxOs = new_utxos();
+ let input_tx_out_addr: Address = new_addr(rand_addr_payload(), 0);
+ let input_tx_out: ByronTxOut = new_tx_out(input_tx_out_addr, 100000);
+ add_to_utxo(&mut utxos, tx_in, input_tx_out);
+ let validation_result = mk_byron_tx_and_validate(
+ &new_tx(tx_ins, tx_outs, empty_attributes()),
+ &empty_witnesses(),
+ &utxos,
+ &protocol_params,
+ );
+ match validation_result {
+ Ok(()) => assert!(false, "Inputs set should not be empty."),
+ Err(err) => match err {
+ ValidationError::TxInsEmpty => (),
+ _ => assert!(false, "Unexpected error ({:?}).", err),
+ },
+ }
+ }
+
+ #[test]
+ // Similar to empty_ins, except that this time no outputs are added to the
+ // transaction, which should raise a ValidationError:TxOutsEmpty error.
+ fn empty_outs() {
+ let protocol_params: ByronProtParams = ByronProtParams;
+ let mut tx_ins: ByronTxIns = empty_tx_ins();
+ let tx_in: ByronTxIn = new_tx_in(rand_tx_id(), 3);
+ add_byron_tx_in(&mut tx_ins, &tx_in);
+ let tx_outs: ByronTxOuts = new_tx_outs();
+ let mut utxos: UTxOs = new_utxos();
+ let input_tx_out_addr: Address = new_addr(rand_addr_payload(), 0);
+ let input_tx_out: ByronTxOut = new_tx_out(input_tx_out_addr, 100000);
+ add_to_utxo(&mut utxos, tx_in, input_tx_out);
+ let validation_result = mk_byron_tx_and_validate(
+ &new_tx(tx_ins, tx_outs, empty_attributes()),
+ &empty_witnesses(),
+ &utxos,
+ &protocol_params,
+ );
+ match validation_result {
+ Ok(()) => assert!(false, "Outputs set should not be empty."),
+ Err(err) => match err {
+ ValidationError::TxOutsEmpty => (),
+ _ => assert!(false, "Unexpected error ({:?}).", err),
+ },
}
}
}
@@ -64,11 +124,11 @@ type ByronTxIns = MaybeIndefArray;
type ByronTxOuts = MaybeIndefArray;
// Helper functions.
-fn empty_byron_tx_ins() -> ByronTxIns {
+fn empty_tx_ins() -> ByronTxIns {
MaybeIndefArray::Def(Vec::new())
}
-fn random_tx_id() -> ByronTxId {
+fn rand_tx_id() -> ByronTxId {
let mut rng = rand::thread_rng();
let mut bytes = [0u8; 32];
for elem in bytes.iter_mut() {
@@ -87,11 +147,11 @@ fn add_byron_tx_in(ins: &mut ByronTxIns, new_in: &ByronTxIn) {
}
}
-fn new_byron_tx_outs() -> ByronTxOuts {
+fn new_tx_outs() -> ByronTxOuts {
MaybeIndefArray::Def(Vec::new())
}
-fn random_address_payload() -> TagWrap {
+fn rand_addr_payload() -> TagWrap {
let mut rng = rand::thread_rng();
let mut bytes = [0u8; 24];
for elem in bytes.iter_mut() {
@@ -100,21 +160,21 @@ fn random_address_payload() -> TagWrap {
TagWrap::::new(ByteVec::from(bytes.to_vec()))
}
-fn new_address(payload: TagWrap, crc: u32) -> Address {
+fn new_addr(payload: TagWrap, crc: u32) -> Address {
Address {
payload: payload,
crc: crc,
}
}
-fn new_byron_tx_out(address: Address, amount: u64) -> ByronTxOut {
+fn new_tx_out(address: Address, amount: u64) -> ByronTxOut {
ByronTxOut {
address: address,
amount: amount,
}
}
-fn add_byron_tx_out(outs: &mut ByronTxOuts, new_out: &ByronTxOut) {
+fn add_tx_out(outs: &mut ByronTxOuts, new_out: &ByronTxOut) {
match outs {
MaybeIndefArray::Def(vec) | MaybeIndefArray::Indef(vec) => vec.push(new_out.clone()),
}
@@ -126,13 +186,14 @@ fn add_to_utxo<'a>(utxos: &mut UTxOs<'a>, tx_in: ByronTxIn, tx_out: ByronTxOut)
utxos.insert(multi_era_in, multi_era_out);
}
-fn empty_byron_attributes() -> Attributes {
+fn empty_attributes() -> Attributes {
EmptyMap
}
-/// pallas_applying::validate takes a MultiEraTx, not a ByronTx and a ByronWitnesses. To be able to
-/// build a MultiEraTx from a ByronTx and a ByronWitnesses, we need to encode each of them and then
-/// decode them into KeepRaw and KeepRaw values, respectively.
+// pallas_applying::validate takes a MultiEraTx, not a ByronTx and a
+// ByronWitnesses. To be able to build a MultiEraTx from a ByronTx and a
+// ByronWitnesses, we need to encode each of them and then decode them into
+// KeepRaw and KeepRaw values, respectively.
fn mk_byron_tx_and_validate(
btx: &ByronTx,
bwit: &ByronWitnesses,
@@ -175,7 +236,7 @@ fn mk_byron_tx_and_validate(
)
}
-fn new_byron_tx(ins: ByronTxIns, outs: ByronTxOuts, attrs: Attributes) -> ByronTx {
+fn new_tx(ins: ByronTxIns, outs: ByronTxOuts, attrs: Attributes) -> ByronTx {
ByronTx {
inputs: ins,
outputs: outs,
@@ -183,7 +244,7 @@ fn new_byron_tx(ins: ByronTxIns, outs: ByronTxOuts, attrs: Attributes) -> ByronT
}
}
-fn empty_byron_witnesses() -> ByronWitnesses {
+fn empty_witnesses() -> ByronWitnesses {
MaybeIndefArray::Def(Vec::new())
}