Skip to content

Commit cffcd6d

Browse files
committed
refactor extcodecopy, fix tests?
1 parent 3184d8b commit cffcd6d

File tree

3 files changed

+66
-30
lines changed

3 files changed

+66
-30
lines changed

crates/handler/src/validation.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -437,21 +437,21 @@ mod tests {
437437

438438
#[test]
439439
fn test_eip7907_code_size_limit_success() {
440-
// EIP-7907: MAX_CODE_SIZE = 0x40000, but EIP-7825 txn gas limit cap is 30_000_000 so only ~0x0BFFF (49151 bytes)
441-
// size contract can be used. Use the simplest method to return a contract code size equal to 0x0BFFF (49151 bytes)
442-
// PUSH3 0x0BFFF - return size
440+
// EIP-7907: MAX_CODE_SIZE = 0xc000, but EIP-7825 txn gas limit cap is 30_000_000 so only ~0x0c000 (49152 bytes)
441+
// size contract can be used. Use the simplest method to return a contract code size equal to 0x0c000 (49152 bytes)
442+
// PUSH3 0x0c000 - return size
443443
// PUSH1 0x00 - memory position 0
444444
// RETURN - return uninitialized memory, will be filled with 0
445445
let init_code: Vec<u8> = vec![
446-
0x62, 0x00, 0xBF, 0xFF, // PUSH3 0x0BFFF
446+
0x62, 0x00, 0xc0, 0x00, // PUSH3 0x0c000
447447
0x60, 0x00, // PUSH1 0
448448
0xf3, // RETURN
449449
];
450450
let bytecode: Bytes = init_code.into();
451451
let r = deploy_contract(bytecode, Some(SpecId::OSAKA));
452452
assert!(matches!(r, Ok(ExecutionResult::Success { .. },)), "{r:?}");
453453
let gas_used = r.unwrap().gas_used();
454-
assert!(gas_used > 29_000_000 && gas_used < 30_000_000);
454+
assert_eq!(gas_used, 9892700);
455455
}
456456

457457
/// EIP-7825: Transaction Gas Limit Cap in Osaka (at 30M gas) will effectively limit the code size
@@ -717,7 +717,7 @@ mod tests {
717717
// PUSH1 0x00 - the memory position
718718
// MSTORE - store a non-zero value at the beginning of memory
719719

720-
// PUSH3 0x18001 - the return size (exceeds 0x18000)
720+
// PUSH3 0x18000 - the return size (exceeds 0x18000)
721721
// PUSH1 0x00 - the memory offset
722722
// PUSH1 0x00 - the amount of ETH sent
723723
// CREATE - create contract instruction (create contract from current memory)
@@ -734,7 +734,7 @@ mod tests {
734734
0x60, 0x00, // PUSH1 0x00
735735
0x52, // MSTORE
736736
// 2. prepare to create a large contract
737-
0x62, 0x01, 0x80, 0x01, // PUSH3 0x18001 (exceeds 0x18000)
737+
0x62, 0x01, 0x80, 0x00, // PUSH3 0x18000
738738
0x60, 0x00, // PUSH1 0x00 (the memory offset)
739739
0x60, 0x00, // PUSH1 0x00 (the amount of ETH sent)
740740
0xf0, // CREATE
@@ -816,7 +816,7 @@ mod tests {
816816
0x60, 0x00, // PUSH1 0x00
817817
0x52, // MSTORE
818818
// 2. prepare to create a contract
819-
0x62, 0x04, 0x00, 0x00, // PUSH3 0x40000
819+
0x62, 0x01, 0x80, 0x00, // PUSH3 0x18000
820820
0x60, 0x00, // PUSH1 0x00 (the memory offset)
821821
0x60, 0x00, // PUSH1 0x00 (the amount of ETH sent)
822822
0xf0, // CREATE

crates/interpreter/src/gas/calc.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,47 @@ pub const fn copy_cost_verylow(len: usize) -> Option<u64> {
112112
copy_cost(VERYLOW, len)
113113
}
114114

115-
/// `EXTCODECOPY` opcode cost calculation.
115+
/// `EXTCODECOPY` opcode cost calculation, berlin specific.
116116
#[inline]
117-
pub const fn extcodecopy_cost(
117+
pub const fn berlin_extcodecopy_cost<T>(
118+
spec_id: SpecId,
119+
state_load: &StateCodeLoad<T>,
120+
copy_len: usize,
121+
) -> Option<u64> {
122+
extcodecopy_inner_cost(spec_id, state_load.is_cold, None, copy_len)
123+
}
124+
125+
/// `EXTCODECOPY` opcode cost calculation, osaka specific.
126+
#[inline]
127+
pub const fn osaka_extcodecopy_cost(
118128
spec_id: SpecId,
119129
state_load: &StateCodeLoad<usize>,
120130
copy_len: usize,
131+
) -> Option<u64> {
132+
extcodecopy_inner_cost(spec_id, state_load.is_cold, Some(state_load.data), copy_len)
133+
}
134+
135+
/// `EXTCODECOPY` opcode cost calculation, common for berlin and osaka.
136+
#[inline]
137+
pub const fn extcodecopy_inner_cost(
138+
spec_id: SpecId,
139+
is_cold: bool,
140+
is_code_cold_and_size: Option<usize>,
141+
copy_len: usize,
121142
) -> Option<u64> {
122143
let mut base_gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
123-
warm_cold_cost(state_load.is_cold)
144+
warm_cold_cost(is_cold)
124145
} else if spec_id.is_enabled_in(SpecId::TANGERINE) {
125146
700
126147
} else {
127148
20
128149
};
129150

130-
if spec_id.is_enabled_in(SpecId::OSAKA) && state_load.is_code_cold {
131-
// extra cost for large code size.
132-
base_gas += large_contract_code_size_cost(state_load.data);
151+
if spec_id.is_enabled_in(SpecId::OSAKA) {
152+
if let Some(code_size) = is_code_cold_and_size {
153+
// extra cost for large code size.
154+
base_gas += large_contract_code_size_cost(code_size);
155+
}
133156
}
134157
copy_cost(base_gas, copy_len)
135158
}

crates/interpreter/src/instructions/host.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,12 @@ pub fn selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(
6161
///
6262
/// Gets the size of an account's code.
6363
pub fn extcodesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
64-
context: InstructionContext<'_, H, WIRE>,
64+
mut context: InstructionContext<'_, H, WIRE>,
6565
) {
6666
popn_top!([], top, context.interpreter);
6767
let address = top.into_address();
6868
let Some(code_size) = context.host.load_account_code_size(address) else {
69-
context
70-
.interpreter
71-
.halt(InstructionResult::FatalExternalError);
69+
context.fatal_halt();
7270
return;
7371
};
7472

@@ -128,21 +126,36 @@ pub fn extcodecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
128126
context.interpreter
129127
);
130128
let address = address.into_address();
129+
let len = as_usize_or_fail!(context.interpreter, len_u256);
131130

132-
// load code size and check if it is cold.
133-
let Some(code_size) = context.host.load_account_code_size(address) else {
134-
return context.fatal_halt();
135-
};
131+
let spec_id = context.interpreter.runtime_flag.spec_id();
132+
let code = if spec_id.is_enabled_in(OSAKA) {
133+
// load code size and check if it is cold.
134+
let Some(code_size) = context.host.load_account_code_size(address) else {
135+
return context.fatal_halt();
136+
};
136137

137-
let len = as_usize_or_fail!(context.interpreter, len_u256);
138-
gas_or_fail!(
139-
context.interpreter,
140-
gas::extcodecopy_cost(context.interpreter.runtime_flag.spec_id(), &code_size, len)
141-
);
138+
gas_or_fail!(
139+
context.interpreter,
140+
gas::osaka_extcodecopy_cost(spec_id, &code_size, len)
141+
);
142+
143+
// load code and make it warm.
144+
let Some(code) = context.host.load_account_code(address) else {
145+
return context.fatal_halt();
146+
};
147+
code.data
148+
} else {
149+
// load code.
150+
let Some(code) = context.host.load_account_code(address) else {
151+
return context.fatal_halt();
152+
};
142153

143-
// load code and make it warm.
144-
let Some(code) = context.host.load_account_code(address) else {
145-
return context.fatal_halt();
154+
gas_or_fail!(
155+
context.interpreter,
156+
gas::berlin_extcodecopy_cost(spec_id, &code, len)
157+
);
158+
code.data
146159
};
147160

148161
// for len == 0 memory expansion is not done.

0 commit comments

Comments
 (0)