Skip to content

Commit

Permalink
Rework Mint transaction to work with the contract (#593)
Browse files Browse the repository at this point in the history
* Rework `Mint` transaction to work with the contract

* Update CHANGELOG.md

* Make CI happy

* Small finding

* Apply comment

* Fix the constructor

* Use `ContractId` for coinbase instead of `Address`

---------

Co-authored-by: Brandon Vrooman <brandon.vrooman@fuel.sh>
  • Loading branch information
xgreenx and Brandon Vrooman authored Oct 9, 2023
1 parent 4bf0a35 commit a228e1c
Show file tree
Hide file tree
Showing 27 changed files with 395 additions and 227 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [#604](https://github.com/FuelLabs/fuel-vm/pull/604): Removed `ChainId` from `PredicateId` calculation. It changes the generated address of the predicates and may break tests or logic that uses hard-coded predicate IDs.
- [#594](https://github.com/FuelLabs/fuel-vm/pull/594): Add new predicate input validation tests. Also improves error propagation so that predicate error message better reflects the reason for invalidity.
- [#596](https://github.com/FuelLabs/fuel-vm/pull/596): Remove `core::ops::{Add, Sub}` impls from `BlockHeight`. Use `succ` and `pred` to access adjacent blocks, or perform arithmetic directly on the wrapped integer instead.
- [#593](https://github.com/FuelLabs/fuel-vm/pull/593): Reworked `Mint` transaction to work with `Input::Contract` and `Output::Contract` instead of `Output::Coin`. It allows account-based fee collection for the block producer.


## [Version 0.38.0]
Expand Down
16 changes: 14 additions & 2 deletions fuel-tx/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{
input,
output,
transaction::{
field,
field::{
Expand Down Expand Up @@ -179,10 +181,20 @@ impl TransactionBuilder<Create> {
}

impl TransactionBuilder<Mint> {
pub fn mint(block_height: BlockHeight, tx_index: u16) -> Self {
pub fn mint(
block_height: BlockHeight,
tx_index: u16,
input_contract: input::contract::Contract,
output_contract: output::contract::Contract,
mint_amount: Word,
mint_asset_id: AssetId,
) -> Self {
let tx = Mint {
tx_pointer: TxPointer::new(block_height, tx_index),
outputs: Default::default(),
input_contract,
output_contract,
mint_amount,
mint_asset_id,
metadata: None,
};

Expand Down
5 changes: 3 additions & 2 deletions fuel-tx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ pub use transaction::{
input,
input::Input,
input::InputRepr,
output,
output::Output,
output::OutputRepr,
Cacheable,
Chargeable,
CheckError,
Expand All @@ -83,8 +86,6 @@ pub use transaction::{
GasCostsValues,
GasUnit,
Mint,
Output,
OutputRepr,
PredicateParameters,
Script,
ScriptParameters,
Expand Down
15 changes: 7 additions & 8 deletions fuel-tx/src/tests/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,14 +407,13 @@ fn transaction() {
vec![],
),
]);
assert_encoding_correct(&[
Transaction::mint(rng.gen(), vec![o]),
Transaction::mint(rng.gen(), vec![o, o]),
Transaction::mint(rng.gen(), vec![o, o, o]),
Transaction::mint(rng.gen(), vec![o, o, o, o]),
Transaction::mint(rng.gen(), vec![o, o, o, o, o]),
Transaction::mint(rng.gen(), vec![o, o, o, o, o, o]),
]);
assert_encoding_correct(&[Transaction::mint(
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
)]);
}

#[test]
Expand Down
45 changes: 34 additions & 11 deletions fuel-tx/src/tests/offset.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use fuel_tx::{
field::{
InputContract,
Inputs,
MintAmount,
MintAssetId,
OutputContract,
Outputs,
ReceiptsRoot,
Salt as SaltField,
StorageSlots,
TxPointer as TxPointerField,
Witnesses,
},
input,
*,
};
use fuel_tx_test_helpers::TransactionFactory;
Expand Down Expand Up @@ -429,7 +434,6 @@ fn tx_offset_script() {

#[test]
fn tx_offset_mint() {
let mut cases = TestedFields::default();
let number_cases = 100;

// The seed will define how the transaction factory will generate a new transaction.
Expand All @@ -447,17 +451,36 @@ fn tx_offset_mint() {

assert_eq!(*tx.tx_pointer(), tx_pointer_p);

outputs_assert(&tx, &bytes, &mut cases);
});
let ofs = tx.input_contract_offset();
let size = tx.input_contract().size();
let input_p = input::contract::Contract::from_bytes(&bytes[ofs..ofs + size])
.expect("Should decode `input::contract::Contract`");

// Actually, `Mint` transaction works only with `Coin`s, but let's test all possible
// outputs.
assert!(cases.output_to);
assert!(cases.output_asset_id);
assert!(cases.output_balance_root);
assert!(cases.output_contract_state_root);
assert!(cases.output_contract_created_state_root);
assert!(cases.output_contract_created_id);
assert_eq!(*tx.input_contract(), input_p);

let ofs = tx.output_contract_offset();
let size = tx.output_contract().size();
let output_p =
output::contract::Contract::from_bytes(&bytes[ofs..ofs + size])
.expect("Should decode `output::contract::Contract`");

assert_eq!(*tx.output_contract(), output_p);

let ofs = tx.mint_amount_offset();
let size = tx.mint_amount().size();
let mint_amount_p =
Word::from_bytes(&bytes[ofs..ofs + size]).expect("Should decode `Word`");

assert_eq!(*tx.mint_amount(), mint_amount_p);

let ofs = tx.mint_asset_id_offset();
let size = tx.mint_asset_id().size();
let mint_asset_id_p =
<AssetId as Deserialize>::from_bytes(&bytes[ofs..ofs + size])
.expect("Should encode `AssetId`");

assert_eq!(*tx.mint_asset_id(), mint_asset_id_p);
});
}

#[test]
Expand Down
72 changes: 43 additions & 29 deletions fuel-tx/src/tests/valid_cases/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,39 +834,53 @@ fn mint() {

let block_height = 1000.into();

TransactionBuilder::mint(block_height, rng.gen())
.add_output(Output::coin(rng.gen(), rng.next_u64(), rng.gen()))
.add_output(Output::coin(rng.gen(), rng.next_u64(), rng.gen()))
.finalize()
.check(block_height, &test_params())
.expect("Failed to validate tx");

let err = TransactionBuilder::mint(block_height, rng.gen())
.add_output(Output::contract(0, rng.gen(), rng.gen()))
.finalize()
.check(block_height, &test_params())
.expect_err("Expected erroneous transaction");
let err = TransactionBuilder::mint(
block_height,
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
)
.finalize()
.check(block_height, &test_params())
.expect_err("Expected erroneous transaction");

assert_eq!(err, CheckError::TransactionMintOutputIsNotCoin);
assert_eq!(err, CheckError::TransactionMintIncorrectOutputIndex);

let err = TransactionBuilder::mint(block_height, rng.gen())
.add_output(Output::coin(rng.gen(), rng.next_u64(), AssetId::BASE))
.add_output(Output::coin(rng.gen(), rng.next_u64(), AssetId::BASE))
.finalize()
.check(block_height, &test_params())
.expect_err("Expected erroneous transaction");
let err = TransactionBuilder::mint(
block_height,
rng.gen(),
rng.gen(),
output::contract::Contract {
input_index: 0,
balance_root: rng.gen(),
state_root: rng.gen(),
},
rng.gen(),
rng.gen(),
)
.finalize()
.check(block_height, &test_params())
.expect_err("Expected erroneous transaction");

assert_eq!(
err,
CheckError::TransactionOutputCoinAssetIdDuplicated(AssetId::BASE)
);
assert_eq!(err, CheckError::TransactionMintNonBaseAsset);

let err = TransactionBuilder::mint(block_height, rng.gen())
.add_output(Output::coin(rng.gen(), rng.next_u64(), AssetId::BASE))
.add_output(Output::coin(rng.gen(), rng.next_u64(), AssetId::BASE))
.finalize()
.check(block_height.succ().unwrap(), &test_params())
.expect_err("Expected erroneous transaction");
let err = TransactionBuilder::mint(
block_height,
rng.gen(),
rng.gen(),
output::contract::Contract {
input_index: 0,
balance_root: rng.gen(),
state_root: rng.gen(),
},
rng.gen(),
rng.gen(),
)
.finalize()
.check(block_height.succ().unwrap(), &test_params())
.expect_err("Expected erroneous transaction");

assert_eq!(err, CheckError::TransactionMintIncorrectBlockHeight);
}
Expand Down
67 changes: 50 additions & 17 deletions fuel-tx/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use fuel_types::{
AssetId,
BlockHeight,
Bytes32,
ChainId,
Nonce,
Salt,
Word,
Expand Down Expand Up @@ -55,22 +56,22 @@ pub use validity::{
FormatValidityChecks,
};

use crate::TxPointer;

use crate::input::coin::{
CoinPredicate,
CoinSigned,
};
use input::*;

use crate::input::{
contract::Contract,
message::{
MessageCoinPredicate,
MessageDataPredicate,
use crate::{
input::{
coin::{
CoinPredicate,
CoinSigned,
},
contract::Contract,
message::{
MessageCoinPredicate,
MessageDataPredicate,
},
},
TxPointer,
};
pub use fuel_types::ChainId;
use input::*;
use output::*;

#[cfg(feature = "alloc")]
pub use id::Signable;
Expand Down Expand Up @@ -172,12 +173,17 @@ impl Transaction {

pub fn mint(
tx_pointer: TxPointer,
// TODO: Use directly `Output::Coin` here.
outputs: Vec<Output>,
input_contract: input::contract::Contract,
output_contract: output::contract::Contract,
mint_amount: Word,
mint_asset_id: AssetId,
) -> Mint {
Mint {
tx_pointer,
outputs,
input_contract,
output_contract,
mint_amount,
mint_asset_id,
metadata: None,
}
}
Expand Down Expand Up @@ -532,12 +538,15 @@ impl Deserialize for Transaction {
/// can be used to write generic code based on the different combinations of the fields.
pub mod field {
use crate::{
input,
output,
Input,
Output,
StorageSlot,
Witness,
};
use fuel_types::{
AssetId,
BlockHeight,
Bytes32,
Word,
Expand Down Expand Up @@ -589,6 +598,30 @@ pub mod field {
fn tx_pointer_static() -> usize;
}

pub trait InputContract {
fn input_contract(&self) -> &input::contract::Contract;
fn input_contract_mut(&mut self) -> &mut input::contract::Contract;
fn input_contract_offset(&self) -> usize;
}

pub trait OutputContract {
fn output_contract(&self) -> &output::contract::Contract;
fn output_contract_mut(&mut self) -> &mut output::contract::Contract;
fn output_contract_offset(&self) -> usize;
}

pub trait MintAmount {
fn mint_amount(&self) -> &Word;
fn mint_amount_mut(&mut self) -> &mut Word;
fn mint_amount_offset(&self) -> usize;
}

pub trait MintAssetId {
fn mint_asset_id(&self) -> &AssetId;
fn mint_asset_id_mut(&mut self) -> &mut AssetId;
fn mint_asset_id_offset(&self) -> usize;
}

pub trait ReceiptsRoot {
fn receipts_root(&self) -> &Bytes32;
fn receipts_root_mut(&mut self) -> &mut Bytes32;
Expand Down
Loading

0 comments on commit a228e1c

Please sign in to comment.