Skip to content

Commit d3595d9

Browse files
authored
Merge pull request #303 from moonbeam-foundation/tarekkma-up/EIP-6780
Cancun support (v0.x)
2 parents 879ffe2 + 680418e commit d3595d9

File tree

15 files changed

+275
-12
lines changed

15 files changed

+275
-12
lines changed

core/src/eval/misc.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::Control;
22
use crate::{ExitError, ExitFatal, ExitRevert, ExitSucceed, Machine};
3-
use core::cmp::min;
3+
use core::cmp::{max, min};
44
use primitive_types::{H256, U256};
55

66
#[inline]
@@ -92,6 +92,23 @@ pub fn mload(state: &mut Machine) -> Control {
9292
Control::Continue(1)
9393
}
9494

95+
/// Support for EIP-5656: MCOPY instruction.
96+
#[inline]
97+
pub fn mcopy(state: &mut Machine) -> Control {
98+
pop_u256!(state, dst, src, len);
99+
try_or_fail!(state.memory.resize_offset(max(dst, src), len));
100+
101+
if len.is_zero() {
102+
return Control::Continue(1);
103+
}
104+
105+
let dst = as_usize_or_fail!(dst);
106+
let src = as_usize_or_fail!(src);
107+
let len = as_usize_or_fail!(len);
108+
state.memory.copy(dst, src, len);
109+
Control::Continue(1)
110+
}
111+
95112
#[inline]
96113
pub fn mstore(state: &mut Machine) -> Control {
97114
pop_u256!(state, index);

core/src/eval/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ fn eval_jumpdest(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Con
176176
Control::Continue(1)
177177
}
178178

179+
fn eval_mcopy(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control {
180+
self::misc::mcopy(state)
181+
}
182+
179183
fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control {
180184
self::misc::push(state, 0, position)
181185
}
@@ -497,6 +501,7 @@ pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control {
497501
table[Opcode::PC.as_usize()] = eval_pc as _;
498502
table[Opcode::MSIZE.as_usize()] = eval_msize as _;
499503
table[Opcode::JUMPDEST.as_usize()] = eval_jumpdest as _;
504+
table[Opcode::MCOPY.as_usize()] = eval_mcopy as _;
500505

501506
table[Opcode::PUSH0.as_usize()] = eval_push0 as _;
502507
table[Opcode::PUSH1.as_usize()] = eval_push1 as _;

core/src/memory.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{ExitError, ExitFatal};
22
use alloc::vec::Vec;
3-
use core::cmp::min;
3+
use core::cmp::{max, min};
44
use core::ops::{BitAnd, Not};
55
use primitive_types::U256;
66

@@ -181,6 +181,15 @@ impl Memory {
181181

182182
self.set(memory_offset, data, Some(ulen))
183183
}
184+
185+
/// Copies part of the memory inside another part of itself.
186+
pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
187+
let resize_offset = max(dst, src);
188+
if self.data.len() < resize_offset + len {
189+
self.data.resize(resize_offset + len, 0);
190+
}
191+
self.data.copy_within(src..src + len, dst);
192+
}
184193
}
185194

186195
/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
@@ -192,7 +201,7 @@ fn next_multiple_of_32(x: U256) -> Option<U256> {
192201

193202
#[cfg(test)]
194203
mod tests {
195-
use super::{next_multiple_of_32, U256};
204+
use super::{next_multiple_of_32, Memory, U256};
196205

197206
#[test]
198207
fn test_next_multiple_of_32() {
@@ -225,4 +234,49 @@ mod tests {
225234
}
226235
}
227236
}
237+
238+
#[test]
239+
fn test_memory_copy_works() {
240+
// Create a new instance of memory
241+
let mut memory = Memory::new(100usize);
242+
243+
// Set the [0,0,0,1,2,3,4] array as memory data.
244+
//
245+
// We insert the [1,2,3,4] array on index 3,
246+
// that's why we have the zero padding at the beginning.
247+
memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap();
248+
assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());
249+
250+
// Copy 1 byte into index 0.
251+
// As the length is 1, we only copy the byte present on index 3.
252+
memory.copy(0usize, 3usize, 1usize);
253+
254+
// Now the new memory data results in [1,0,0,1,2,3,4]
255+
assert_eq!(memory.data(), &[1u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());
256+
}
257+
258+
#[test]
259+
fn test_memory_copy_resize() {
260+
// Create a new instance of memory
261+
let mut memory = Memory::new(100usize);
262+
263+
// Set the [0,0,0,1,2,3,4] array as memory data.
264+
//
265+
// We insert the [1,2,3,4] array on index 3,
266+
// that's why we have the zero padding at the beginning.
267+
memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap();
268+
assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());
269+
270+
// Copy 2 bytes into index 3.
271+
// As the length is 2, we copy the bytes present on indexes 6 and 7,
272+
// which are [4,0].
273+
memory.copy(3usize, 6usize, 2usize);
274+
275+
// Now the new memory data results in [0, 0, 0, 4, 0, 3, 4, 0].
276+
// An extra element is added due to rezising.
277+
assert_eq!(
278+
memory.data(),
279+
&[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec()
280+
);
281+
}
228282
}

core/src/opcode.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ impl Opcode {
9393
pub const MSIZE: Opcode = Opcode(0x59);
9494
/// `JUMPDEST`
9595
pub const JUMPDEST: Opcode = Opcode(0x5b);
96+
/// `MCOPY`
97+
pub const MCOPY: Opcode = Opcode(0x5e);
9698

9799
/// `PUSHn`
98100
pub const PUSH0: Opcode = Opcode(0x5f);
@@ -225,6 +227,10 @@ impl Opcode {
225227
pub const SSTORE: Opcode = Opcode(0x55);
226228
/// `GAS`
227229
pub const GAS: Opcode = Opcode(0x5a);
230+
/// `TLOAD`
231+
pub const TLOAD: Opcode = Opcode(0x5c);
232+
/// `TSTORE`
233+
pub const TSTORE: Opcode = Opcode(0x5d);
228234
/// `LOGn`
229235
pub const LOG0: Opcode = Opcode(0xa0);
230236
pub const LOG1: Opcode = Opcode(0xa1);

gasometer/src/costs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ pub fn sstore_cost(
245245
)
246246
}
247247

248+
pub fn tload_cost(config: &Config) -> Result<u64, ExitError> {
249+
Ok(config.gas_storage_read_warm)
250+
}
251+
252+
pub fn tstore_cost(config: &Config) -> Result<u64, ExitError> {
253+
Ok(config.gas_storage_read_warm)
254+
}
255+
248256
pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
249257
let eip161 = !config.empty_considered_exists;
250258
let should_charge_topup = if eip161 {

gasometer/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
586586
len: U256::from_big_endian(&stack.peek(3)?[..]),
587587
}
588588
}
589-
Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
589+
Opcode::CALLDATACOPY | Opcode::CODECOPY | Opcode::MCOPY => GasCost::VeryLowCopy {
590590
len: U256::from_big_endian(&stack.peek(2)?[..]),
591591
},
592592
Opcode::EXP => GasCost::Exp {
@@ -599,6 +599,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
599599
target_is_cold: handler.is_cold(address, Some(index))?,
600600
}
601601
}
602+
Opcode::TLOAD => GasCost::TLoad,
602603

603604
Opcode::DELEGATECALL if config.has_delegate_call => {
604605
let target = stack.peek(1)?.into();
@@ -632,6 +633,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
632633
target_is_cold: handler.is_cold(address, Some(index))?,
633634
}
634635
}
636+
Opcode::TSTORE if !is_static => GasCost::TStore,
635637
Opcode::LOG0 if !is_static => GasCost::Log {
636638
n: 0,
637639
len: U256::from_big_endian(&stack.peek(1)?[..]),
@@ -704,6 +706,16 @@ pub fn dynamic_opcode_cost<H: Handler>(
704706
len: U256::from_big_endian(&stack.peek(1)?[..]),
705707
}),
706708

709+
Opcode::MCOPY => {
710+
let top0 = U256::from_big_endian(&stack.peek(0)?[..]);
711+
let top1 = U256::from_big_endian(&stack.peek(1)?[..]);
712+
let offset = top0.max(top1);
713+
Some(MemoryCost {
714+
offset,
715+
len: U256::from_big_endian(&stack.peek(2)?[..]),
716+
})
717+
}
718+
707719
Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost {
708720
offset: U256::from_big_endian(&stack.peek(0)?[..]),
709721
len: U256::from_big_endian(&stack.peek(2)?[..]),
@@ -867,6 +879,9 @@ impl<'config> Inner<'config> {
867879
target_is_cold,
868880
} => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,
869881

882+
GasCost::TLoad => costs::tload_cost(self.config)?,
883+
GasCost::TStore => costs::tstore_cost(self.config)?,
884+
870885
GasCost::Sha3 { len } => costs::sha3_cost(len)?,
871886
GasCost::Log { n, len } => costs::log_cost(n, len)?,
872887
GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
@@ -1053,6 +1068,10 @@ pub enum GasCost {
10531068
/// True if target has not been previously accessed in this transaction
10541069
target_is_cold: bool,
10551070
},
1071+
/// Gas cost for `TLOAD`.
1072+
TLoad,
1073+
/// Gas cost for `TSTORE`.
1074+
TStore,
10561075
}
10571076

10581077
/// Storage opcode will access. Used for tracking accessed storage (EIP-2929).

runtime/src/eval/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub fn eval<H: Handler>(state: &mut Runtime, opcode: Opcode, handler: &mut H) ->
4545
Opcode::SLOAD => system::sload(state, handler),
4646
Opcode::SSTORE => system::sstore(state, handler),
4747
Opcode::GAS => system::gas(state, handler),
48+
Opcode::TLOAD => system::tload(state, handler),
49+
Opcode::TSTORE => system::tstore(state, handler),
4850
Opcode::LOG0 => system::log(state, 0, handler),
4951
Opcode::LOG1 => system::log(state, 1, handler),
5052
Opcode::LOG2 => system::log(state, 2, handler),

runtime/src/eval/system.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,21 @@ pub fn gas<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
252252
Control::Continue
253253
}
254254

255+
pub fn tload<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
256+
pop!(runtime, index);
257+
let value = handler.transient_storage(runtime.context.address, index);
258+
push!(runtime, value);
259+
260+
Control::Continue
261+
}
262+
263+
pub fn tstore<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
264+
pop!(runtime, index, value);
265+
handler.set_transient_storage(runtime.context.address, index, value);
266+
267+
Control::Continue
268+
}
269+
255270
pub fn log<H: Handler>(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control<H> {
256271
pop_u256!(runtime, offset, len);
257272

runtime/src/handler.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ pub trait Handler {
3535
fn code(&self, address: H160) -> Vec<u8>;
3636
/// Get storage value of address at index.
3737
fn storage(&self, address: H160, index: H256) -> H256;
38+
/// Get transient storage value of address at index.
39+
fn transient_storage(&self, address: H160, index: H256) -> H256;
40+
3841
/// Get original storage value of address at index.
3942
fn original_storage(&self, address: H160, index: H256) -> H256;
4043

@@ -77,6 +80,13 @@ pub trait Handler {
7780

7881
/// Set storage value of address at index.
7982
fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>;
83+
/// Set transient storage value of address at index, transient storage gets discarded after every transaction. (see EIP-1153)
84+
fn set_transient_storage(
85+
&mut self,
86+
address: H160,
87+
index: H256,
88+
value: H256,
89+
);
8090
/// Create a log owned by address with given topics and data.
8191
fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError>;
8292
/// Mark an address to be deleted, with funds transferred to target.

runtime/src/lib.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ pub struct Config {
288288
pub has_push0: bool,
289289
/// Whether the gasometer is running in estimate mode.
290290
pub estimate: bool,
291+
/// Has EIP-6780. See [EIP-6780](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6780.md)
292+
pub has_eip_6780: bool,
291293
}
292294

293295
impl Config {
@@ -342,6 +344,7 @@ impl Config {
342344
has_base_fee: false,
343345
has_push0: false,
344346
estimate: false,
347+
has_eip_6780: false,
345348
}
346349
}
347350

@@ -396,6 +399,7 @@ impl Config {
396399
has_base_fee: false,
397400
has_push0: false,
398401
estimate: false,
402+
has_eip_6780: false,
399403
}
400404
}
401405

@@ -419,6 +423,11 @@ impl Config {
419423
Self::config_with_derived_values(DerivedConfigInputs::shanghai())
420424
}
421425

426+
/// Cancun hard fork configuration.
427+
pub const fn cancun() -> Config {
428+
Self::config_with_derived_values(DerivedConfigInputs::cancun())
429+
}
430+
422431
const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config {
423432
let DerivedConfigInputs {
424433
gas_storage_read_warm,
@@ -430,6 +439,7 @@ impl Config {
430439
disallow_executable_format,
431440
warm_coinbase_address,
432441
max_initcode_size,
442+
has_eip_6780,
433443
} = inputs;
434444

435445
// See https://eips.ethereum.org/EIPS/eip-2929
@@ -493,6 +503,7 @@ impl Config {
493503
has_base_fee,
494504
has_push0,
495505
estimate: false,
506+
has_eip_6780,
496507
}
497508
}
498509
}
@@ -509,6 +520,7 @@ struct DerivedConfigInputs {
509520
disallow_executable_format: bool,
510521
warm_coinbase_address: bool,
511522
max_initcode_size: Option<usize>,
523+
has_eip_6780: bool,
512524
}
513525

514526
impl DerivedConfigInputs {
@@ -523,6 +535,7 @@ impl DerivedConfigInputs {
523535
disallow_executable_format: false,
524536
warm_coinbase_address: false,
525537
max_initcode_size: None,
538+
has_eip_6780: false,
526539
}
527540
}
528541

@@ -537,6 +550,7 @@ impl DerivedConfigInputs {
537550
disallow_executable_format: true,
538551
warm_coinbase_address: false,
539552
max_initcode_size: None,
553+
has_eip_6780: false,
540554
}
541555
}
542556

@@ -551,6 +565,7 @@ impl DerivedConfigInputs {
551565
disallow_executable_format: true,
552566
warm_coinbase_address: false,
553567
max_initcode_size: None,
568+
has_eip_6780: false,
554569
}
555570
}
556571

@@ -566,6 +581,23 @@ impl DerivedConfigInputs {
566581
warm_coinbase_address: true,
567582
// 2 * 24576 as per EIP-3860
568583
max_initcode_size: Some(0xC000),
584+
has_eip_6780: false,
585+
}
586+
}
587+
588+
const fn cancun() -> Self {
589+
Self {
590+
gas_storage_read_warm: 100,
591+
gas_sload_cold: 2100,
592+
gas_access_list_storage_key: 1900,
593+
decrease_clears_refund: true,
594+
has_base_fee: true,
595+
has_push0: true,
596+
disallow_executable_format: true,
597+
warm_coinbase_address: true,
598+
// 2 * 24576 as per EIP-3860
599+
max_initcode_size: Some(0xC000),
600+
has_eip_6780: true,
569601
}
570602
}
571603
}

0 commit comments

Comments
 (0)