-
Notifications
You must be signed in to change notification settings - Fork 88
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
Support transaction policies #623
Conversation
Updated transaction validity rules according to policies. Updated fee calculation and refund logic.
Fixed all tests.
pub fn free() -> Self { | ||
Self { | ||
base: 0, | ||
dep_per_unit: 0, | ||
dep_per_unit: u64::MAX, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will produce zero-price
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a comment explaining that would be nice. Although, I'm also considering using 0
to make them free anyway in #625
let max_gas = tx.max_gas(gas_costs, fee_params); | ||
if max_gas > tx_params.max_gas_per_tx { | ||
Err(CheckError::TransactionMaxGasExceeded)? | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The specification says GasLimit > MAX_GAS_PER_TX
, while in real life should limit the total transaction cost.
I've created a specification PR to actualize it.
But it is a huge breaking change because SDK right now uses MAX_GAS_PER_TX
as a GasLimit
-> the transaction always fails because of this check.
#[test_case(0, 0, 1 => Some(96))] | ||
#[test_case(88, 0, 1 => Some(184))] | ||
#[test_case(0, 1, 2 => Some(168))] | ||
#[test_case(0, 2, 3 => Some(240))] | ||
#[test_case(0, 1, 3 => Some(168))] | ||
#[test_case(44, 2, 3 => Some(284))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we removed two fields, we need to subtract 16
.
|
||
#[cfg(test)] | ||
#[allow(clippy::cast_possible_truncation)] | ||
mod tests { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is tested in the fuel_vm/src/checked_transaction.rs
# Conflicts: # .github/workflows/ci.yml # fuel-tx/src/transaction/fee.rs
fuel-tx/src/builder.rs
Outdated
@@ -129,11 +116,10 @@ pub struct TransactionBuilder<Tx> { | |||
impl TransactionBuilder<Script> { | |||
pub fn script(script: Vec<u8>, script_data: Vec<u8>) -> Self { | |||
let tx = Script { | |||
gas_price: Default::default(), | |||
gas_limit: Default::default(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also rename this to script_gas_limit to more closely match the spec?
fuel-tx/src/builder.rs
Outdated
@@ -313,8 +297,11 @@ impl<Tx: Buildable> TransactionBuilder<Tx> { | |||
self | |||
} | |||
|
|||
pub fn gas_limit(&mut self, gas_limit: Word) -> &mut Self { | |||
self.tx.set_gas_limit(gas_limit); | |||
pub fn gas_limit(&mut self, gas_limit: Word) -> &mut Self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub fn gas_limit(&mut self, gas_limit: Word) -> &mut Self | |
pub fn script_gas_limit(&mut self, gas_limit: Word) -> &mut Self |
} | ||
|
||
#[test] | ||
fn script_set_witness_limit_for_non_empty_witness_fails() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn script_set_witness_limit_for_non_empty_witness_fails() { | |
fn script_set_witness_limit_less_than_witness_data_size_fails() { |
} | ||
|
||
#[test] | ||
fn create_set_witness_limit_for_non_empty_witness_fails() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn create_set_witness_limit_for_non_empty_witness_fails() { | |
fn create_set_witness_limit_less_than_witness_data_size_fails() { |
fuel-tx/src/transaction.rs
Outdated
@@ -17,6 +39,7 @@ use itertools::Itertools; | |||
|
|||
mod fee; | |||
mod metadata; | |||
pub mod policies; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Should pub mods be sorted into a separate section from private mods?
15efde3
to
69c08ef
Compare
PolicyType::GasPrice => 0, | ||
PolicyType::WitnessLimit => 1, | ||
PolicyType::Maturity => 2, | ||
PolicyType::MaxFee => 3, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use the inverse operation of how you defined the PolicyType
to find the index:
PolicyType::GasPrice => 0, | |
PolicyType::WitnessLimit => 1, | |
PolicyType::Maturity => 2, | |
PolicyType::MaxFee => 3, | |
PolicyType::GasPrice => PoliciesBits::GasPrice.trailing_zeros(), | |
PolicyType::WitnessLimit => PoliciesBits::WitnessLimit.trailing_zeros(), | |
PolicyType::Maturity => PoliciesBits::Maturity.trailing_zeros(), | |
PolicyType::MaxFee => PoliciesBits::MaxFee.trailing_zeros(), |
or:
PolicyType::GasPrice => 0, | |
PolicyType::WitnessLimit => 1, | |
PolicyType::Maturity => 2, | |
PolicyType::MaxFee => 3, | |
self.bit().trailinig_zeroes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to write the implementation manually to control values because, in the future, we may remove GasPrice
or any other policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as they don't get out of sync 👍
fuel-tx/src/transaction/policies.rs
Outdated
/// The total number of policies. | ||
pub const POLICIES_NUMBER: usize = PoliciesBits::all().bits().count_ones() as usize; | ||
|
||
/// It is a container for managing policies. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// It is a container for managing policies. | |
/// Container for managing policies. |
fuel-vm/src/error.rs
Outdated
/// The transaction doesn't contain enough gas to evaluate all predicates | ||
#[display(fmt = "Insufficient gas available for all predicates")] | ||
CumulativePredicateGasExceededTxGasLimit, | ||
/// The transaction `max_gas` more than global gas limit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The transaction `max_gas` more than global gas limit. | |
/// The transaction's `max_gas` is greater than the global gas limit. |
or
/// The transaction `max_gas` more than global gas limit. | |
/// The transaction policy value for `MaxGas` is greater than the global gas limit. |
I proposed some changes in #630 - mostly for the purpose of allowing us to encapsulate policy definitions and checks better. It would be nice to have this logic centralized rather than spread out, because it means a reduction in overall complexity: Having one place to define this logic would mean better maintainability/less duplication and easier extensibility. But I am simply suggesting one approach - if you have an idea on how you would like to do this, we can review that too now or at a later date after this PR is in. |
# Conflicts: # CHANGELOG.md # Cargo.toml
Imports FuelLabs/fuel-vm#623 --------- Co-authored-by: Brandon Vrooman <[email protected]> Co-authored-by: Brandon Vrooman <[email protected]> Co-authored-by: hal3e <[email protected]>
Findings after implementation FuelLabs/fuel-vm#623
Imports FuelLabs/fuel-vm#623 --------- Co-authored-by: Brandon Vrooman <[email protected]> Co-authored-by: Brandon Vrooman <[email protected]> Co-authored-by: hal3e <[email protected]>
Closes #576 Closes FuelLabs/fuel-core#1398
Added support for transaction policies. The
Script
andCreate
transactions received a new field,
policies
. Policies allow the additionof some limits to the transaction to protect the user or specify some details regarding execution.
This change makes the
GasPrice
andMaturity
fields optional, allowing to save space in the future. Also, this will enable us to support multidimensional prices later.Along with this change, we introduced two new policies:
WitnessLimit
- allows the limitation of the maximum size of witnesses in bytes for the contract. Because of the changes in the gas calculation model(the blockchain also charges the user for the witness data), the user should protect himself from the block producer or third parties blowing up witness data and draining the user's funds.MaxFee
- allows the upper bound for the maximum fee that users agree to pay for the transaction.This change brings the following modification to the gas model:
GasLimit
only limits the script execution. Previously, theGasLimit
also limited the predicate execution time, but it is not valid anymore. So, it is not possible to use theGasLimit
for transaction cost limitations. A newMaxFee
policy is a way to do that. TheGasLimit
field was removed from theCreate
transaction because it only relates to the script execution(which theCreate
transaction doesn't have).min_gas
andmin_fee
calculation.WhitnessLimit
also impacts themax_gas
andmax_fee
calculation along with theGasLimit
(in the case ofCreate
transaction onlyWitnessLimit
affects themax_gas
andmax_fee
).The change has the following modification to the layout transaction:
Create
transaction doesn't have theGasLimit
field anymore. Because theCreate
transaction doesn't have any script to executeCreate
andScript
transactions don't have explicitmaturity
andgas_price
fields. Instead, these fields can be set via a newpolicies
field.Create
andScript
transactions have a newpolicies
field with a unique canonical serialization and deserialization for optimal space consumption.Other breaking changes caused by the change:
GasPrice
policy.GasLimit
should be less than theMAX_GAS_PER_TX
constant. After removing this field from theCreate
transaction, it is impossible to require it. Instead, it requires thatmax_gas <= MAX_GAS_PER_TX
for any transaction. Consequently, anyScript
transaction that usesMAX_GAS_PER_TX
as aGasLimit
will always fail because of a new rule. Setting the estimated gas usage instead solves the problem.max_fee > policies.max_fee
, then transaction will be rejected.witnessses_size > policies.witness_limit
, then transaction will be rejected.