EIP-2315: Simple Subroutines for the EVM (#11629)

This commit is contained in:
adria0.eth 2020-08-07 20:41:07 +03:00 committed by Artem Vorotnikov
parent 751210c963
commit 1460f6cc27
No known key found for this signature in database
GPG Key ID: E0148C3F2FBB7A20
11 changed files with 452 additions and 40 deletions

View File

@ -321,6 +321,13 @@ enum_with_from_u8! {
#[doc = "Makes a log entry, 4 topics."]
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"]
CREATE = 0xf0,
#[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[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 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[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special));
arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special));

View File

@ -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_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 {
let mut bytes = [0u8; 32];
x.to_little_endian(&mut bytes);
@ -101,6 +105,8 @@ enum InstructionResult<Gas> {
Ok,
UnusedGas(Gas),
JumpToPosition(U256),
JumpToSubroutine(U256),
ReturnFromSubroutine(usize),
StopExecutionNeedsReturn {
/// Gas left.
gas: Gas,
@ -183,8 +189,10 @@ pub struct Interpreter<Cost: CostType> {
do_trace: bool,
done: bool,
valid_jump_destinations: Option<Arc<BitSet>>,
valid_subroutine_destinations: Option<Arc<BitSet>>,
gasometer: Option<Gasometer<Cost>>,
stack: VecStack<U256>,
return_stack: Vec<usize>,
resume_output_range: Option<(U256, U256)>,
resume_result: Option<InstructionResult<Cost>>,
last_stack_ret_len: usize,
@ -290,10 +298,12 @@ impl<Cost: CostType> Interpreter<Cost> {
let params = InterpreterParams::from(params);
let informant = informant::EvmInformant::new(depth);
let valid_jump_destinations = None;
let valid_subroutine_destinations = None;
let gasometer = Cost::from_u256(params.gas)
.ok()
.map(|gas| Gasometer::<Cost>::new(gas));
let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero());
let return_stack = Vec::with_capacity(MAX_SUB_STACK_SIZE);
Interpreter {
cache,
@ -301,8 +311,10 @@ impl<Cost: CostType> Interpreter<Cost> {
reader,
informant,
valid_jump_destinations,
valid_subroutine_destinations,
gasometer,
stack,
return_stack,
done: false,
// Overridden in `step_inner` based on
// the result of `ext.trace_next_instruction`.
@ -478,7 +490,8 @@ impl<Cost: CostType> Interpreter<Cost> {
if self.valid_jump_destinations.is_none() {
self.valid_jump_destinations = Some(
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
@ -491,6 +504,28 @@ impl<Cost: CostType> Interpreter<Cost> {
};
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 {
gas,
init_off,
@ -537,20 +572,20 @@ impl<Cost: CostType> Interpreter<Cost> {
) -> vm::Result<()> {
let schedule = ext.schedule();
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call)
|| (instruction == instructions::CREATE2 && !schedule.have_create2)
|| (instruction == instructions::STATICCALL && !schedule.have_static_call)
|| ((instruction == instructions::RETURNDATACOPY
|| instruction == instructions::RETURNDATASIZE)
use instructions::*;
if (instruction == DELEGATECALL && !schedule.have_delegate_call)
|| (instruction == CREATE2 && !schedule.have_create2)
|| (instruction == STATICCALL && !schedule.have_static_call)
|| ((instruction == RETURNDATACOPY || instruction == RETURNDATASIZE)
&& !schedule.have_return_data)
|| (instruction == instructions::REVERT && !schedule.have_revert)
|| ((instruction == instructions::SHL
|| instruction == instructions::SHR
|| instruction == instructions::SAR)
|| (instruction == REVERT && !schedule.have_revert)
|| ((instruction == SHL || instruction == SHR || instruction == SAR)
&& !schedule.have_bitwise_shifting)
|| (instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash)
|| (instruction == instructions::CHAINID && !schedule.have_chain_id)
|| (instruction == instructions::SELFBALANCE && !schedule.have_selfbalance)
|| (instruction == EXTCODEHASH && !schedule.have_extcodehash)
|| (instruction == CHAINID && !schedule.have_chain_id)
|| (instruction == SELFBALANCE && !schedule.have_selfbalance)
|| ((instruction == BEGINSUB || instruction == JUMPSUB || instruction == RETURNSUB)
&& !schedule.have_subs)
{
return Err(vm::Error::BadInstruction {
instruction: instruction as u8,
@ -623,6 +658,29 @@ impl<Cost: CostType> Interpreter<Cost> {
instructions::JUMPDEST => {
// 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 => {
let endowment = 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 {
Ok(jump)
} else {
// Note: if jump > usize, BadJumpDestination value is trimmed
Err(vm::Error::BadJumpDestination { destination: jump })
}
}

View File

@ -26,6 +26,7 @@ use std::sync::Arc;
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
// stub for a HeapSizeOf implementation.
#[derive(Clone)]
struct Bits(Arc<BitSet>);
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
pub struct SharedCache {
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
jump_destinations: Mutex<MemoryLruCache<H256, CacheItem>>,
}
impl SharedCache {
@ -50,47 +63,62 @@ impl SharedCache {
}
/// 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 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) {
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 {
self.jump_destinations
.lock()
.insert(*code_hash, Bits(d.clone()));
self.jump_destinations.lock().insert(*code_hash, 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 sub_entrypoints = BitSet::with_capacity(code.len());
let mut position = 0;
while position < code.len() {
let instruction = Instruction::from_u8(code[position]);
if let Some(instruction) = instruction {
if instruction == instructions::JUMPDEST {
match instruction {
instructions::JUMPDEST => {
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 += 1;
}
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 {
}
}
#[cfg(test)]
mod test {
use super::*;
use hex_literal::hex;
#[test]
fn test_find_jump_destinations() {
use rustc_hex::FromHex;
// 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
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
// 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()));
}
}

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// 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 factory::Factory;
use hex_literal::hex;
@ -1037,6 +1038,171 @@ fn test_jumps(factory: super::Factory) {
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}
fn test_calls(factory: super::Factory) {
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();

View File

@ -435,6 +435,9 @@ impl<'a> CallCreateExecutive<'a> {
| Err(vm::Error::MutableCallInStaticContext)
| Err(vm::Error::OutOfBounds)
| Err(vm::Error::Reverted)
| Err(vm::Error::SubStackUnderflow { .. })
| Err(vm::Error::OutOfSubStack { .. })
| Err(vm::Error::InvalidSubEntry)
| Ok(FinalizationResult {
apply_state: false, ..
}) => {

View File

@ -126,6 +126,8 @@ pub struct CommonParams {
pub eip1884_transition: BlockNumber,
/// Number of first block where EIP-2028 rules begin.
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.
pub dust_protection_transition: BlockNumber,
/// 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_reenable_transition;
schedule.eip1706 = block_number >= self.eip1706_transition;
schedule.have_subs = block_number >= self.eip2315_transition;
if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true;
@ -329,6 +332,9 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2028_transition: p
.eip2028_transition
.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
.map_or_else(BlockNumber::max_value, Into::into),

View File

@ -34,6 +34,12 @@ pub enum Error {
StackUnderflow,
/// When execution would exceed defined Stack Limit
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
BuiltIn,
/// 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::StackUnderflow { .. } => Error::StackUnderflow,
VmError::OutOfStack { .. } => Error::OutOfStack,
VmError::SubStackUnderflow { .. } => Error::SubStackUnderflow,
VmError::OutOfSubStack { .. } => Error::OutOfSubStack,
VmError::InvalidSubEntry { .. } => Error::InvalidSubEntry,
VmError::BuiltIn { .. } => Error::BuiltIn,
VmError::Wasm { .. } => Error::Wasm,
VmError::Internal(_) => Error::Internal,
@ -82,7 +91,10 @@ impl fmt::Display for Error {
BadInstruction => "Bad instruction",
StackUnderflow => "Stack underflow",
OutOfStack => "Out of stack",
SubStackUnderflow => "Subroutine stack underflow",
OutOfSubStack => "Subroutine stack overflow",
BuiltIn => "Built-in failed",
InvalidSubEntry => "Invalid subroutine entry",
Wasm => "Wasm runtime error",
Internal => "Internal error",
MutableCallInStaticContext => "Mutable Call In Static Context",
@ -108,6 +120,9 @@ impl Encodable for Error {
Wasm => 8,
OutOfBounds => 9,
Reverted => 10,
SubStackUnderflow => 11,
OutOfSubStack => 12,
InvalidSubEntry => 13,
};
s.append_internal(&value);
@ -130,6 +145,9 @@ impl Decodable for Error {
8 => Ok(Wasm),
9 => Ok(OutOfBounds),
10 => Ok(Reverted),
11 => Ok(SubStackUnderflow),
12 => Ok(OutOfSubStack),
13 => Ok(InvalidSubEntry),
_ => Err(DecoderError::Custom("Invalid error type")),
}
}

View File

@ -72,6 +72,22 @@ pub enum Error {
/// What was the stack limit
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
BuiltIn(&'static str),
/// When execution tries to modify the state in static context
@ -103,9 +119,11 @@ impl fmt::Display for Error {
use self::Error::*;
match *self {
OutOfGas => write!(f, "Out of gas"),
BadJumpDestination { destination } => {
write!(f, "Bad jump destination {:x}", destination)
}
BadJumpDestination { destination } => write!(
f,
"Bad jump destination {:x} (trimmed to usize)",
destination
),
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
StackUnderflow {
instruction,
@ -117,6 +135,13 @@ impl fmt::Display for Error {
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),
Internal(ref msg) => write!(f, "Internal error: {}", msg),
MutableCallInStaticContext => write!(f, "Mutable call in static context"),

View File

@ -120,6 +120,8 @@ pub struct Schedule {
pub have_chain_id: bool,
/// SELFBALANCE opcode enabled.
pub have_selfbalance: bool,
/// BEGINSUB, JUMPSUB and RETURNSUB opcodes enabled.
pub have_subs: bool,
/// Kill basic accounts below this balance if touched.
pub kill_dust: CleanDustMode,
/// Enable EIP-1283 rules
@ -224,6 +226,7 @@ impl Schedule {
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_subs: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,
@ -303,6 +306,13 @@ impl 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 {
Schedule {
exceptional_failed_code_deposit: efcd,
@ -313,6 +323,7 @@ impl Schedule {
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_subs: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,

View File

@ -115,6 +115,13 @@ impl FakeExt {
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
pub fn with_wasm(mut self) -> Self {
self.schedule.wasm = Some(Default::default());

View File

@ -104,6 +104,8 @@ pub struct Params {
/// See `CommonParams` docs.
pub eip2028_transition: Option<Uint>,
/// See `CommonParams` docs.
pub eip2315_transition: Option<Uint>,
/// See `CommonParams` docs.
pub dust_protection_transition: Option<Uint>,
/// See `CommonParams` docs.
pub nonce_cap_increment: Option<Uint>,