Skip to content

Commit

Permalink
feat: multiple native assets (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulRBerg committed Sep 27, 2023
1 parent 8206193 commit 4131eba
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 199 deletions.
10 changes: 9 additions & 1 deletion bins/revme/src/statetest/merkle_trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,15 @@ pub fn state_merkle_trie_root<'a>(
pub fn trie_account_rlp(acc: &PlainAccount) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&acc.info.nonce);
stream.append(&acc.info.balance);
// TODO: double check that this is RLP compliant
stream.append(&{
sec_trie_root::<KeccakHasher, _, _, _>(
acc.info
.balances
.iter()
.map(|(&k, v)| (H256::from(k), rlp::encode(v))),
)
});
stream.append(&{
sec_trie_root::<KeccakHasher, _, _, _>(
acc.storage
Expand Down
4 changes: 2 additions & 2 deletions bins/revme/src/statetest/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bytes::Bytes;
use revm::primitives::{HashMap, B160, B256, U256};
use revm::primitives::{Balances, HashMap, B160, B256, U256};
use serde::Deserialize;
use std::collections::BTreeMap;

Expand Down Expand Up @@ -58,7 +58,7 @@ pub struct TxPartIndices {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AccountInfo {
pub balance: U256,
pub balances: Balances,
#[serde(deserialize_with = "deserialize_str_as_bytes")]
pub code: Bytes,
#[serde(deserialize_with = "deserialize_str_as_u64")]
Expand Down
2 changes: 1 addition & 1 deletion bins/revme/src/statetest/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ pub fn execute_test_suite(
let mut cache_state = revm::CacheState::new(false);
for (address, info) in unit.pre {
let acc_info = revm::primitives::AccountInfo {
balance: info.balance,
balances: info.balances,
code_hash: keccak256(&info.code),
code: Some(Bytecode::new_raw(info.code)),
nonce: info.nonce,
Expand Down
9 changes: 8 additions & 1 deletion crates/interpreter/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub trait Host {
fn load_account(&mut self, address: B160) -> Option<(bool, bool)>;
/// Get environmental block hash.
fn block_hash(&mut self, number: U256) -> Option<B256>;
/// Get balance of address and if account is cold loaded.
/// Get base asset balance of address and if account is cold loaded.
fn balance(&mut self, address: B160) -> Option<(U256, bool)>;
/// Get code of address and if account is cold loaded.
fn code(&mut self, address: B160) -> Option<(Bytecode, bool)>;
Expand Down Expand Up @@ -54,4 +54,11 @@ pub trait Host {
) -> (InstructionResult, Option<B160>, Gas, Bytes);
/// Invoke a call operation.
fn call(&mut self, input: &mut CallInputs) -> (InstructionResult, Gas, Bytes);

/// Get asset balance of address and if account is cold loaded.
fn balanceof(&mut self, asset_id: B256, address: B160) -> Option<(U256, bool)>;
/// Mint a native asset.
fn mint(&mut self, address: B160, value: U256) -> (InstructionResult, Gas);
/// Burn a native asset.
fn burn(&mut self, address: B160, value: U256) -> (InstructionResult, Gas);
}
15 changes: 15 additions & 0 deletions crates/interpreter/src/host/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,19 @@ impl Host for DummyHost {
fn call(&mut self, _input: &mut CallInputs) -> (InstructionResult, Gas, Bytes) {
panic!("Call is not supported for this host")
}

#[inline]
fn balanceof(&mut self, _asset_id: B256, _address: B160) -> Option<(U256, bool)> {
Some((U256::ZERO, false))
}

#[inline]
fn mint(&mut self, _address: B160, _value: U256) -> (InstructionResult, Gas) {
panic!("Mint is not supported for this host")
}

#[inline]
fn burn(&mut self, _address: B160, _value: U256) -> (InstructionResult, Gas) {
panic!("Burn is not supported for this host")
}
}
41 changes: 25 additions & 16 deletions crates/interpreter/src/instructions/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,22 +216,9 @@ pub fn log<H: Host, const N: usize>(interpreter: &mut Interpreter, host: &mut H)
host.log(interpreter.contract.address, topics, data);
}

pub fn selfdestruct<H: Host, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
check_staticcall!(interpreter);
pop_address!(interpreter, target);

let Some(res) = host.selfdestruct(interpreter.contract.address, target) else {
interpreter.instruction_result = InstructionResult::FatalExternalError;
return;
};

// EIP-3529: Reduction in refunds
if !SPEC::enabled(LONDON) && !res.previously_destroyed {
refund!(interpreter, gas::SELFDESTRUCT)
}
gas!(interpreter, gas::selfdestruct_cost::<SPEC>(res));

interpreter.instruction_result = InstructionResult::SelfDestruct;
/// DEPRECATED: the SELFDESTRUCT opcode is not available in the SabVM
pub fn selfdestruct<H: Host, SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut H) {
interpreter.instruction_result = InstructionResult::NotActivated;
}

#[inline(never)]
Expand Down Expand Up @@ -565,3 +552,25 @@ pub fn call_inner<H: Host, SPEC: Spec>(
}
}
}

pub fn balanceof<H: Host, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
pop_address!(interpreter, address);
pop!(interpreter, asset_id);

let asset_id = B256::from(asset_id);

let Some((balance, is_cold)) = host.balanceof(asset_id, address) else {
interpreter.instruction_result = InstructionResult::FatalExternalError;
return;
};
gas!(
interpreter,
// EIP-1884: Repricing for trie-size-dependent opcodes
gas::account_access_gas::<SPEC>(is_cold)
);
push!(interpreter, balance);
}

pub fn mint<H: Host, SPEC: Spec>(_interpreter: &mut Interpreter, _host: &mut H) {}

pub fn burn<H: Host, SPEC: Spec>(_interpreter: &mut Interpreter, _host: &mut H) {}
8 changes: 4 additions & 4 deletions crates/interpreter/src/instructions/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,9 @@ opcodes! {
// 0xBD
// 0xBE
// 0xBF
// 0xC0
// 0xC1
// 0xC2
0xC0 => BALANCEOF => host::balanceof::<H, SPEC>,
// 0xC1 => MINT => (),
// 0xC2 => BURN => (),
// 0xC3
// 0xC4
// 0xC5
Expand Down Expand Up @@ -752,7 +752,7 @@ const fn opcode_gas_info(opcode: u8, spec: SpecId) -> OpInfo {
0xBD => OpInfo::none(),
0xBE => OpInfo::none(),
0xBF => OpInfo::none(),
0xC0 => OpInfo::none(),
BALANCEOF => OpInfo::dynamic_gas(),
0xC1 => OpInfo::none(),
0xC2 => OpInfo::none(),
0xC3 => OpInfo::none(),
Expand Down
5 changes: 4 additions & 1 deletion crates/primitives/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::B160;
use crate::{B160, B256};

/// Interpreter stack limit
pub const STACK_LIMIT: u64 = 1024;
Expand Down Expand Up @@ -37,3 +37,6 @@ pub const MIN_BLOB_GASPRICE: u64 = 1;
pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477;
/// First version of the blob.
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;

// ID of base asset
pub const BASE_ASSET_ID: B256 = B256::zero();
19 changes: 17 additions & 2 deletions crates/primitives/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ pub struct TxEnv {
///
/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
pub max_fee_per_blob_gas: Option<U256>,

/// A list of asset IDs and values to transfer in the transaction.
///
/// If this is set, `value` must be zero.
pub asset_values: Option<Vec<(B256, U256)>>,
}

impl TxEnv {
Expand Down Expand Up @@ -403,6 +408,7 @@ impl Default for TxEnv {
access_list: Vec::new(),
blob_hashes: Vec::new(),
max_fee_per_blob_gas: None,
asset_values: None,
}
}
}
Expand Down Expand Up @@ -545,6 +551,14 @@ impl Env {
}
}

if self.tx.asset_values.is_some() {
// If `asset_values` is set, check that `value` is zero.
if self.tx.value != U256::ZERO {
return Err(InvalidTransaction::BaseValueNotZero);
}
// TODO: check that asset IDs are unique
}

Ok(())
}

Expand Down Expand Up @@ -586,10 +600,11 @@ impl Env {

// Check if account has enough balance for gas_limit*gas_price and value transfer.
// Transfer will be done inside `*_inner` functions.
if !self.cfg.is_balance_check_disabled() && balance_check > account.info.balance {
let base_asset_balance = account.info.get_base_balance();
if !self.cfg.is_balance_check_disabled() && balance_check > base_asset_balance {
return Err(InvalidTransaction::LackOfFundForMaxFee {
fee: self.tx.gas_limit,
balance: account.info.balance,
balance: base_asset_balance,
});
}

Expand Down
4 changes: 4 additions & 0 deletions crates/primitives/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ pub enum InvalidTransaction {
TooManyBlobs,
/// Blob transaction contains a versioned hash with an incorrect version
BlobVersionNotSupported,
/// The base `value` has to be zero for asset transactions
BaseValueNotZero,
/// Asset IDs in transaction are not unique
AssetIdsNotUnique,
}

impl<DBError> From<InvalidHeader> for EVMError<DBError> {
Expand Down
Loading

0 comments on commit 4131eba

Please sign in to comment.