Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Configurable Base Asset ID #573

Merged
merged 20 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

#### Breaking

- [#573](https://github.com/FuelLabs/fuel-vm/pull/573): Added `base_asset_id` as a required field to `FeeParameters`. `base_asset_id` is used to supply the ID of the base asset.
- [#554](https://github.com/FuelLabs/fuel-vm/pull/554): Removed `debug` feature from the `fuel-vm`. The debugger is always available and becomes active after calling any `set_*` method.
- [#537](https://github.com/FuelLabs/fuel-vm/pull/537): Use dependent cost for `k256`, `s256`, `mcpi`, `scwq`, `swwq` opcodes.
These opcodes charged inadequately low costs in comparison to the amount of work.
Expand Down
3 changes: 2 additions & 1 deletion fuel-tx/src/tests/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use fuel_types::{
Deserializable,
SerializableVec,
},
AssetId,
ChainId,
};
use rand::{
Expand Down Expand Up @@ -87,7 +88,7 @@ fn common_parts_create_and_script<Tx: Buildable>(
assert_eq!(owner, owner_p);
}

if let Some(asset_id) = i.asset_id() {
if let Some(asset_id) = i.asset_id(&AssetId::BASE) {
// Message doesn't store `AssetId` explicitly but works with base asset
if let Some(offset) = i.repr().asset_id_offset() {
cases.asset_id = true;
Expand Down
14 changes: 10 additions & 4 deletions fuel-tx/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,10 @@ impl Transaction {

pub trait Executable: field::Inputs + field::Outputs + field::Witnesses {
/// Returns the assets' ids used in the inputs in the order of inputs.
fn input_asset_ids(&self) -> IntoIter<&AssetId> {
fn input_asset_ids<'a>(
&'a self,
base_asset_id: &'a AssetId,
) -> IntoIter<&'a AssetId> {
self.inputs()
.iter()
.filter_map(|input| match input {
Expand All @@ -277,16 +280,19 @@ pub trait Executable: field::Inputs + field::Outputs + field::Witnesses {
Input::MessageCoinSigned(_)
| Input::MessageCoinPredicate(_)
| Input::MessageDataPredicate(_)
| Input::MessageDataSigned(_) => Some(&AssetId::BASE),
| Input::MessageDataSigned(_) => Some(base_asset_id),
_ => None,
})
.collect_vec()
.into_iter()
}

/// Returns unique assets' ids used in the inputs.
fn input_asset_ids_unique(&self) -> IntoIter<&AssetId> {
let asset_ids = self.input_asset_ids();
fn input_asset_ids_unique<'a>(
&'a self,
base_asset_id: &'a AssetId,
) -> IntoIter<&'a AssetId> {
let asset_ids = self.input_asset_ids(base_asset_id);

#[cfg(feature = "std")]
let asset_ids = asset_ids.unique();
Expand Down
3 changes: 3 additions & 0 deletions fuel-tx/src/transaction/consensus_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ impl ConsensusParameters {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct FeeParameters {
/// The base native asset of the Fuel protocol.
pub base_asset_id: AssetId,
/// Factor to convert between gas and transaction assets value.
pub gas_price_factor: u64,
/// A fixed ratio linking metered bytes to gas price
Expand All @@ -134,6 +136,7 @@ pub struct FeeParameters {
impl FeeParameters {
/// Default consensus parameters with settings suggested in fuel-specs
pub const DEFAULT: Self = Self {
base_asset_id: AssetId::zeroed(),
gas_price_factor: 1_000_000_000,
gas_per_byte: 4,
};
Expand Down
26 changes: 14 additions & 12 deletions fuel-tx/src/transaction/types/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use fuel_types::{
WORD_SIZE,
},
mem_layout,
AssetId,
BlockHeight,
Bytes32,
ContractId,
Expand Down Expand Up @@ -243,21 +242,23 @@ impl FormatValidityChecks for Create {
block_height: BlockHeight,
consensus_params: &ConsensusParameters,
) -> Result<(), CheckError> {
check_common_part(
self,
block_height,
consensus_params.tx_params(),
consensus_params.predicate_params(),
)?;
let ConsensusParameters {
tx_params,
predicate_params,
contract_params,
fee_params,
chain_id,
..
} = consensus_params;

check_common_part(self, block_height, tx_params, predicate_params, fee_params)?;

let bytecode_witness_len = self
.witnesses
.get(self.bytecode_witness_index as usize)
.map(|w| w.as_ref().len() as Word)
.ok_or(CheckError::TransactionCreateBytecodeWitnessIndex)?;

let contract_params = consensus_params.contract_params();

if bytecode_witness_len > contract_params.contract_max_size
|| bytecode_witness_len / 4 != self.bytecode_length
{
Expand Down Expand Up @@ -303,8 +304,7 @@ impl FormatValidityChecks for Create {
} else {
#[cfg(feature = "std")]
{
let metadata =
CreateMetadata::compute(self, &consensus_params.chain_id())?;
let metadata = CreateMetadata::compute(self, chain_id)?;
(metadata.state_root, metadata.contract_id)
}

Expand Down Expand Up @@ -333,7 +333,9 @@ impl FormatValidityChecks for Create {
Err(CheckError::TransactionCreateOutputVariable { index })
}

Output::Change { asset_id, .. } if asset_id != &AssetId::BASE => {
Output::Change { asset_id, .. }
if asset_id != &fee_params.base_asset_id =>
{
Err(CheckError::TransactionCreateOutputChangeNotBaseAsset { index })
}

Expand Down
7 changes: 5 additions & 2 deletions fuel-tx/src/transaction/types/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,14 +359,17 @@ impl Input {
}
}

pub const fn asset_id(&self) -> Option<&AssetId> {
pub const fn asset_id<'a>(
&'a self,
base_asset_id: &'a AssetId,
) -> Option<&'a AssetId> {
match self {
Input::CoinSigned(CoinSigned { asset_id, .. })
| Input::CoinPredicate(CoinPredicate { asset_id, .. }) => Some(asset_id),
Input::MessageCoinSigned(_)
| Input::MessageCoinPredicate(_)
| Input::MessageDataSigned(_)
| Input::MessageDataPredicate(_) => Some(&AssetId::BASE),
| Input::MessageDataPredicate(_) => Some(base_asset_id),
Input::Contract(_) => None,
}
}
Expand Down
1 change: 1 addition & 0 deletions fuel-tx/src/transaction/types/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ impl FormatValidityChecks for Script {
block_height,
consensus_params.tx_params(),
consensus_params.predicate_params(),
consensus_params.fee_params(),
)?;
let script_params = consensus_params.script_params();
if self.script.len() > script_params.max_script_length as usize {
Expand Down
61 changes: 32 additions & 29 deletions fuel-tx/src/transaction/validity.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
use crate::{
ConsensusParameters,
FeeParameters,
Input,
Output,
Transaction,
Witness,
};
use core::hash::Hash;

use fuel_types::{
AssetId,
BlockHeight,
};
use fuel_types::BlockHeight;

#[cfg(feature = "std")]
use fuel_types::{
Expand Down Expand Up @@ -307,6 +305,7 @@ pub(crate) fn check_common_part<T>(
block_height: BlockHeight,
tx_params: &TxParameters,
predicate_params: &PredicateParameters,
fee_params: &FeeParameters,
) -> Result<(), CheckError>
where
T: field::GasPrice
Expand Down Expand Up @@ -350,30 +349,34 @@ where
Err(CheckError::NoSpendableInput)?
}

tx.input_asset_ids_unique().try_for_each(|input_asset_id| {
// check for duplicate change outputs
if tx
.outputs()
.iter()
.filter_map(|output| match output {
Output::Change { asset_id, .. } if input_asset_id == asset_id => Some(()),
Output::Change { asset_id, .. }
if asset_id != &AssetId::default() && input_asset_id == asset_id =>
{
Some(())
}
_ => None,
})
.count()
> 1
{
return Err(CheckError::TransactionOutputChangeAssetIdDuplicated(
*input_asset_id,
))
}
tx.input_asset_ids_unique(&fee_params.base_asset_id)
.try_for_each(|input_asset_id| {
// check for duplicate change outputs
if tx
.outputs()
.iter()
.filter_map(|output| match output {
Output::Change { asset_id, .. } if input_asset_id == asset_id => {
Some(())
}
Output::Change { asset_id, .. }
if asset_id != &fee_params.base_asset_id
&& input_asset_id == asset_id =>
{
Some(())
}
_ => None,
})
.count()
> 1
{
return Err(CheckError::TransactionOutputChangeAssetIdDuplicated(
*input_asset_id,
))
}

Ok(())
})?;
Ok(())
})?;

// Check for duplicated input utxo id
let duplicated_utxo_id = tx
Expand Down Expand Up @@ -419,7 +422,7 @@ where

if let Output::Change { asset_id, .. } = output {
if !tx
.input_asset_ids()
.input_asset_ids(&fee_params.base_asset_id)
.any(|input_asset_id| input_asset_id == asset_id)
{
return Err(CheckError::TransactionOutputChangeAssetIdNotFound(
Expand All @@ -430,7 +433,7 @@ where

if let Output::Coin { asset_id, .. } = output {
if !tx
.input_asset_ids()
.input_asset_ids(&fee_params.base_asset_id)
.any(|input_asset_id| input_asset_id == asset_id)
{
return Err(CheckError::TransactionOutputCoinAssetIdNotFound(
Expand Down
1 change: 0 additions & 1 deletion fuel-types/src/array_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@ impl ContractId {
}

impl AssetId {
/// The base native asset of the Fuel protocol.
pub const BASE: AssetId = AssetId::zeroed();
}

Expand Down
7 changes: 5 additions & 2 deletions fuel-vm/src/checked_transaction/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ where
// Sum message coin inputs
Input::MessageCoinSigned(MessageCoinSigned { amount, .. })
| Input::MessageCoinPredicate(MessageCoinPredicate { amount, .. }) => {
*non_retryable_balances.entry(AssetId::BASE).or_default() += amount;
*non_retryable_balances
.entry(params.base_asset_id)
.or_default() += amount;
}
// Sum data messages
Input::MessageDataSigned(MessageDataSigned { amount, .. })
Expand All @@ -66,7 +68,8 @@ where
let fee = TransactionFee::checked_from_tx(params, transaction)
.ok_or(CheckError::ArithmeticOverflow)?;

let base_asset_balance = non_retryable_balances.entry(AssetId::BASE).or_default();
let base_asset_id = params.base_asset_id;
let base_asset_balance = non_retryable_balances.entry(base_asset_id).or_default();

*base_asset_balance = fee.checked_deduct_total(*base_asset_balance).ok_or(
CheckError::InsufficientFeeAmount {
Expand Down
14 changes: 10 additions & 4 deletions fuel-vm/src/checked_transaction/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,22 @@ impl core::ops::Deref for NonRetryableFreeBalances {
/// More information about it in the specification:
/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/protocol/tx-validity.md#sufficient-balance>
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct RetryableAmount(pub(crate) Word);
pub struct RetryableAmount {
pub(crate) amount: Word,
pub(crate) base_asset_id: AssetId,
}

impl From<RetryableAmount> for Word {
fn from(value: RetryableAmount) -> Self {
value.0
value.amount
}
}

impl core::ops::Deref for RetryableAmount {
type Target = Word;

fn deref(&self) -> &Self::Target {
&self.0
&self.amount
}
}

Expand Down Expand Up @@ -219,7 +222,10 @@ pub mod script {

let metadata = CheckedMetadata {
non_retryable_balances: NonRetryableFreeBalances(non_retryable_balances),
retryable_balance: RetryableAmount(retryable_balance),
retryable_balance: RetryableAmount {
amount: retryable_balance,
base_asset_id: consensus_params.fee_params.base_asset_id,
},
block_height,
fee,
gas_used_by_predicates: 0,
Expand Down
7 changes: 4 additions & 3 deletions fuel-vm/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ pub trait ExecutableTransaction:
where
I: for<'a> Index<&'a AssetId, Output = Word>,
{
let base_asset_id = &fee_params.base_asset_id;
let gas_refund =
TransactionFee::gas_refund_value(fee_params, remaining_gas, self.price())
.ok_or(CheckError::ArithmeticOverflow)?;
Expand All @@ -408,8 +409,8 @@ pub trait ExecutableTransaction:
// Note: the initial balance deducts the gas limit from base asset
Output::Change {
asset_id, amount, ..
} if revert && asset_id == &AssetId::BASE => initial_balances.non_retryable
[&AssetId::BASE]
} if revert && asset_id == base_asset_id => initial_balances.non_retryable
[base_asset_id]
.checked_add(gas_refund)
.map(|v| *amount = v)
.ok_or(CheckError::ArithmeticOverflow),
Expand All @@ -425,7 +426,7 @@ pub trait ExecutableTransaction:
// The change for the base asset will be the available balance + unused gas
Output::Change {
asset_id, amount, ..
} if asset_id == &AssetId::BASE => balances[asset_id]
} if asset_id == base_asset_id => balances[asset_id]
.checked_add(gas_refund)
.map(|v| *amount = v)
.ok_or(CheckError::ArithmeticOverflow),
Expand Down
4 changes: 2 additions & 2 deletions fuel-vm/src/interpreter/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ impl TryFrom<InitialBalances> for RuntimeBalances {
fn try_from(initial_balances: InitialBalances) -> Result<Self, CheckError> {
let mut balances: BTreeMap<_, _> = initial_balances.non_retryable.into();
if let Some(retryable_amount) = initial_balances.retryable {
let entry = balances.entry(AssetId::BASE).or_default();
let entry = balances.entry(retryable_amount.base_asset_id).or_default();
*entry = entry
.checked_add(*retryable_amount)
.checked_add(retryable_amount.amount)
.ok_or(CheckError::ArithmeticOverflow)?;
}
Self::try_from_iter(balances)
Expand Down
Loading