Skip to content

Commit

Permalink
Aptos gas payer (aptos-labs#8773)
Browse files Browse the repository at this point in the history
* initial commit

* Support gas payer

* Remove relics

* Fix

* Make verification know about gas payer bit

* update comments

* Remove merge conflict

* Give MSB a good name

* Reformat

* improve

* add comment

* Fix spec
  • Loading branch information
gerben-stavenga authored and xbtmatt committed Jul 25, 2023
1 parent 5529d81 commit 28b2212
Show file tree
Hide file tree
Showing 17 changed files with 786 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub enum FeatureFlag {
StorageSlotMetadata,
ChargeInvariantViolation,
DelegationPoolPartialGovernanceVoting,
GasPayerEnabled,
}

fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
Expand Down Expand Up @@ -160,6 +161,7 @@ impl From<FeatureFlag> for AptosFeatureFlag {
FeatureFlag::DelegationPoolPartialGovernanceVoting => {
AptosFeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING
},
FeatureFlag::GasPayerEnabled => AptosFeatureFlag::GAS_PAYER_ENABLED,
}
}
}
Expand Down Expand Up @@ -203,6 +205,7 @@ impl From<AptosFeatureFlag> for FeatureFlag {
AptosFeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING => {
FeatureFlag::DelegationPoolPartialGovernanceVoting
},
AptosFeatureFlag::GAS_PAYER_ENABLED => FeatureFlag::GasPayerEnabled,
}
}
}
Expand Down
21 changes: 15 additions & 6 deletions aptos-move/aptos-vm/src/aptos_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
adapter_common::{
discard_error_output, discard_error_vm_status, PreprocessedTransaction, VMAdapter,
},
aptos_vm_impl::{get_transaction_output, AptosVMImpl, AptosVMInternals},
aptos_vm_impl::{get_transaction_output, AptosVMImpl, AptosVMInternals, GAS_PAYER_FLAG_BIT},
block_executor::BlockAptosVM,
counters::*,
data_cache::StorageAdapter,
Expand Down Expand Up @@ -394,6 +394,18 @@ impl AptosVM {
)?)
}

fn get_senders(txn_data: &TransactionMetadata) -> Vec<AccountAddress> {
let mut res = vec![txn_data.sender];
res.extend(txn_data.secondary_signers());
if txn_data.sequence_number & GAS_PAYER_FLAG_BIT != 0 {
// In a gas payer tx, the last multi-agent signer of the secondary signers is in
// fact the gas payer and not to be part of the tx parameters. So we remove the last
// signer.
res.pop();
}
res
}

fn execute_script_or_entry_function(
&self,
resolver: &impl MoveResolverExt,
Expand All @@ -419,8 +431,7 @@ impl AptosVM {

match payload {
TransactionPayload::Script(script) => {
let mut senders = vec![txn_data.sender()];
senders.extend(txn_data.secondary_signers());
let senders = Self::get_senders(txn_data);
let loaded_func =
session.load_script(script.code(), script.ty_args().to_vec())?;
let args =
Expand All @@ -441,9 +452,7 @@ impl AptosVM {
)?;
},
TransactionPayload::EntryFunction(script_fn) => {
let mut senders = vec![txn_data.sender()];

senders.extend(txn_data.secondary_signers());
let senders = Self::get_senders(txn_data);
self.validate_and_execute_entry_function(
&mut session,
gas_meter,
Expand Down
96 changes: 58 additions & 38 deletions aptos-move/aptos-vm/src/aptos_vm_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ use aptos_types::{
use aptos_vm_logging::{log_schema::AdapterLogSchema, prelude::*};
use aptos_vm_types::output::VMOutput;
use fail::fail_point;
use move_binary_format::{errors::VMResult, CompiledModule};
use move_binary_format::{
errors::{Location, PartialVMError, VMResult},
CompiledModule,
};
use move_core_types::{
gas_algebra::NumArgs,
language_storage::ModuleId,
Expand All @@ -43,6 +46,7 @@ use move_vm_types::gas::UnmeteredGasMeter;
use std::sync::Arc;

pub const MAXIMUM_APPROVED_TRANSACTION_SIZE: u64 = 1024 * 1024;
pub const GAS_PAYER_FLAG_BIT: u64 = 1u64 << 63; // MSB of sequence number is used to flag a gas payer tx

/// A wrapper to make VMRuntime standalone
pub struct AptosVMImpl {
Expand Down Expand Up @@ -502,28 +506,21 @@ impl AptosVMImpl {
.or_else(|err| convert_prologue_error(transaction_validation, err, log_context))
}

/// Run the epilogue of a transaction by calling into `EPILOGUE_NAME` function stored
/// in the `ACCOUNT_MODULE` on chain.
pub(crate) fn run_success_epilogue(
fn run_epiloque(
&self,
session: &mut SessionExt,
gas_remaining: Gas,
txn_data: &TransactionMetadata,
log_context: &AdapterLogSchema,
) -> Result<(), VMStatus> {
fail_point!("move_adapter::run_success_epilogue", |_| {
Err(VMStatus::error(
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
None,
))
});

let transaction_validation = self.transaction_validation();
transaction_validation: &TransactionValidation,
) -> VMResult<()> {
let txn_sequence_number = txn_data.sequence_number();
let txn_gas_price = txn_data.gas_unit_price();
let txn_max_gas_units = txn_data.max_gas_amount();
session
.execute_function_bypass_visibility(
// We can unconditionally do this as this condition can only be true if the prologue
// accepted it, in which case the gas payer feature is enabled.
if txn_sequence_number & GAS_PAYER_FLAG_BIT == 0 {
// Regular tx, run the normal epilogue
session.execute_function_bypass_visibility(
&transaction_validation.module_id(),
&transaction_validation.user_epilogue_name,
// TODO: Deprecate this once we remove gas currency on the Move side.
Expand All @@ -537,8 +534,50 @@ impl AptosVMImpl {
]),
&mut UnmeteredGasMeter,
)
.map(|_return_vals| ())
.map_err(expect_no_verification_errors)
} else {
// Gas payer tx
let gas_payer = *txn_data.secondary_signers.last().ok_or_else(|| {
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
.finish(Location::Undefined)
})?;
session.execute_function_bypass_visibility(
&transaction_validation.module_id(),
&transaction_validation.user_epilogue_gas_payer_name,
// TODO: Deprecate this once we remove gas currency on the Move side.
vec![],
serialize_values(&vec![
MoveValue::Signer(txn_data.sender),
MoveValue::Address(gas_payer),
MoveValue::U64(txn_sequence_number),
MoveValue::U64(txn_gas_price.into()),
MoveValue::U64(txn_max_gas_units.into()),
MoveValue::U64(gas_remaining.into()),
]),
&mut UnmeteredGasMeter,
)
}
.map(|_return_vals| ())
.map_err(expect_no_verification_errors)
}

/// Run the epilogue of a transaction by calling into `EPILOGUE_NAME` function stored
/// in the `ACCOUNT_MODULE` on chain.
pub(crate) fn run_success_epilogue(
&self,
session: &mut SessionExt,
gas_remaining: Gas,
txn_data: &TransactionMetadata,
log_context: &AdapterLogSchema,
) -> Result<(), VMStatus> {
fail_point!("move_adapter::run_success_epilogue", |_| {
Err(VMStatus::error(
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
None,
))
});

let transaction_validation = self.transaction_validation();
self.run_epiloque(session, gas_remaining, txn_data, transaction_validation)
.or_else(|err| convert_epilogue_error(transaction_validation, err, log_context))
}

Expand All @@ -552,26 +591,7 @@ impl AptosVMImpl {
log_context: &AdapterLogSchema,
) -> Result<(), VMStatus> {
let transaction_validation = self.transaction_validation();
let txn_sequence_number = txn_data.sequence_number();
let txn_gas_price = txn_data.gas_unit_price();
let txn_max_gas_units = txn_data.max_gas_amount();
session
.execute_function_bypass_visibility(
&transaction_validation.module_id(),
&transaction_validation.user_epilogue_name,
// TODO: Deprecate this once we remove gas currency on the Move side.
vec![],
serialize_values(&vec![
MoveValue::Signer(txn_data.sender),
MoveValue::U64(txn_sequence_number),
MoveValue::U64(txn_gas_price.into()),
MoveValue::U64(txn_max_gas_units.into()),
MoveValue::U64(gas_remaining.into()),
]),
&mut UnmeteredGasMeter,
)
.map(|_return_vals| ())
.map_err(expect_no_verification_errors)
self.run_epiloque(session, gas_remaining, txn_data, transaction_validation)
.or_else(|e| {
expect_only_successful_execution(
e,
Expand Down
6 changes: 6 additions & 0 deletions aptos-move/aptos-vm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub const EBAD_CHAIN_ID: u64 = 1007;
pub const ESEQUENCE_NUMBER_TOO_BIG: u64 = 1008;
// Counts of secondary keys and addresses don't match.
pub const ESECONDARY_KEYS_ADDRESSES_COUNT_MISMATCH: u64 = 1009;
// Gas payer account missing in gas payer tx
pub const EGAS_PAYER_ACCOUNT_MISSING: u64 = 1010;

// Specified account is not a multisig account.
const EACCOUNT_NOT_MULTISIG: u64 = 2002;
// Account executing this operation is not an owner of the multisig account.
Expand Down Expand Up @@ -116,6 +119,9 @@ pub fn convert_prologue_error(
(INVALID_ARGUMENT, ESECONDARY_KEYS_ADDRESSES_COUNT_MISMATCH) => {
StatusCode::SECONDARY_KEYS_ADDRESSES_COUNT_MISMATCH
},
(INVALID_ARGUMENT, EGAS_PAYER_ACCOUNT_MISSING) => {
StatusCode::GAS_PAYER_ACCOUNT_MISSING
},
(category, reason) => {
let err_msg = format!("[aptos_vm] Unexpected prologue Move abort: {:?}::{:?} (Category: {:?} Reason: {:?})",
location, code, category, reason);
Expand Down
Loading

0 comments on commit 28b2212

Please sign in to comment.