fix(applying): contemplate fee rules for genesis UTxOs (#332)
This commit is contained in:
parent
f26b9bd591
commit
317e23fb45
6 changed files with 102 additions and 22 deletions
|
|
@ -29,7 +29,7 @@ Refer to the [Byron's ledger white paper](https://github.com/input-output-hk/car
|
||||||
- ***addrHash<sub>utxo</sub> : TxIn -> KeyHash*** takes a transaction input, extracts its associated transaction output from ***utxo***, extracts the address contained in it, and returns its hash. In other words, given ***utxo*** and transaction input ***i*** such that ***utxo(i) = (a, _)***, we have that ***addrHash<sub>utxo</sub>(i) := hash(a)***.
|
- ***addrHash<sub>utxo</sub> : TxIn -> KeyHash*** takes a transaction input, extracts its associated transaction output from ***utxo***, extracts the address contained in it, and returns its hash. In other words, given ***utxo*** and transaction input ***i*** such that ***utxo(i) = (a, _)***, we have that ***addrHash<sub>utxo</sub>(i) := hash(a)***.
|
||||||
- **Protocol Parameters**:
|
- **Protocol Parameters**:
|
||||||
- ***pps ∈ PParams*** is the set of (Byron) protocol parameters, with the following associated functions:
|
- ***pps ∈ PParams*** is the set of (Byron) protocol parameters, with the following associated functions:
|
||||||
- ***minFees : PParams x Tx → ℕ*** gives the minimum amount of fees that must be paid for the transaction as determined by the protocol parameters.
|
- ***minFees : PParams x Tx → ℕ*** gives the minimum amount of fees that must be paid for the transaction as determined by the protocol parameters. If ***tx*** spends only genesis UTxOs (i.e., only input UTxOs generated at the genesis of the ledger), then ***minFees(pps, tx) = 0***.
|
||||||
- ***maxTxSize : PParams → ℕ*** gives the (global) maximum transaction size.
|
- ***maxTxSize : PParams → ℕ*** gives the (global) maximum transaction size.
|
||||||
- ***Witnesses***:
|
- ***Witnesses***:
|
||||||
- ***VKey*** is the set of verification keys (a.k.a. public keys).
|
- ***VKey*** is the set of verification keys (a.k.a. public keys).
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,11 @@ fn check_outs_have_lovelace(tx: &Tx) -> ValidationResult {
|
||||||
|
|
||||||
fn check_fees(tx: &Tx, size: &u64, utxos: &UTxOs, prot_pps: &ByronProtParams) -> ValidationResult {
|
fn check_fees(tx: &Tx, size: &u64, utxos: &UTxOs, prot_pps: &ByronProtParams) -> ValidationResult {
|
||||||
let mut inputs_balance: u64 = 0;
|
let mut inputs_balance: u64 = 0;
|
||||||
|
let mut only_redeem_utxos: bool = true;
|
||||||
for input in tx.inputs.iter() {
|
for input in tx.inputs.iter() {
|
||||||
|
if !is_redeem_utxo(input, utxos) {
|
||||||
|
only_redeem_utxos = false;
|
||||||
|
}
|
||||||
match utxos
|
match utxos
|
||||||
.get(&MultiEraInput::from_byron(input))
|
.get(&MultiEraInput::from_byron(input))
|
||||||
.and_then(MultiEraOutput::as_byron)
|
.and_then(MultiEraOutput::as_byron)
|
||||||
|
|
@ -83,6 +87,9 @@ fn check_fees(tx: &Tx, size: &u64, utxos: &UTxOs, prot_pps: &ByronProtParams) ->
|
||||||
None => return Err(ValidationError::UnableToComputeFees),
|
None => return Err(ValidationError::UnableToComputeFees),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if only_redeem_utxos {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
let mut outputs_balance: u64 = 0;
|
let mut outputs_balance: u64 = 0;
|
||||||
for output in tx.outputs.iter() {
|
for output in tx.outputs.iter() {
|
||||||
outputs_balance += output.amount
|
outputs_balance += output.amount
|
||||||
|
|
@ -90,9 +97,24 @@ fn check_fees(tx: &Tx, size: &u64, utxos: &UTxOs, prot_pps: &ByronProtParams) ->
|
||||||
let total_balance: u64 = inputs_balance - outputs_balance;
|
let total_balance: u64 = inputs_balance - outputs_balance;
|
||||||
let min_fees: u64 = prot_pps.min_fees_const + prot_pps.min_fees_factor * size;
|
let min_fees: u64 = prot_pps.min_fees_const + prot_pps.min_fees_factor * size;
|
||||||
if total_balance < min_fees {
|
if total_balance < min_fees {
|
||||||
return Err(ValidationError::FeesBelowMin);
|
Err(ValidationError::FeesBelowMin)
|
||||||
}
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_redeem_utxo(input: &TxIn, utxos: &UTxOs) -> bool {
|
||||||
|
match find_tx_out(input, utxos) {
|
||||||
|
Ok(tx_out) => {
|
||||||
|
let address: ByronAddress = mk_byron_address(&tx_out.address);
|
||||||
|
match address.decode() {
|
||||||
|
Ok(addr_payload) => matches!(addr_payload.addrtype, AddrType::Redeem),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_size(size: &u64, prot_pps: &ByronProtParams) -> ValidationResult {
|
fn check_size(size: &u64, prot_pps: &ByronProtParams) -> ValidationResult {
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,11 @@ mod byron_tests {
|
||||||
pallas_codec::minicbor::decode::<MintedTxPayload>(&tx_cbor[..]).unwrap()
|
pallas_codec::minicbor::decode::<MintedTxPayload>(&tx_cbor[..]).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_utxo<'a>(tx: &Tx) -> UTxOs<'a> {
|
// 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();
|
let mut tx_ins: Vec<TxIn> = tx.inputs.clone().to_vec();
|
||||||
assert_eq!(tx_ins.len(), 1, "Unexpected number of inputs.");
|
assert_eq!(tx_ins.len(), 1, "Unexpected number of inputs.");
|
||||||
let tx_in: TxIn = tx_ins.pop().unwrap();
|
let tx_in: TxIn = tx_ins.pop().unwrap();
|
||||||
let address_payload =
|
|
||||||
"83581cff66e7549ee0706abe5ce63ba325f792f2c1145d918baf563db2b457a101581e581cca3e553c9c63\
|
|
||||||
c5927480e7434620200eb3a162ef0b6cf6f671ba925100";
|
|
||||||
let input_tx_out_addr: Address = match hex::decode(address_payload) {
|
let input_tx_out_addr: Address = match hex::decode(address_payload) {
|
||||||
Ok(addr_bytes) => Address {
|
Ok(addr_bytes) => Address {
|
||||||
payload: TagWrap(ByteVec::from(addr_bytes)),
|
payload: TagWrap(ByteVec::from(addr_bytes)),
|
||||||
|
|
@ -43,18 +41,47 @@ mod byron_tests {
|
||||||
};
|
};
|
||||||
let tx_out: TxOut = TxOut {
|
let tx_out: TxOut = TxOut {
|
||||||
address: input_tx_out_addr,
|
address: input_tx_out_addr,
|
||||||
amount: 19999000000,
|
amount: amount,
|
||||||
};
|
};
|
||||||
let mut utxos: UTxOs = new_utxos();
|
let mut utxos: UTxOs = new_utxos();
|
||||||
add_to_utxo(&mut utxos, tx_in, tx_out);
|
add_to_utxo(&mut utxos, tx_in, tx_out);
|
||||||
utxos
|
utxos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn successful_mainnet_tx_with_genesis_utxos() {
|
||||||
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron2.tx"));
|
||||||
|
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron2.address")),
|
||||||
|
// The number of lovelace in this input is irrelevant, since no fees have to be paid
|
||||||
|
// for this transaction.
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
let env: Environment = Environment {
|
||||||
|
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
||||||
|
min_fees_const: 155381,
|
||||||
|
min_fees_factor: 44,
|
||||||
|
max_tx_size: 4096,
|
||||||
|
}),
|
||||||
|
prot_magic: 764824073,
|
||||||
|
};
|
||||||
|
match mk_byron_tx_and_validate(&mtxp.transaction, &mtxp.witness, &utxos, &env) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => assert!(false, "Unexpected error ({:?}).", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_mainnet_tx() {
|
fn successful_mainnet_tx() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
let env: Environment = Environment {
|
let env: Environment = Environment {
|
||||||
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
||||||
min_fees_const: 155381,
|
min_fees_const: 155381,
|
||||||
|
|
@ -74,7 +101,11 @@ mod byron_tests {
|
||||||
fn empty_ins() {
|
fn empty_ins() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
// Clear the set of inputs in the transaction.
|
// Clear the set of inputs in the transaction.
|
||||||
let mut tx: Tx = (*mtxp.transaction).clone();
|
let mut tx: Tx = (*mtxp.transaction).clone();
|
||||||
tx.inputs = MaybeIndefArray::Def(Vec::new());
|
tx.inputs = MaybeIndefArray::Def(Vec::new());
|
||||||
|
|
@ -106,7 +137,11 @@ mod byron_tests {
|
||||||
fn empty_outs() {
|
fn empty_outs() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
// Clear the set of outputs in the transaction.
|
// Clear the set of outputs in the transaction.
|
||||||
let mut tx: Tx = (*mtxp.transaction).clone();
|
let mut tx: Tx = (*mtxp.transaction).clone();
|
||||||
tx.outputs = MaybeIndefArray::Def(Vec::new());
|
tx.outputs = MaybeIndefArray::Def(Vec::new());
|
||||||
|
|
@ -161,7 +196,11 @@ mod byron_tests {
|
||||||
fn output_without_lovelace() {
|
fn output_without_lovelace() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
// Remove lovelace from output.
|
// Remove lovelace from output.
|
||||||
let mut tx: Tx = (*mtxp.transaction).clone();
|
let mut tx: Tx = (*mtxp.transaction).clone();
|
||||||
let altered_tx_out: TxOut = TxOut {
|
let altered_tx_out: TxOut = TxOut {
|
||||||
|
|
@ -200,7 +239,11 @@ mod byron_tests {
|
||||||
fn not_enough_fees() {
|
fn not_enough_fees() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
let env: Environment = Environment {
|
let env: Environment = Environment {
|
||||||
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
||||||
min_fees_const: 1000,
|
min_fees_const: 1000,
|
||||||
|
|
@ -223,7 +266,11 @@ mod byron_tests {
|
||||||
fn tx_size_exceeds_max() {
|
fn tx_size_exceeds_max() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
let env: Environment = Environment {
|
let env: Environment = Environment {
|
||||||
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
prot_params: MultiEraProtParams::Byron(ByronProtParams {
|
||||||
min_fees_const: 155381,
|
min_fees_const: 155381,
|
||||||
|
|
@ -246,7 +293,11 @@ mod byron_tests {
|
||||||
fn missing_witness() {
|
fn missing_witness() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
// Remove witness
|
// Remove witness
|
||||||
let new_witnesses: Witnesses = MaybeIndefArray::Def(Vec::new());
|
let new_witnesses: Witnesses = MaybeIndefArray::Def(Vec::new());
|
||||||
let mut tx_buf: Vec<u8> = Vec::new();
|
let mut tx_buf: Vec<u8> = Vec::new();
|
||||||
|
|
@ -278,7 +329,11 @@ mod byron_tests {
|
||||||
fn wrong_signature() {
|
fn wrong_signature() {
|
||||||
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/byron1.tx"));
|
||||||
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
let mut mtxp: MintedTxPayload = mainnet_tx_from_bytes_cbor(&cbor_bytes);
|
||||||
let utxos: UTxOs = build_utxo(&mtxp.transaction);
|
let utxos: UTxOs = mk_utxo_for_single_input_tx(
|
||||||
|
&mtxp.transaction,
|
||||||
|
String::from(include_str!("../../test_data/byron1.address")),
|
||||||
|
19999000000,
|
||||||
|
);
|
||||||
// Modify signature in witness
|
// Modify signature in witness
|
||||||
let new_wit: Twit = match mtxp.witness[0].clone() {
|
let new_wit: Twit = match mtxp.witness[0].clone() {
|
||||||
Twit::PkWitness(CborWrap((pk, _))) => {
|
Twit::PkWitness(CborWrap((pk, _))) => {
|
||||||
|
|
|
||||||
1
test_data/byron1.address
Normal file
1
test_data/byron1.address
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
83581cff66e7549ee0706abe5ce63ba325f792f2c1145d918baf563db2b457a101581e581cca3e553c9c63c5927480e7434620200eb3a162ef0b6cf6f671ba925100
|
||||||
1
test_data/byron2.address
Normal file
1
test_data/byron2.address
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
83581CDC7E4DD6A44886816DEC9A4B2021056A8FCAF500C09E316028F2985FA002
|
||||||
1
test_data/byron2.tx
Normal file
1
test_data/byron2.tx
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
82839f8200d818582482582031eae73dd9b018a6bc2eb1b229b5497de563a81872d54516251cc8185f93288300ff9f8282d818584283581c29bb9c5f4e8d38a10117e700a9238238d6f528df04b998af83ecc424a101581e581cc9deb2c011989356a5dc96450244e7ed22bfb30fbfc3f5807237fc39001a97bf58b71a000f4240ffa0818202d81858658258204d99cc5ec6c1891a483157560771a114463d92a530df8fd64e03f68f571de9b75840f67993afa4ec3b897355593e77baaf754ecfba2bba96265b829d79e4f452c7d6a6f363d41dea6731bff7a14efcc900e08f94e92c326344e540064ddf99052806
|
||||||
Loading…
Add table
Add a link
Reference in a new issue