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(levm): opcodes revert invalid selfdestruct #946

Merged
merged 61 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
4f2c616
implementation of transaction report in execute and call
JereSalo Oct 22, 2024
e9f304f
pseudo-fix tests
JereSalo Oct 22, 2024
5f46de1
fix gas error
JereSalo Oct 22, 2024
30deafe
fix one more testt
JereSalo Oct 22, 2024
bce759d
fix one more panicking test
JereSalo Oct 22, 2024
1111e34
fix call test (chaning returndata)
JereSalo Oct 22, 2024
b0e2302
refactor of return data offset and size
JereSalo Oct 22, 2024
e40e726
refactor of return data of subcontext and fix some tests
JereSalo Oct 22, 2024
e31c4b5
fix nested call test
JereSalo Oct 22, 2024
d148400
adapt comparison with revm to new execute() interface
JereSalo Oct 22, 2024
69d63e6
change calldata for returndata in benchmarks
JereSalo Oct 22, 2024
d7358b8
change name of transaction report
JereSalo Oct 22, 2024
d1f2b33
fix clippy lint
JereSalo Oct 22, 2024
443f5f4
run cargo fmt
JereSalo Oct 22, 2024
50e89fb
add gas_refunded behavior in call (even though it is not implemented …
JereSalo Oct 22, 2024
5ff9b3f
change gas refunds logic
JereSalo Oct 22, 2024
807bc5f
run cargo fmt
JereSalo Oct 22, 2024
8091e47
change some comments
JereSalo Oct 22, 2024
a5d9b73
run cargo fmt, again
JereSalo Oct 22, 2024
49fc47b
uncomment opcodes
JereSalo Oct 23, 2024
3fae9f7
Revert "uncomment opcodes"
JereSalo Oct 23, 2024
2d50eed
uncomment opcodes
JereSalo Oct 23, 2024
fa95f81
merge branch main
JereSalo Oct 23, 2024
f624276
merge last changes
JereSalo Oct 23, 2024
2b1808b
add basic code for opcodes
JereSalo Oct 23, 2024
850dd42
change some comments
JereSalo Oct 23, 2024
0774ec2
make some changes in gas cost
JereSalo Oct 23, 2024
5d5f6a5
add test for invalid opcode
JereSalo Oct 23, 2024
3583b6d
add functionalities to revert
JereSalo Oct 23, 2024
9022e5d
fix gas consumption problem
JereSalo Oct 23, 2024
a54c3a8
change transact return type
JereSalo Oct 23, 2024
3b5a41e
add test for revert opcode
JereSalo Oct 23, 2024
7f7e43e
make little change in test
JereSalo Oct 23, 2024
1310116
move revert behavior to execute
JereSalo Oct 23, 2024
e23c962
add oneliner and run cargo fmt
JereSalo Oct 23, 2024
4b811c0
remove comment
JereSalo Oct 23, 2024
8b69f6d
add one more test for revert
JereSalo Oct 23, 2024
74ec5e5
add some comments
JereSalo Oct 23, 2024
57b7722
add little docs for Callframe
JereSalo Oct 23, 2024
aa2273c
add docs comments
JereSalo Oct 23, 2024
6037206
fix clippy lint and run cargo fmt
JereSalo Oct 23, 2024
365df7b
remove unused import
JereSalo Oct 23, 2024
16544d3
Merge branch 'levm/feat/transaction_report' into levm/feat/revert_inv…
JereSalo Oct 23, 2024
bf5b19c
merge main into branch
JereSalo Oct 24, 2024
e5d6e13
delete comment
JereSalo Oct 24, 2024
3b2ba4d
make changes to selfdestruct account management
JereSalo Oct 25, 2024
251292a
run cargo fmt
JereSalo Oct 26, 2024
3ab97ce
Merge branch 'main' into levm/feat/revert_invalid_selfdestruct
JereSalo Oct 26, 2024
8ed0b97
abstraction for restoring state
JereSalo Oct 29, 2024
cd40809
change popping in revert
JereSalo Oct 29, 2024
732f2e3
run cargo fmt
JereSalo Oct 29, 2024
6f6d60e
merge main and fix problems
JereSalo Nov 1, 2024
d50356a
merge main into branch
JereSalo Nov 1, 2024
16f5ce1
run cargo fmt
JereSalo Nov 1, 2024
a07b506
add accounts to cache in selfdestruct and fix possible issues
JereSalo Nov 1, 2024
b81d569
run cargo fmt
JereSalo Nov 1, 2024
5155c96
make some changes to call opcode
JereSalo Nov 2, 2024
f2efa72
run cargo fmt
JereSalo Nov 2, 2024
024f118
add comments clarifying state of revert
JereSalo Nov 4, 2024
796ceb1
Merge branch 'main' into levm/feat/revert_invalid_selfdestruct
JereSalo Nov 4, 2024
362ff24
fix issues with create opcode
JereSalo Nov 4, 2024
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
2 changes: 1 addition & 1 deletion crates/vm/levm/docs/substate.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## Substate

`accessed_addresses` and `accessed_storage_keys` follow the structure defined in [EIP 2929](https://eips.ethereum.org/EIPS/eip-2929#specification)
`accessed_addresses` and `accessed_storage_keys` belong to the Substate but in our VM implementation they are not there because we already know what the warm addresses and storage keys are by looking at the `Cache` structure.
3 changes: 3 additions & 0 deletions crates/vm/levm/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ pub mod gas_cost {
pub const CODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]);
pub const GASPRICE: U256 = U256([2, 0, 0, 0]);
pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]);
pub const SELFDESTRUCT_STATIC: U256 = U256([5000, 0, 0, 0]);
pub const SELFDESTRUCT_DYNAMIC: U256 = U256([25000, 0, 0, 0]);
pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]);
}

// Costs in gas for call opcodes (in wei)
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/levm/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Database for Db {
}
}

#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Cache {
pub accounts: HashMap<Address, Account>,
}
Expand Down
5 changes: 4 additions & 1 deletion crates/vm/levm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ pub enum VMError {
OverflowInArithmeticOp,
FatalError,
InvalidTransaction,
RevertOpcode,
InvalidOpcode,
MissingBlobHashes,
BlobHashIndexOutOfBounds,
RevertOpcode,
SenderAccountDoesNotExist,
AddressDoesNotMatchAnAccount,
SenderAccountShouldNotHaveBytecode,
SenderBalanceShouldContainTransferValue,
Expand All @@ -40,6 +42,7 @@ pub enum ResultReason {
Stop,
Revert,
Return,
SelfDestruct,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
104 changes: 101 additions & 3 deletions crates/vm/levm/src/opcode_handlers/system.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{
call_frame::CallFrame,
constants::{call_opcode, SUCCESS_FOR_RETURN},
constants::{call_opcode, gas_cost, SUCCESS_FOR_RETURN},
errors::{OpcodeSuccess, ResultReason, VMError},
vm::VM,
vm::{word_to_address, VM},
};
use ethereum_rust_core::{Address, U256};
use ethereum_rust_core::{types::TxKind, Address, U256};

// System Operations (10)
// Opcodes: CREATE, CALL, CALLCODE, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT
Expand Down Expand Up @@ -39,6 +39,10 @@ impl VM {
.try_into()
.unwrap_or(usize::MAX);

if current_call_frame.is_static && !value.is_zero() {
return Err(VMError::OpcodeNotAllowedInStaticContext);
}

let memory_byte_size = (args_offset + args_size).max(ret_offset + ret_size);
let memory_expansion_cost = current_call_frame.memory.expansion_cost(memory_byte_size)?;

Expand Down Expand Up @@ -253,4 +257,98 @@ impl VM {
current_call_frame,
)
}

// REVERT operation
pub fn op_revert(
&mut self,
current_call_frame: &mut CallFrame,
) -> Result<OpcodeSuccess, VMError> {
// Description: Gets values from stack, calculates gas cost and sets return data.
// Returns: VMError RevertOpcode if executed correctly.
// Notes:
// The actual reversion of changes is made in the execute() function.

let offset = current_call_frame.stack.pop()?.as_usize();

let size = current_call_frame.stack.pop()?.as_usize();

let gas_cost = current_call_frame.memory.expansion_cost(offset + size)?;

self.increase_consumed_gas(current_call_frame, gas_cost)?;

current_call_frame.returndata = current_call_frame.memory.load_range(offset, size).into();

Err(VMError::RevertOpcode)
}

/// ### INVALID operation
/// Reverts consuming all gas, no return data.
pub fn op_invalid(&mut self) -> Result<OpcodeSuccess, VMError> {
Err(VMError::InvalidOpcode)
}

// SELFDESTRUCT operation
pub fn op_selfdestruct(
&mut self,
current_call_frame: &mut CallFrame,
) -> Result<OpcodeSuccess, VMError> {
// Sends all ether in the account to the target address
// Steps:
// 1. Pop the target address from the stack
// 2. Get current account and: Store the balance in a variable, set it's balance to 0
// 3. Get the target account, checking if it is empty and if it is cold. Update gas cost accordingly.
// 4. Add the balance of the current account to the target account
// 5. Register account to be destroyed in accrued substate.

// Notes:
// If context is Static, return error.
// If executed in the same transaction a contract was created, the current account is registered to be destroyed
if current_call_frame.is_static {
return Err(VMError::OpcodeNotAllowedInStaticContext);
}

// Gas costs variables
let static_gas_cost = gas_cost::SELFDESTRUCT_STATIC;
let dynamic_gas_cost = gas_cost::SELFDESTRUCT_DYNAMIC;
let cold_gas_cost = gas_cost::COLD_ADDRESS_ACCESS_COST;
let mut gas_cost = static_gas_cost;

// 1. Pop the target address from the stack
let target_address = word_to_address(current_call_frame.stack.pop()?);

// 2. Get current account and: Store the balance in a variable, set it's balance to 0
let mut current_account = self.get_account(&current_call_frame.to);
let current_account_balance = current_account.info.balance;

current_account.info.balance = U256::zero();

// 3 & 4. Get target account and add the balance of the current account to it
// TODO: If address is cold, there is an additional cost of 2600.
if !self.cache.is_account_cached(&target_address) {
gas_cost += cold_gas_cost;
}

let mut target_account = self.get_account(&target_address);
if target_account.is_empty() {
gas_cost += dynamic_gas_cost;
}
target_account.info.balance += current_account_balance;

// 5. Register account to be destroyed in accrued substate IF executed in the same transaction a contract was created
if self.tx_kind == TxKind::Create {
self.accrued_substate
.selfdestrutct_set
.insert(current_call_frame.to);
}
// Accounts in SelfDestruct set should be destroyed at the end of the transaction.

// Update cache after modifying accounts.
self.cache
.add_account(&current_call_frame.to, &current_account);
self.cache.add_account(&target_address, &target_account);

self.increase_consumed_gas(current_call_frame, gas_cost)?;

Ok(OpcodeSuccess::Result(ResultReason::SelfDestruct))
}
}
9 changes: 6 additions & 3 deletions crates/vm/levm/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ pub enum Opcode {
DELEGATECALL = 0xF4,
CREATE2 = 0xF5,
STATICCALL = 0xFA,
// REVERT = 0xFD,
// INVALID = 0xFE,
// SELFDESTRUCT = 0xFF,
REVERT = 0xFD,
INVALID = 0xFE,
SELFDESTRUCT = 0xFF,
}

impl Copy for Opcode {}
Expand Down Expand Up @@ -321,6 +321,9 @@ impl From<u8> for Opcode {
0xF5 => Opcode::CREATE2,
0xF4 => Opcode::DELEGATECALL,
0xFA => Opcode::STATICCALL,
0xFD => Opcode::REVERT,
0xFE => Opcode::INVALID,
0xFF => Opcode::SELFDESTRUCT,
_ => panic!("Unknown opcode: 0x{:02X}", byte),
}
}
Expand Down
12 changes: 6 additions & 6 deletions crates/vm/levm/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ pub enum Operation {
DelegateCall,
Create2,
StaticCall,
// Revert,
// Invalid,
// SelfDestruct,
Revert,
Invalid,
SelfDestruct,
}

impl Operation {
Expand Down Expand Up @@ -201,9 +201,9 @@ impl Operation {
Operation::DelegateCall => Bytes::copy_from_slice(&[Opcode::DELEGATECALL as u8]),
Operation::Create2 => Bytes::copy_from_slice(&[Opcode::CREATE2 as u8]),
Operation::StaticCall => Bytes::copy_from_slice(&[Opcode::STATICCALL as u8]),
// Operation::Revert => Bytes::copy_from_slice(&[Opcode::REVERT as u8]),
// Operation::Invalid => Bytes::copy_from_slice(&[Opcode::INVALID as u8]),
// Operation::SelfDestruct => Bytes::copy_from_slice(&[Opcode::SELFDESTRUCT as u8]),
Operation::Revert => Bytes::copy_from_slice(&[Opcode::REVERT as u8]),
Operation::Invalid => Bytes::copy_from_slice(&[Opcode::INVALID as u8]),
Operation::SelfDestruct => Bytes::copy_from_slice(&[Opcode::SELFDESTRUCT as u8]),
}
}
}
Loading
Loading