EIP-2315: Simple Subroutines for the EVM (#11629)
This commit is contained in:
parent
751210c963
commit
1460f6cc27
@ -321,6 +321,13 @@ enum_with_from_u8! {
|
|||||||
#[doc = "Makes a log entry, 4 topics."]
|
#[doc = "Makes a log entry, 4 topics."]
|
||||||
LOG4 = 0xa4,
|
LOG4 = 0xa4,
|
||||||
|
|
||||||
|
#[doc = "Marks the entry point to a subroutine."]
|
||||||
|
BEGINSUB = 0x5c,
|
||||||
|
#[doc = "Returns from a subroutine."]
|
||||||
|
RETURNSUB = 0x5d,
|
||||||
|
#[doc = "Jumps to a defined BEGINSUB subroutine."]
|
||||||
|
JUMPSUB = 0x5e,
|
||||||
|
|
||||||
#[doc = "create a new account with associated code"]
|
#[doc = "create a new account with associated code"]
|
||||||
CREATE = 0xf0,
|
CREATE = 0xf0,
|
||||||
#[doc = "message-call into an account"]
|
#[doc = "message-call into an account"]
|
||||||
@ -591,6 +598,9 @@ lazy_static! {
|
|||||||
arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special));
|
arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special));
|
||||||
arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special));
|
arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special));
|
||||||
arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special));
|
arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special));
|
||||||
|
arr[BEGINSUB as usize] = Some(InstructionInfo::new("BEGINSUB", 0, 0, GasPriceTier::Base));
|
||||||
|
arr[JUMPSUB as usize] = Some(InstructionInfo::new("JUMPSUB", 1, 0, GasPriceTier::High));
|
||||||
|
arr[RETURNSUB as usize] = Some(InstructionInfo::new("RETURNSUB", 0, 0, GasPriceTier::Low));
|
||||||
arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special));
|
arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special));
|
||||||
arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special));
|
arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special));
|
||||||
arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special));
|
arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special));
|
||||||
|
@ -61,6 +61,10 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0
|
|||||||
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||||
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
|
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
|
||||||
|
|
||||||
|
/// Maximum subroutine stack size as specified in
|
||||||
|
/// https://eips.ethereum.org/EIPS/eip-2315.
|
||||||
|
pub const MAX_SUB_STACK_SIZE: usize = 1023;
|
||||||
|
|
||||||
fn to_biguint(x: U256) -> BigUint {
|
fn to_biguint(x: U256) -> BigUint {
|
||||||
let mut bytes = [0u8; 32];
|
let mut bytes = [0u8; 32];
|
||||||
x.to_little_endian(&mut bytes);
|
x.to_little_endian(&mut bytes);
|
||||||
@ -101,6 +105,8 @@ enum InstructionResult<Gas> {
|
|||||||
Ok,
|
Ok,
|
||||||
UnusedGas(Gas),
|
UnusedGas(Gas),
|
||||||
JumpToPosition(U256),
|
JumpToPosition(U256),
|
||||||
|
JumpToSubroutine(U256),
|
||||||
|
ReturnFromSubroutine(usize),
|
||||||
StopExecutionNeedsReturn {
|
StopExecutionNeedsReturn {
|
||||||
/// Gas left.
|
/// Gas left.
|
||||||
gas: Gas,
|
gas: Gas,
|
||||||
@ -183,8 +189,10 @@ pub struct Interpreter<Cost: CostType> {
|
|||||||
do_trace: bool,
|
do_trace: bool,
|
||||||
done: bool,
|
done: bool,
|
||||||
valid_jump_destinations: Option<Arc<BitSet>>,
|
valid_jump_destinations: Option<Arc<BitSet>>,
|
||||||
|
valid_subroutine_destinations: Option<Arc<BitSet>>,
|
||||||
gasometer: Option<Gasometer<Cost>>,
|
gasometer: Option<Gasometer<Cost>>,
|
||||||
stack: VecStack<U256>,
|
stack: VecStack<U256>,
|
||||||
|
return_stack: Vec<usize>,
|
||||||
resume_output_range: Option<(U256, U256)>,
|
resume_output_range: Option<(U256, U256)>,
|
||||||
resume_result: Option<InstructionResult<Cost>>,
|
resume_result: Option<InstructionResult<Cost>>,
|
||||||
last_stack_ret_len: usize,
|
last_stack_ret_len: usize,
|
||||||
@ -290,10 +298,12 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
let params = InterpreterParams::from(params);
|
let params = InterpreterParams::from(params);
|
||||||
let informant = informant::EvmInformant::new(depth);
|
let informant = informant::EvmInformant::new(depth);
|
||||||
let valid_jump_destinations = None;
|
let valid_jump_destinations = None;
|
||||||
|
let valid_subroutine_destinations = None;
|
||||||
let gasometer = Cost::from_u256(params.gas)
|
let gasometer = Cost::from_u256(params.gas)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|gas| Gasometer::<Cost>::new(gas));
|
.map(|gas| Gasometer::<Cost>::new(gas));
|
||||||
let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero());
|
let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero());
|
||||||
|
let return_stack = Vec::with_capacity(MAX_SUB_STACK_SIZE);
|
||||||
|
|
||||||
Interpreter {
|
Interpreter {
|
||||||
cache,
|
cache,
|
||||||
@ -301,8 +311,10 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
reader,
|
reader,
|
||||||
informant,
|
informant,
|
||||||
valid_jump_destinations,
|
valid_jump_destinations,
|
||||||
|
valid_subroutine_destinations,
|
||||||
gasometer,
|
gasometer,
|
||||||
stack,
|
stack,
|
||||||
|
return_stack,
|
||||||
done: false,
|
done: false,
|
||||||
// Overridden in `step_inner` based on
|
// Overridden in `step_inner` based on
|
||||||
// the result of `ext.trace_next_instruction`.
|
// the result of `ext.trace_next_instruction`.
|
||||||
@ -478,7 +490,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
if self.valid_jump_destinations.is_none() {
|
if self.valid_jump_destinations.is_none() {
|
||||||
self.valid_jump_destinations = Some(
|
self.valid_jump_destinations = Some(
|
||||||
self.cache
|
self.cache
|
||||||
.jump_destinations(&self.params.code_hash, &self.reader.code),
|
.jump_and_sub_destinations(&self.params.code_hash, &self.reader.code)
|
||||||
|
.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let jump_destinations = self
|
let jump_destinations = self
|
||||||
@ -491,6 +504,28 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
};
|
};
|
||||||
self.reader.position = pos;
|
self.reader.position = pos;
|
||||||
}
|
}
|
||||||
|
InstructionResult::JumpToSubroutine(position) => {
|
||||||
|
if self.valid_subroutine_destinations.is_none() {
|
||||||
|
self.valid_subroutine_destinations = Some(
|
||||||
|
self.cache
|
||||||
|
.jump_and_sub_destinations(&self.params.code_hash, &self.reader.code)
|
||||||
|
.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let subroutine_destinations = self
|
||||||
|
.valid_subroutine_destinations
|
||||||
|
.as_ref()
|
||||||
|
.expect("subroutine_destinations are initialized on first jump; qed");
|
||||||
|
let pos = match self.verify_jump(position, subroutine_destinations) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return InterpreterResult::Done(Err(e)),
|
||||||
|
};
|
||||||
|
self.return_stack.push(self.reader.position);
|
||||||
|
self.reader.position = pos + 1;
|
||||||
|
}
|
||||||
|
InstructionResult::ReturnFromSubroutine(pos) => {
|
||||||
|
self.reader.position = pos;
|
||||||
|
}
|
||||||
InstructionResult::StopExecutionNeedsReturn {
|
InstructionResult::StopExecutionNeedsReturn {
|
||||||
gas,
|
gas,
|
||||||
init_off,
|
init_off,
|
||||||
@ -537,20 +572,20 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
) -> vm::Result<()> {
|
) -> vm::Result<()> {
|
||||||
let schedule = ext.schedule();
|
let schedule = ext.schedule();
|
||||||
|
|
||||||
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call)
|
use instructions::*;
|
||||||
|| (instruction == instructions::CREATE2 && !schedule.have_create2)
|
if (instruction == DELEGATECALL && !schedule.have_delegate_call)
|
||||||
|| (instruction == instructions::STATICCALL && !schedule.have_static_call)
|
|| (instruction == CREATE2 && !schedule.have_create2)
|
||||||
|| ((instruction == instructions::RETURNDATACOPY
|
|| (instruction == STATICCALL && !schedule.have_static_call)
|
||||||
|| instruction == instructions::RETURNDATASIZE)
|
|| ((instruction == RETURNDATACOPY || instruction == RETURNDATASIZE)
|
||||||
&& !schedule.have_return_data)
|
&& !schedule.have_return_data)
|
||||||
|| (instruction == instructions::REVERT && !schedule.have_revert)
|
|| (instruction == REVERT && !schedule.have_revert)
|
||||||
|| ((instruction == instructions::SHL
|
|| ((instruction == SHL || instruction == SHR || instruction == SAR)
|
||||||
|| instruction == instructions::SHR
|
|
||||||
|| instruction == instructions::SAR)
|
|
||||||
&& !schedule.have_bitwise_shifting)
|
&& !schedule.have_bitwise_shifting)
|
||||||
|| (instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash)
|
|| (instruction == EXTCODEHASH && !schedule.have_extcodehash)
|
||||||
|| (instruction == instructions::CHAINID && !schedule.have_chain_id)
|
|| (instruction == CHAINID && !schedule.have_chain_id)
|
||||||
|| (instruction == instructions::SELFBALANCE && !schedule.have_selfbalance)
|
|| (instruction == SELFBALANCE && !schedule.have_selfbalance)
|
||||||
|
|| ((instruction == BEGINSUB || instruction == JUMPSUB || instruction == RETURNSUB)
|
||||||
|
&& !schedule.have_subs)
|
||||||
{
|
{
|
||||||
return Err(vm::Error::BadInstruction {
|
return Err(vm::Error::BadInstruction {
|
||||||
instruction: instruction as u8,
|
instruction: instruction as u8,
|
||||||
@ -623,6 +658,29 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instructions::JUMPDEST => {
|
instructions::JUMPDEST => {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
instructions::BEGINSUB => {
|
||||||
|
return Err(vm::Error::InvalidSubEntry);
|
||||||
|
}
|
||||||
|
instructions::JUMPSUB => {
|
||||||
|
if self.return_stack.len() >= MAX_SUB_STACK_SIZE {
|
||||||
|
return Err(vm::Error::OutOfSubStack {
|
||||||
|
wanted: 1,
|
||||||
|
limit: MAX_SUB_STACK_SIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let sub_destination = self.stack.pop_back();
|
||||||
|
return Ok(InstructionResult::JumpToSubroutine(sub_destination));
|
||||||
|
}
|
||||||
|
instructions::RETURNSUB => {
|
||||||
|
if let Some(pos) = self.return_stack.pop() {
|
||||||
|
return Ok(InstructionResult::ReturnFromSubroutine(pos));
|
||||||
|
} else {
|
||||||
|
return Err(vm::Error::SubStackUnderflow {
|
||||||
|
wanted: 1,
|
||||||
|
on_stack: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
instructions::CREATE | instructions::CREATE2 => {
|
instructions::CREATE | instructions::CREATE2 => {
|
||||||
let endowment = self.stack.pop_back();
|
let endowment = self.stack.pop_back();
|
||||||
let init_off = self.stack.pop_back();
|
let init_off = self.stack.pop_back();
|
||||||
@ -1413,6 +1471,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
|
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
|
||||||
Ok(jump)
|
Ok(jump)
|
||||||
} else {
|
} else {
|
||||||
|
// Note: if jump > usize, BadJumpDestination value is trimmed
|
||||||
Err(vm::Error::BadJumpDestination { destination: jump })
|
Err(vm::Error::BadJumpDestination { destination: jump })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ use std::sync::Arc;
|
|||||||
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||||
|
|
||||||
// stub for a HeapSizeOf implementation.
|
// stub for a HeapSizeOf implementation.
|
||||||
|
#[derive(Clone)]
|
||||||
struct Bits(Arc<BitSet>);
|
struct Bits(Arc<BitSet>);
|
||||||
|
|
||||||
impl HeapSizeOf for Bits {
|
impl HeapSizeOf for Bits {
|
||||||
@ -35,9 +36,21 @@ impl HeapSizeOf for Bits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CacheItem {
|
||||||
|
jump_destination: Bits,
|
||||||
|
sub_entrypoint: Bits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeapSizeOf for CacheItem {
|
||||||
|
fn heap_size_of_children(&self) -> usize {
|
||||||
|
self.jump_destination.heap_size_of_children() + self.sub_entrypoint.heap_size_of_children()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Global cache for EVM interpreter
|
/// Global cache for EVM interpreter
|
||||||
pub struct SharedCache {
|
pub struct SharedCache {
|
||||||
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
|
jump_destinations: Mutex<MemoryLruCache<H256, CacheItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedCache {
|
impl SharedCache {
|
||||||
@ -50,47 +63,62 @@ impl SharedCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get jump destinations bitmap for a contract.
|
/// Get jump destinations bitmap for a contract.
|
||||||
pub fn jump_destinations(&self, code_hash: &Option<H256>, code: &[u8]) -> Arc<BitSet> {
|
pub fn jump_and_sub_destinations(
|
||||||
|
&self,
|
||||||
|
code_hash: &Option<H256>,
|
||||||
|
code: &[u8],
|
||||||
|
) -> (Arc<BitSet>, Arc<BitSet>) {
|
||||||
if let Some(ref code_hash) = code_hash {
|
if let Some(ref code_hash) = code_hash {
|
||||||
if code_hash == &KECCAK_EMPTY {
|
if code_hash == &KECCAK_EMPTY {
|
||||||
return Self::find_jump_destinations(code);
|
let cache_item = Self::find_jump_and_sub_destinations(code);
|
||||||
|
return (cache_item.jump_destination.0, cache_item.sub_entrypoint.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
||||||
return d.0.clone();
|
return (d.jump_destination.0.clone(), d.sub_entrypoint.0.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = Self::find_jump_destinations(code);
|
let d = Self::find_jump_and_sub_destinations(code);
|
||||||
|
|
||||||
if let Some(ref code_hash) = code_hash {
|
if let Some(ref code_hash) = code_hash {
|
||||||
self.jump_destinations
|
self.jump_destinations.lock().insert(*code_hash, d.clone());
|
||||||
.lock()
|
|
||||||
.insert(*code_hash, Bits(d.clone()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d
|
(d.jump_destination.0, d.sub_entrypoint.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_jump_destinations(code: &[u8]) -> Arc<BitSet> {
|
fn find_jump_and_sub_destinations(code: &[u8]) -> CacheItem {
|
||||||
let mut jump_dests = BitSet::with_capacity(code.len());
|
let mut jump_dests = BitSet::with_capacity(code.len());
|
||||||
|
let mut sub_entrypoints = BitSet::with_capacity(code.len());
|
||||||
let mut position = 0;
|
let mut position = 0;
|
||||||
|
|
||||||
while position < code.len() {
|
while position < code.len() {
|
||||||
let instruction = Instruction::from_u8(code[position]);
|
let instruction = Instruction::from_u8(code[position]);
|
||||||
|
|
||||||
if let Some(instruction) = instruction {
|
if let Some(instruction) = instruction {
|
||||||
if instruction == instructions::JUMPDEST {
|
match instruction {
|
||||||
|
instructions::JUMPDEST => {
|
||||||
jump_dests.insert(position);
|
jump_dests.insert(position);
|
||||||
} else if let Some(push_bytes) = instruction.push_bytes() {
|
}
|
||||||
|
instructions::BEGINSUB => {
|
||||||
|
sub_entrypoints.insert(position);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(push_bytes) = instruction.push_bytes() {
|
||||||
position += push_bytes;
|
position += push_bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
position += 1;
|
position += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
jump_dests.shrink_to_fit();
|
jump_dests.shrink_to_fit();
|
||||||
Arc::new(jump_dests)
|
CacheItem {
|
||||||
|
jump_destination: Bits(Arc::new(jump_dests)),
|
||||||
|
sub_entrypoint: Bits(Arc::new(sub_entrypoints)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,15 +128,92 @@ impl Default for SharedCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn test_find_jump_destinations() {
|
mod test {
|
||||||
use rustc_hex::FromHex;
|
use super::*;
|
||||||
|
use hex_literal::hex;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_jump_destinations() {
|
||||||
// given
|
// given
|
||||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
|
||||||
|
// 0000 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
// 0021 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
// 0042 5B JUMPDEST
|
||||||
|
// 0043 01 ADD
|
||||||
|
// 0044 60 PUSH1 0x00
|
||||||
|
// 0046 55 SSTORE
|
||||||
|
let code = hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
|
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(valid_jump_destinations.contains(66));
|
assert!(cache_item
|
||||||
|
.jump_destination
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.eq(vec![66].into_iter()));
|
||||||
|
assert!(cache_item.sub_entrypoint.0.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_jump_destinations_not_in_data_segments() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// 0000 60 06 PUSH1 06
|
||||||
|
// 0002 56 JUMP
|
||||||
|
// 0003 50 5B PUSH1 0x5B
|
||||||
|
// 0005 56 STOP
|
||||||
|
// 0006 5B JUMPDEST
|
||||||
|
// 0007 60 04 PUSH1 04
|
||||||
|
// 0009 56 JUMP
|
||||||
|
let code = hex!("600656605B565B6004");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(cache_item.jump_destination.0.iter().eq(vec![6].into_iter()));
|
||||||
|
assert!(cache_item.sub_entrypoint.0.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_sub_entrypoints() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// see https://eips.ethereum.org/EIPS/eip-2315 for disassembly
|
||||||
|
let code = hex!("6800000000000000000c5e005c60115e5d5c5d");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(cache_item.jump_destination.0.is_empty());
|
||||||
|
assert!(cache_item
|
||||||
|
.sub_entrypoint
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.eq(vec![12, 17].into_iter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_jump_and_sub_allowing_unknown_opcodes() {
|
||||||
|
// precondition
|
||||||
|
assert!(Instruction::from_u8(0xcc) == None);
|
||||||
|
|
||||||
|
// given
|
||||||
|
|
||||||
|
// 0000 5B JUMPDEST
|
||||||
|
// 0001 CC ???
|
||||||
|
// 0002 5C BEGINSUB
|
||||||
|
let code = hex!("5BCC5C");
|
||||||
|
|
||||||
|
// when
|
||||||
|
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(cache_item.jump_destination.0.iter().eq(vec![0].into_iter()));
|
||||||
|
assert!(cache_item.sub_entrypoint.0.iter().eq(vec![2].into_iter()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use super::interpreter::MAX_SUB_STACK_SIZE;
|
||||||
use ethereum_types::{Address, H256, U256};
|
use ethereum_types::{Address, H256, U256};
|
||||||
use factory::Factory;
|
use factory::Factory;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
@ -1037,6 +1038,171 @@ fn test_jumps(factory: super::Factory) {
|
|||||||
assert_eq!(gas_left, U256::from(54_117));
|
assert_eq!(gas_left, U256::from(54_117));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_simple: test_subs_simple_int}
|
||||||
|
fn test_subs_simple(factory: super::Factory) {
|
||||||
|
// as defined in https://eips.ethereum.org/EIPS/eip-2315
|
||||||
|
let code = hex!("60045e005c5d").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(18);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_two_levels: test_subs_two_levels_int}
|
||||||
|
fn test_subs_two_levels(factory: super::Factory) {
|
||||||
|
// as defined in https://eips.ethereum.org/EIPS/eip-2315
|
||||||
|
let code = hex!("6800000000000000000c5e005c60115e5d5c5d").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(36);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_invalid_jump: test_subs_invalid_jump_int}
|
||||||
|
fn test_subs_invalid_jump(factory: super::Factory) {
|
||||||
|
// as defined in https://eips.ethereum.org/EIPS/eip-2315
|
||||||
|
let code = hex!("6801000000000000000c5e005c60115e5d5c5d").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(24);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let current = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected = Result::Err(vm::Error::BadJumpDestination { destination: 0xc });
|
||||||
|
assert_eq!(current, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_shallow_return_stack: test_subs_shallow_return_stack_int}
|
||||||
|
fn test_subs_shallow_return_stack(factory: super::Factory) {
|
||||||
|
// as defined in https://eips.ethereum.org/EIPS/eip-2315
|
||||||
|
let code = hex!("5d5858").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(24);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let current = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected = Result::Err(vm::Error::SubStackUnderflow {
|
||||||
|
wanted: 1,
|
||||||
|
on_stack: 0,
|
||||||
|
});
|
||||||
|
assert_eq!(current, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_substack_limit: test_subs_substack_limit_int}
|
||||||
|
fn test_subs_substack_limit(factory: super::Factory) {
|
||||||
|
// PUSH <recursion_limit>
|
||||||
|
// JUMP a
|
||||||
|
// s: BEGINSUB
|
||||||
|
// a: JUMPDEST
|
||||||
|
// DUP1
|
||||||
|
// JUMPI c
|
||||||
|
// STOP
|
||||||
|
// c: JUMPDEST
|
||||||
|
// PUSH1 1
|
||||||
|
// SWAP
|
||||||
|
// SUB
|
||||||
|
// JUMPSUB s
|
||||||
|
|
||||||
|
let mut code = hex!("6104006007565c5b80600d57005b6001900360065e").to_vec();
|
||||||
|
code[1..3].copy_from_slice(&(MAX_SUB_STACK_SIZE as u16).to_be_bytes()[..]);
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(1_000_000);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(959_049));
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_substack_out: test_subs_substack_out_int}
|
||||||
|
fn test_subs_substack_out(factory: super::Factory) {
|
||||||
|
let mut code = hex!("6104006007565c5b80600d57005b6001900360065e").to_vec();
|
||||||
|
code[1..3].copy_from_slice(&((MAX_SUB_STACK_SIZE + 1) as u16).to_be_bytes()[..]);
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(1_000_000);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let current = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected = Result::Err(vm::Error::OutOfSubStack {
|
||||||
|
wanted: 1,
|
||||||
|
limit: MAX_SUB_STACK_SIZE,
|
||||||
|
});
|
||||||
|
assert_eq!(current, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_sub_at_end: test_subs_sub_at_end_int}
|
||||||
|
fn test_subs_sub_at_end(factory: super::Factory) {
|
||||||
|
let code = hex!("6005565c5d5b60035e").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(30);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_test! {test_subs_walk_into_subroutine: test_subs_walk_into_subroutine_int}
|
||||||
|
fn test_subs_walk_into_subroutine(factory: super::Factory) {
|
||||||
|
let code = hex!("5c5d00").to_vec();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(100);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_berlin();
|
||||||
|
|
||||||
|
let current = {
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
test_finalize(vm.exec(&mut ext).ok().unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected = Result::Err(vm::Error::InvalidSubEntry);
|
||||||
|
assert_eq!(current, expected);
|
||||||
|
}
|
||||||
|
|
||||||
evm_test! {test_calls: test_calls_int}
|
evm_test! {test_calls: test_calls_int}
|
||||||
fn test_calls(factory: super::Factory) {
|
fn test_calls(factory: super::Factory) {
|
||||||
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();
|
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();
|
||||||
|
@ -435,6 +435,9 @@ impl<'a> CallCreateExecutive<'a> {
|
|||||||
| Err(vm::Error::MutableCallInStaticContext)
|
| Err(vm::Error::MutableCallInStaticContext)
|
||||||
| Err(vm::Error::OutOfBounds)
|
| Err(vm::Error::OutOfBounds)
|
||||||
| Err(vm::Error::Reverted)
|
| Err(vm::Error::Reverted)
|
||||||
|
| Err(vm::Error::SubStackUnderflow { .. })
|
||||||
|
| Err(vm::Error::OutOfSubStack { .. })
|
||||||
|
| Err(vm::Error::InvalidSubEntry)
|
||||||
| Ok(FinalizationResult {
|
| Ok(FinalizationResult {
|
||||||
apply_state: false, ..
|
apply_state: false, ..
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -126,6 +126,8 @@ pub struct CommonParams {
|
|||||||
pub eip1884_transition: BlockNumber,
|
pub eip1884_transition: BlockNumber,
|
||||||
/// Number of first block where EIP-2028 rules begin.
|
/// Number of first block where EIP-2028 rules begin.
|
||||||
pub eip2028_transition: BlockNumber,
|
pub eip2028_transition: BlockNumber,
|
||||||
|
/// Number of first block where EIP-2315 rules begin.
|
||||||
|
pub eip2315_transition: BlockNumber,
|
||||||
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
|
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
|
||||||
pub dust_protection_transition: BlockNumber,
|
pub dust_protection_transition: BlockNumber,
|
||||||
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
|
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
|
||||||
@ -197,6 +199,7 @@ impl CommonParams {
|
|||||||
&& !(block_number >= self.eip1283_disable_transition))
|
&& !(block_number >= self.eip1283_disable_transition))
|
||||||
|| block_number >= self.eip1283_reenable_transition;
|
|| block_number >= self.eip1283_reenable_transition;
|
||||||
schedule.eip1706 = block_number >= self.eip1706_transition;
|
schedule.eip1706 = block_number >= self.eip1706_transition;
|
||||||
|
schedule.have_subs = block_number >= self.eip2315_transition;
|
||||||
|
|
||||||
if block_number >= self.eip1884_transition {
|
if block_number >= self.eip1884_transition {
|
||||||
schedule.have_selfbalance = true;
|
schedule.have_selfbalance = true;
|
||||||
@ -329,6 +332,9 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
eip2028_transition: p
|
eip2028_transition: p
|
||||||
.eip2028_transition
|
.eip2028_transition
|
||||||
.map_or_else(BlockNumber::max_value, Into::into),
|
.map_or_else(BlockNumber::max_value, Into::into),
|
||||||
|
eip2315_transition: p
|
||||||
|
.eip2315_transition
|
||||||
|
.map_or_else(BlockNumber::max_value, Into::into),
|
||||||
dust_protection_transition: p
|
dust_protection_transition: p
|
||||||
.dust_protection_transition
|
.dust_protection_transition
|
||||||
.map_or_else(BlockNumber::max_value, Into::into),
|
.map_or_else(BlockNumber::max_value, Into::into),
|
||||||
|
@ -34,6 +34,12 @@ pub enum Error {
|
|||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
/// When execution would exceed defined Stack Limit
|
/// When execution would exceed defined Stack Limit
|
||||||
OutOfStack,
|
OutOfStack,
|
||||||
|
/// When there is not enough subroutine stack elements to return from
|
||||||
|
SubStackUnderflow,
|
||||||
|
/// When execution would exceed defined subroutine Stack Limit
|
||||||
|
OutOfSubStack,
|
||||||
|
/// When the code walks into a subroutine, that is not allowed
|
||||||
|
InvalidSubEntry,
|
||||||
/// When builtin contract failed on input data
|
/// When builtin contract failed on input data
|
||||||
BuiltIn,
|
BuiltIn,
|
||||||
/// Returned on evm internal error. Should never be ignored during development.
|
/// Returned on evm internal error. Should never be ignored during development.
|
||||||
@ -57,6 +63,9 @@ impl<'a> From<&'a VmError> for Error {
|
|||||||
VmError::BadInstruction { .. } => Error::BadInstruction,
|
VmError::BadInstruction { .. } => Error::BadInstruction,
|
||||||
VmError::StackUnderflow { .. } => Error::StackUnderflow,
|
VmError::StackUnderflow { .. } => Error::StackUnderflow,
|
||||||
VmError::OutOfStack { .. } => Error::OutOfStack,
|
VmError::OutOfStack { .. } => Error::OutOfStack,
|
||||||
|
VmError::SubStackUnderflow { .. } => Error::SubStackUnderflow,
|
||||||
|
VmError::OutOfSubStack { .. } => Error::OutOfSubStack,
|
||||||
|
VmError::InvalidSubEntry { .. } => Error::InvalidSubEntry,
|
||||||
VmError::BuiltIn { .. } => Error::BuiltIn,
|
VmError::BuiltIn { .. } => Error::BuiltIn,
|
||||||
VmError::Wasm { .. } => Error::Wasm,
|
VmError::Wasm { .. } => Error::Wasm,
|
||||||
VmError::Internal(_) => Error::Internal,
|
VmError::Internal(_) => Error::Internal,
|
||||||
@ -82,7 +91,10 @@ impl fmt::Display for Error {
|
|||||||
BadInstruction => "Bad instruction",
|
BadInstruction => "Bad instruction",
|
||||||
StackUnderflow => "Stack underflow",
|
StackUnderflow => "Stack underflow",
|
||||||
OutOfStack => "Out of stack",
|
OutOfStack => "Out of stack",
|
||||||
|
SubStackUnderflow => "Subroutine stack underflow",
|
||||||
|
OutOfSubStack => "Subroutine stack overflow",
|
||||||
BuiltIn => "Built-in failed",
|
BuiltIn => "Built-in failed",
|
||||||
|
InvalidSubEntry => "Invalid subroutine entry",
|
||||||
Wasm => "Wasm runtime error",
|
Wasm => "Wasm runtime error",
|
||||||
Internal => "Internal error",
|
Internal => "Internal error",
|
||||||
MutableCallInStaticContext => "Mutable Call In Static Context",
|
MutableCallInStaticContext => "Mutable Call In Static Context",
|
||||||
@ -108,6 +120,9 @@ impl Encodable for Error {
|
|||||||
Wasm => 8,
|
Wasm => 8,
|
||||||
OutOfBounds => 9,
|
OutOfBounds => 9,
|
||||||
Reverted => 10,
|
Reverted => 10,
|
||||||
|
SubStackUnderflow => 11,
|
||||||
|
OutOfSubStack => 12,
|
||||||
|
InvalidSubEntry => 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
s.append_internal(&value);
|
s.append_internal(&value);
|
||||||
@ -130,6 +145,9 @@ impl Decodable for Error {
|
|||||||
8 => Ok(Wasm),
|
8 => Ok(Wasm),
|
||||||
9 => Ok(OutOfBounds),
|
9 => Ok(OutOfBounds),
|
||||||
10 => Ok(Reverted),
|
10 => Ok(Reverted),
|
||||||
|
11 => Ok(SubStackUnderflow),
|
||||||
|
12 => Ok(OutOfSubStack),
|
||||||
|
13 => Ok(InvalidSubEntry),
|
||||||
_ => Err(DecoderError::Custom("Invalid error type")),
|
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,22 @@ pub enum Error {
|
|||||||
/// What was the stack limit
|
/// What was the stack limit
|
||||||
limit: usize,
|
limit: usize,
|
||||||
},
|
},
|
||||||
|
/// When there is not enough subroutine stack elements to return from
|
||||||
|
SubStackUnderflow {
|
||||||
|
/// How many stack elements was requested by instruction
|
||||||
|
wanted: usize,
|
||||||
|
/// How many elements were on stack
|
||||||
|
on_stack: usize,
|
||||||
|
},
|
||||||
|
/// When execution would exceed defined subroutine Stack Limit
|
||||||
|
OutOfSubStack {
|
||||||
|
/// How many stack elements instruction wanted to pop
|
||||||
|
wanted: usize,
|
||||||
|
/// What was the stack limit
|
||||||
|
limit: usize,
|
||||||
|
},
|
||||||
|
/// When the code walks into a subroutine, that is not allowed
|
||||||
|
InvalidSubEntry,
|
||||||
/// Built-in contract failed on given input
|
/// Built-in contract failed on given input
|
||||||
BuiltIn(&'static str),
|
BuiltIn(&'static str),
|
||||||
/// When execution tries to modify the state in static context
|
/// When execution tries to modify the state in static context
|
||||||
@ -103,9 +119,11 @@ impl fmt::Display for Error {
|
|||||||
use self::Error::*;
|
use self::Error::*;
|
||||||
match *self {
|
match *self {
|
||||||
OutOfGas => write!(f, "Out of gas"),
|
OutOfGas => write!(f, "Out of gas"),
|
||||||
BadJumpDestination { destination } => {
|
BadJumpDestination { destination } => write!(
|
||||||
write!(f, "Bad jump destination {:x}", destination)
|
f,
|
||||||
}
|
"Bad jump destination {:x} (trimmed to usize)",
|
||||||
|
destination
|
||||||
|
),
|
||||||
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
|
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
|
||||||
StackUnderflow {
|
StackUnderflow {
|
||||||
instruction,
|
instruction,
|
||||||
@ -117,6 +135,13 @@ impl fmt::Display for Error {
|
|||||||
wanted,
|
wanted,
|
||||||
limit,
|
limit,
|
||||||
} => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
|
} => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
|
||||||
|
SubStackUnderflow { wanted, on_stack } => {
|
||||||
|
write!(f, "Subroutine stack underflow {}/{}", wanted, on_stack)
|
||||||
|
}
|
||||||
|
OutOfSubStack { wanted, limit } => {
|
||||||
|
write!(f, "Out of subroutine stack {}/{}", wanted, limit)
|
||||||
|
}
|
||||||
|
InvalidSubEntry => write!(f, "Invalid subroutine entry"),
|
||||||
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
|
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
|
||||||
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
||||||
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
|
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
|
||||||
|
@ -120,6 +120,8 @@ pub struct Schedule {
|
|||||||
pub have_chain_id: bool,
|
pub have_chain_id: bool,
|
||||||
/// SELFBALANCE opcode enabled.
|
/// SELFBALANCE opcode enabled.
|
||||||
pub have_selfbalance: bool,
|
pub have_selfbalance: bool,
|
||||||
|
/// BEGINSUB, JUMPSUB and RETURNSUB opcodes enabled.
|
||||||
|
pub have_subs: bool,
|
||||||
/// Kill basic accounts below this balance if touched.
|
/// Kill basic accounts below this balance if touched.
|
||||||
pub kill_dust: CleanDustMode,
|
pub kill_dust: CleanDustMode,
|
||||||
/// Enable EIP-1283 rules
|
/// Enable EIP-1283 rules
|
||||||
@ -224,6 +226,7 @@ impl Schedule {
|
|||||||
have_bitwise_shifting: false,
|
have_bitwise_shifting: false,
|
||||||
have_chain_id: false,
|
have_chain_id: false,
|
||||||
have_selfbalance: false,
|
have_selfbalance: false,
|
||||||
|
have_subs: false,
|
||||||
have_extcodehash: false,
|
have_extcodehash: false,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
||||||
max_depth: 1024,
|
max_depth: 1024,
|
||||||
@ -303,6 +306,13 @@ impl Schedule {
|
|||||||
schedule
|
schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Schedule for the Berlin fork of the Ethereum main net.
|
||||||
|
pub fn new_berlin() -> Schedule {
|
||||||
|
let mut schedule = Self::new_istanbul();
|
||||||
|
schedule.have_subs = true; // EIP 2315
|
||||||
|
schedule
|
||||||
|
}
|
||||||
|
|
||||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||||
Schedule {
|
Schedule {
|
||||||
exceptional_failed_code_deposit: efcd,
|
exceptional_failed_code_deposit: efcd,
|
||||||
@ -313,6 +323,7 @@ impl Schedule {
|
|||||||
have_bitwise_shifting: false,
|
have_bitwise_shifting: false,
|
||||||
have_chain_id: false,
|
have_chain_id: false,
|
||||||
have_selfbalance: false,
|
have_selfbalance: false,
|
||||||
|
have_subs: false,
|
||||||
have_extcodehash: false,
|
have_extcodehash: false,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
||||||
max_depth: 1024,
|
max_depth: 1024,
|
||||||
|
@ -115,6 +115,13 @@ impl FakeExt {
|
|||||||
ext
|
ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// New fake externalities with Berlin schedule rules
|
||||||
|
pub fn new_berlin() -> Self {
|
||||||
|
let mut ext = FakeExt::default();
|
||||||
|
ext.schedule = Schedule::new_berlin();
|
||||||
|
ext
|
||||||
|
}
|
||||||
|
|
||||||
/// Alter fake externalities to allow wasm
|
/// Alter fake externalities to allow wasm
|
||||||
pub fn with_wasm(mut self) -> Self {
|
pub fn with_wasm(mut self) -> Self {
|
||||||
self.schedule.wasm = Some(Default::default());
|
self.schedule.wasm = Some(Default::default());
|
||||||
|
@ -104,6 +104,8 @@ pub struct Params {
|
|||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
pub eip2028_transition: Option<Uint>,
|
pub eip2028_transition: Option<Uint>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
|
pub eip2315_transition: Option<Uint>,
|
||||||
|
/// See `CommonParams` docs.
|
||||||
pub dust_protection_transition: Option<Uint>,
|
pub dust_protection_transition: Option<Uint>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
pub nonce_cap_increment: Option<Uint>,
|
pub nonce_cap_increment: Option<Uint>,
|
||||||
|
Loading…
Reference in New Issue
Block a user