Skip to content

Commit

Permalink
feat: custom opcodes touching MNAs
Browse files Browse the repository at this point in the history
 - feat: adapting the "self balance" opcode for MNAs;
 - feat: make Interpreter aware of MNAs.
  • Loading branch information
IaroslavMazur committed Apr 17, 2024
1 parent f048973 commit 1575f43
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 49 deletions.
6 changes: 3 additions & 3 deletions crates/interpreter/src/instructions/bitwise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ mod tests {
#[test]
fn test_shift_left() {
let mut host = DummyHost::new(Env::default());
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false);
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false, Vec::new());

struct TestCase {
value: U256,
Expand Down Expand Up @@ -216,7 +216,7 @@ mod tests {
#[test]
fn test_logical_shift_right() {
let mut host = DummyHost::new(Env::default());
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false);
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false, Vec::new());

struct TestCase {
value: U256,
Expand Down Expand Up @@ -297,7 +297,7 @@ mod tests {
#[test]
fn test_arithmetic_shift_right() {
let mut host = DummyHost::new(Env::default());
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false);
let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false, Vec::new());

struct TestCase {
value: U256,
Expand Down
123 changes: 112 additions & 11 deletions crates/interpreter/src/instructions/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
SStoreResult, Transfer, MAX_INITCODE_SIZE,
};
use core::cmp::min;
use revm_primitives::{Asset, BLOCK_HASH_HISTORY};
use revm_primitives::{Asset, BASE_ASSET_ID, BLOCK_HASH_HISTORY};

/// EIP-1884: Repricing for trie-size-dependent opcodes
pub fn selfbalance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
Expand All @@ -21,10 +21,33 @@ pub fn selfbalance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter,
return;
};

// TODO: take MNAs into account here
push!(interpreter, balance);
}

/// EIP-1884: Repricing for trie-size-dependent opcodes
pub fn self_mna_balances<H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
) {
check!(interpreter, ISTANBUL);
gas!(interpreter, gas::LOW);

for asset_id in interpreter.asset_ids.iter() {
// Get the balance of the contract for the asset_id
let Some((balance, _)) = host.balance(*asset_id, interpreter.contract.address) else {
interpreter.instruction_result = InstructionResult::FatalExternalError;
return;
};

// Push balance and asset_id to the stack
push!(interpreter, balance);
push_b256!(interpreter, *asset_id);
}

// Push the number of assets to the stack
push!(interpreter, U256::from(interpreter.asset_ids.len() as u64));
}

pub fn extcodesize<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
pop_address!(interpreter, address);
let Some((code, is_cold)) = host.code(address) else {
Expand Down Expand Up @@ -224,15 +247,24 @@ fn pop_transferred_assets(interpreter: &mut Interpreter, transferred_assets: &mu
}
}

fn get_transferred_assets(interpreter: &mut Interpreter) -> Vec<Asset> {
let mut transferred_assets = Vec::new();
pop_transferred_assets(interpreter, &mut transferred_assets);
transferred_assets
pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
) {
create_inner::<IS_CREATE2, H, SPEC>(interpreter, host, false);
}

pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
pub fn mna_create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
) {
create_inner::<IS_CREATE2, H, SPEC>(interpreter, host, true);
}

fn create_inner<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
is_mna_create: bool,
) {
// Dev: deploying smart contracts is not allowed for general public
// TODO: implement a way to allow deploying smart contracts by Sablier
Expand All @@ -246,7 +278,18 @@ pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
}

let mut transferred_assets = Vec::<Asset>::new();
pop_transferred_assets(interpreter, transferred_assets.as_mut());

if is_mna_create {
pop_transferred_assets(interpreter, transferred_assets.as_mut());
} else {
pop!(interpreter, value);
if value != U256::ZERO {
transferred_assets.push(Asset {
id: BASE_ASSET_ID,
amount: value,
});
}
}

pop!(interpreter, code_offset, len);
let len = as_usize_or_fail!(interpreter, len);
Expand Down Expand Up @@ -307,12 +350,37 @@ pub fn create<const IS_CREATE2: bool, H: Host + ?Sized, SPEC: Spec>(
}

pub fn call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
call_inner::<H, SPEC>(interpreter, host, false);
}

pub fn mna_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
call_inner::<H, SPEC>(interpreter, host, true);
}

fn call_inner<H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
is_mna_call: bool,
) {
pop!(interpreter, local_gas_limit);
pop_address!(interpreter, to);
// max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);

let transferred_assets = get_transferred_assets(interpreter);
let mut transferred_assets = Vec::<Asset>::new();

if is_mna_call {
pop_transferred_assets(interpreter, transferred_assets.as_mut());
} else {
pop!(interpreter, value);
if value != U256::ZERO {
transferred_assets.push(Asset {
id: BASE_ASSET_ID,
amount: value,
});
}
}

if interpreter.is_static && !transferred_assets.is_empty() {
interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic;
return;
Expand Down Expand Up @@ -367,12 +435,37 @@ pub fn call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &
}

pub fn call_code<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
call_code_inner::<H, SPEC>(interpreter, host, false);
}

pub fn mna_call_code<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
call_code_inner::<H, SPEC>(interpreter, host, true);
}

fn call_code_inner<H: Host + ?Sized, SPEC: Spec>(
interpreter: &mut Interpreter,
host: &mut H,
is_mna_call_code: bool,
) {
pop!(interpreter, local_gas_limit);
pop_address!(interpreter, to);
// max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);

let transferred_assets = get_transferred_assets(interpreter);
let mut transferred_assets = Vec::<Asset>::new();

if is_mna_call_code {
pop_transferred_assets(interpreter, transferred_assets.as_mut());
} else {
pop!(interpreter, value);
if value != U256::ZERO {
transferred_assets.push(Asset {
id: BASE_ASSET_ID,
amount: value,
});
}
}

let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else {
return;
};
Expand Down Expand Up @@ -457,7 +550,7 @@ pub fn delegate_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter
address: interpreter.contract.address,
caller: interpreter.contract.caller,
code_address: to,
apparent_assets: interpreter.contract.assets.clone(),
apparent_assets: interpreter.contract.call_assets.clone(),
scheme: CallScheme::DelegateCall,
},
is_static: interpreter.is_static,
Expand Down Expand Up @@ -513,6 +606,14 @@ pub fn static_call<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter,
}

pub fn balance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
pop_address!(interpreter, address);
push_b256!(interpreter, BASE_ASSET_ID);
push_b256!(interpreter, address.into_word());

mna_balance::<H, SPEC>(interpreter, host);
}

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

Expand Down
44 changes: 23 additions & 21 deletions crates/interpreter/src/instructions/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,18 +216,18 @@ opcodes! {
// 0x2B
// 0x2C
// 0x2D
// 0x2E
// 0x2F
0x30 => ADDRESS => system::address,
0x31 => BALANCE => host::balance::<H, SPEC>,
0x32 => ORIGIN => host_env::origin,
0x33 => CALLER => system::caller,
0x34 => CALLVALUES => system::callvalues,
0x35 => CALLDATALOAD => system::calldataload,
0x36 => CALLDATASIZE => system::calldatasize,
0x37 => CALLDATACOPY => system::calldatacopy,
0x38 => CODESIZE => system::codesize,
0x39 => CODECOPY => system::codecopy,
0x2E => MNABALANCE => host::mna_balance::<H, SPEC>,
0x2F => MNACALLVALUES => system::mna_callvalues,
0x30 => ADDRESS => system::address,
0x31 => BALANCE => host::balance::<H, SPEC>,
0x32 => ORIGIN => host_env::origin,
0x33 => CALLER => system::caller,
0x34 => CALLVALUE => system::callvalue,
0x35 => CALLDATALOAD => system::calldataload,
0x36 => CALLDATASIZE => system::calldatasize,
0x37 => CALLDATACOPY => system::calldatacopy,
0x38 => CODESIZE => system::codesize,
0x39 => CODECOPY => system::codecopy,

0x3A => GASPRICE => host_env::gasprice,
0x3B => EXTCODESIZE => host::extcodesize::<H, SPEC>,
Expand Down Expand Up @@ -413,15 +413,15 @@ opcodes! {
// 0xEB
// 0xEC
// 0xED
// 0xEE
// 0xEF
0xEE => MNACALL => host::mna_call::<H, SPEC>,
0xEF => MNACALLCODE => host::mna_call_code::<H, SPEC>,
0xF0 => CREATE => host::create::<false, H, SPEC>,
0xF1 => CALL => host::call::<H, SPEC>,
0xF2 => CALLCODE => host::call_code::<H, SPEC>,
0xF3 => RETURN => control::ret,
0xF4 => DELEGATECALL => host::delegate_call::<H, SPEC>,
0xF5 => CREATE2 => host::create::<true, H, SPEC>,
// 0xF6
0xF6 => MNACREATE => host::mna_create::<true, H, SPEC>,
// 0xF7
// 0xF8
// 0xF9
Expand Down Expand Up @@ -511,7 +511,9 @@ impl OpCode {
| OpCode::CALLDATACOPY
| OpCode::RETURNDATACOPY
| OpCode::CALL
| OpCode::MNACALL
| OpCode::CALLCODE
| OpCode::MNACALLCODE
| OpCode::DELEGATECALL
| OpCode::STATICCALL
)
Expand Down Expand Up @@ -652,13 +654,13 @@ const fn opcode_gas_info(opcode: u8, spec: SpecId) -> OpInfo {
0x2B => OpInfo::none(),
0x2C => OpInfo::none(),
0x2D => OpInfo::none(),
0x2E => OpInfo::none(),
0x2F => OpInfo::none(),
MNABALANCE => OpInfo::dynamic_gas(),
MNACALLVALUES => OpInfo::gas(gas::BASE),
ADDRESS => OpInfo::gas(gas::BASE),
BALANCE => OpInfo::dynamic_gas(),
ORIGIN => OpInfo::gas(gas::BASE),
CALLER => OpInfo::gas(gas::BASE),
CALLVALUES => OpInfo::gas(gas::BASE),
CALLVALUE => OpInfo::gas(gas::BASE),
CALLDATALOAD => OpInfo::gas(gas::VERYLOW),
CALLDATASIZE => OpInfo::gas(gas::BASE),
CALLDATACOPY => OpInfo::dynamic_gas(),
Expand Down Expand Up @@ -905,15 +907,15 @@ const fn opcode_gas_info(opcode: u8, spec: SpecId) -> OpInfo {
0xEB => OpInfo::none(),
0xEC => OpInfo::none(),
0xED => OpInfo::none(),
0xEE => OpInfo::none(),
0xEF => OpInfo::none(),
MNACALL => OpInfo::gas_block_end(0),
MNACALLCODE => OpInfo::gas_block_end(0),
CREATE => OpInfo::gas_block_end(0),
CALL => OpInfo::gas_block_end(0),
CALLCODE => OpInfo::gas_block_end(0),
RETURN => OpInfo::gas_block_end(0),
DELEGATECALL => OpInfo::gas_block_end(0),
CREATE2 => OpInfo::gas_block_end(0),
0xF6 => OpInfo::none(),
MNACREATE => OpInfo::gas_block_end(0),
0xF7 => OpInfo::none(),
0xF8 => OpInfo::none(),
0xF9 => OpInfo::none(),
Expand Down
24 changes: 20 additions & 4 deletions crates/interpreter/src/instructions/system.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
gas,
primitives::{Spec, B256, KECCAK_EMPTY, U256},
primitives::{Spec, B256, BASE_ASSET_ID, KECCAK_EMPTY, U256},
Host, InstructionResult, Interpreter,
};
use core::ptr;
Expand Down Expand Up @@ -82,16 +82,32 @@ pub fn calldatasize<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut
push!(interpreter, U256::from(interpreter.contract.input.len()));
}

pub fn callvalues<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
pub fn callvalue<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
gas!(interpreter, gas::BASE);

let base_asset_amount = interpreter
.contract
.call_assets
.iter()
.find(|asset| asset.id == BASE_ASSET_ID)
.map(|asset| asset.amount)
.unwrap_or(U256::ZERO);
push!(interpreter, base_asset_amount);
}

pub fn mna_callvalues<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
// TODO: make the gas cost proportional to the MNAs number
gas!(interpreter, gas::BASE);

for asset in &interpreter.contract.assets {
for asset in &interpreter.contract.call_assets {
push!(interpreter, asset.amount);
push!(interpreter, asset.id.into());
}

push!(interpreter, U256::from(interpreter.contract.assets.len()));
push!(
interpreter,
U256::from(interpreter.contract.call_assets.len())
);
}

pub fn calldatacopy<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
Expand Down
Loading

0 comments on commit 1575f43

Please sign in to comment.