Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Commit 9845679

Browse files
committed
Merge branch 'master' into dp/chore/upgrade-to-rocksdb-0.14
* master: Implement Simple Subroutines for the EVM (EIP 2315) (#11629) Adria0/fix/tests (#11597)
2 parents ac070d7 + 234a287 commit 9845679

31 files changed

+1067
-163
lines changed

ethcore/evm/src/instructions.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ macro_rules! enum_with_from_u8 {
4141
}
4242
};
4343
}
44-
4544
enum_with_from_u8! {
4645
#[doc = "Virtual machine bytecode instruction."]
4746
#[repr(u8)]
@@ -321,6 +320,13 @@ enum_with_from_u8! {
321320
#[doc = "Makes a log entry, 4 topics."]
322321
LOG4 = 0xa4,
323322

323+
#[doc = "Marks the entry point to a subroutine."]
324+
BEGINSUB = 0xb2,
325+
#[doc = "Jumps to a defined BEGINSUB subroutine."]
326+
JUMPSUB = 0xb3,
327+
#[doc = "Returns from a subroutine."]
328+
RETURNSUB = 0xb7,
329+
324330
#[doc = "create a new account with associated code"]
325331
CREATE = 0xf0,
326332
#[doc = "message-call into an account"]
@@ -586,6 +592,9 @@ lazy_static! {
586592
arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special));
587593
arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special));
588594
arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special));
595+
arr[BEGINSUB as usize] = Some(InstructionInfo::new("BEGINSUB", 0, 0, GasPriceTier::Base));
596+
arr[JUMPSUB as usize] = Some(InstructionInfo::new("JUMPSUB", 1, 0, GasPriceTier::Low));
597+
arr[RETURNSUB as usize] = Some(InstructionInfo::new("RETURNSUB", 0, 0, GasPriceTier::VeryLow));
589598
arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special));
590599
arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special));
591600
arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special));

ethcore/evm/src/interpreter/mod.rs

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0
6262
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
6363
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
6464

65+
/// Maximal subroutine stack size as specified in
66+
/// https://eips.ethereum.org/EIPS/eip-2315.
67+
pub const MAX_SUB_STACK_SIZE : usize = 1024;
68+
6569
/// Abstraction over raw vector of Bytes. Easier state management of PC.
6670
struct CodeReader {
6771
position: ProgramCounter,
@@ -94,6 +98,8 @@ enum InstructionResult<Gas> {
9498
Ok,
9599
UnusedGas(Gas),
96100
JumpToPosition(U256),
101+
JumpToSubroutine(U256),
102+
ReturnFromSubroutine(usize),
97103
StopExecutionNeedsReturn {
98104
/// Gas left.
99105
gas: Gas,
@@ -179,8 +185,10 @@ pub struct Interpreter<Cost: CostType> {
179185
do_trace: bool,
180186
done: bool,
181187
valid_jump_destinations: Option<Arc<BitSet>>,
188+
valid_subroutine_destinations: Option<Arc<BitSet>>,
182189
gasometer: Option<Gasometer<Cost>>,
183190
stack: VecStack<U256>,
191+
return_stack: Vec<usize>,
184192
resume_output_range: Option<(U256, U256)>,
185193
resume_result: Option<InstructionResult<Cost>>,
186194
last_stack_ret_len: usize,
@@ -271,12 +279,14 @@ impl<Cost: CostType> Interpreter<Cost> {
271279
let params = InterpreterParams::from(params);
272280
let informant = informant::EvmInformant::new(depth);
273281
let valid_jump_destinations = None;
282+
let valid_subroutine_destinations = None;
274283
let gasometer = Cost::from_u256(params.gas).ok().map(|gas| Gasometer::<Cost>::new(gas));
275284
let stack = VecStack::with_capacity(schedule.stack_limit, U256::zero());
276-
285+
let return_stack = Vec::with_capacity(MAX_SUB_STACK_SIZE);
277286
Interpreter {
278287
cache, params, reader, informant,
279-
valid_jump_destinations, gasometer, stack,
288+
valid_jump_destinations, valid_subroutine_destinations,
289+
gasometer, stack, return_stack,
280290
done: false,
281291
// Overridden in `step_inner` based on
282292
// the result of `ext.trace_next_instruction`.
@@ -403,7 +413,7 @@ impl<Cost: CostType> Interpreter<Cost> {
403413
match result {
404414
InstructionResult::JumpToPosition(position) => {
405415
if self.valid_jump_destinations.is_none() {
406-
self.valid_jump_destinations = Some(self.cache.jump_destinations(&self.params.code_hash, &self.reader.code));
416+
self.valid_jump_destinations = Some(self.cache.jump_and_sub_destinations(&self.params.code_hash, &self.reader.code).0);
407417
}
408418
let jump_destinations = self.valid_jump_destinations.as_ref().expect("jump_destinations are initialized on first jump; qed");
409419
let pos = match self.verify_jump(position, jump_destinations) {
@@ -412,6 +422,21 @@ impl<Cost: CostType> Interpreter<Cost> {
412422
};
413423
self.reader.position = pos;
414424
},
425+
InstructionResult::JumpToSubroutine(position) => {
426+
if self.valid_subroutine_destinations.is_none() {
427+
self.valid_subroutine_destinations = Some(self.cache.jump_and_sub_destinations(&self.params.code_hash, &self.reader.code).1);
428+
}
429+
let subroutine_destinations = self.valid_subroutine_destinations.as_ref().expect("subroutine_destinations are initialized on first jump; qed");
430+
let pos = match self.verify_jump(position, subroutine_destinations) {
431+
Ok(x) => x,
432+
Err(e) => return InterpreterResult::Done(Err(e))
433+
};
434+
self.return_stack.push(self.reader.position);
435+
self.reader.position = pos;
436+
},
437+
InstructionResult::ReturnFromSubroutine(pos) => {
438+
self.reader.position = pos;
439+
},
415440
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
416441
let mem = mem::replace(&mut self.mem, Vec::new());
417442
return InterpreterResult::Done(Ok(GasLeft::NeedsReturn {
@@ -436,15 +461,17 @@ impl<Cost: CostType> Interpreter<Cost> {
436461
fn verify_instruction(&self, ext: &dyn vm::Ext, instruction: Instruction, info: &InstructionInfo) -> vm::Result<()> {
437462
let schedule = ext.schedule();
438463

439-
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
440-
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
441-
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
442-
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
443-
(instruction == instructions::REVERT && !schedule.have_revert) ||
444-
((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) ||
445-
(instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash) ||
446-
(instruction == instructions::CHAINID && !schedule.have_chain_id) ||
447-
(instruction == instructions::SELFBALANCE && !schedule.have_selfbalance)
464+
use instructions::*;
465+
if (instruction == DELEGATECALL && !schedule.have_delegate_call) ||
466+
(instruction == CREATE2 && !schedule.have_create2) ||
467+
(instruction == STATICCALL && !schedule.have_static_call) ||
468+
((instruction == RETURNDATACOPY || instruction == RETURNDATASIZE) && !schedule.have_return_data) ||
469+
(instruction == REVERT && !schedule.have_revert) ||
470+
((instruction == SHL || instruction == SHR || instruction == SAR) && !schedule.have_bitwise_shifting) ||
471+
(instruction == EXTCODEHASH && !schedule.have_extcodehash) ||
472+
(instruction == CHAINID && !schedule.have_chain_id) ||
473+
(instruction == SELFBALANCE && !schedule.have_selfbalance) ||
474+
((instruction == BEGINSUB || instruction == JUMPSUB || instruction == RETURNSUB) && !schedule.have_subs)
448475
{
449476
return Err(vm::Error::BadInstruction {
450477
instruction: instruction as u8
@@ -525,6 +552,32 @@ impl<Cost: CostType> Interpreter<Cost> {
525552
instructions::JUMPDEST => {
526553
// ignore
527554
},
555+
instructions::BEGINSUB => {
556+
// ignore
557+
},
558+
instructions::JUMPSUB => {
559+
if self.return_stack.len() >= MAX_SUB_STACK_SIZE {
560+
return Err(vm::Error::OutOfSubStack {
561+
wanted: 1,
562+
limit: MAX_SUB_STACK_SIZE,
563+
});
564+
}
565+
let sub_destination = self.stack.pop_back();
566+
return Ok(InstructionResult::JumpToSubroutine(
567+
sub_destination
568+
))
569+
},
570+
instructions::RETURNSUB => {
571+
if let Some(pos) = self.return_stack.pop() {
572+
return Ok(InstructionResult::ReturnFromSubroutine(pos))
573+
} else {
574+
return Err(vm::Error::SubStackUnderflow {
575+
wanted: 1,
576+
on_stack: 0,
577+
});
578+
}
579+
580+
},
528581
instructions::CREATE | instructions::CREATE2 => {
529582
let endowment = self.stack.pop_back();
530583
let init_off = self.stack.pop_back();
@@ -1177,6 +1230,7 @@ impl<Cost: CostType> Interpreter<Cost> {
11771230
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
11781231
Ok(jump)
11791232
} else {
1233+
// Note: if jump > usize, BadJumpDestination value is trimmed
11801234
Err(vm::Error::BadJumpDestination {
11811235
destination: jump
11821236
})

ethcore/evm/src/interpreter/shared_cache.rs

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
2727

2828
/// Stub for a sharing `BitSet` data in cache (reference counted)
2929
/// and implementing MallocSizeOf on it.
30+
#[derive(Clone)]
3031
struct Bits(Arc<BitSet>);
3132

3233
impl MallocSizeOf for Bits {
@@ -36,9 +37,14 @@ impl MallocSizeOf for Bits {
3637
}
3738
}
3839

40+
#[derive(MallocSizeOf, Clone)]
41+
struct CacheItem {
42+
jump_destination: Bits,
43+
sub_entrypoint: Bits,
44+
}
3945
/// Global cache for EVM interpreter
4046
pub struct SharedCache {
41-
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
47+
jump_destinations: Mutex<MemoryLruCache<H256, CacheItem>>,
4248
}
4349

4450
impl SharedCache {
@@ -51,45 +57,54 @@ impl SharedCache {
5157
}
5258

5359
/// Get jump destinations bitmap for a contract.
54-
pub fn jump_destinations(&self, code_hash: &Option<H256>, code: &[u8]) -> Arc<BitSet> {
60+
pub fn jump_and_sub_destinations(&self, code_hash: &Option<H256>, code: &[u8]) -> (Arc<BitSet>, Arc<BitSet>) {
5561
if let Some(ref code_hash) = code_hash {
5662
if code_hash == &KECCAK_EMPTY {
57-
return Self::find_jump_destinations(code);
63+
let cache_item = Self::find_jump_and_sub_destinations(code);
64+
return (cache_item.jump_destination.0, cache_item.sub_entrypoint.0);
5865
}
5966

6067
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
61-
return d.0.clone();
68+
return (d.jump_destination.0.clone(), d.sub_entrypoint.0.clone());
6269
}
6370
}
6471

65-
let d = Self::find_jump_destinations(code);
72+
let d = Self::find_jump_and_sub_destinations(code);
6673

6774
if let Some(ref code_hash) = code_hash {
68-
self.jump_destinations.lock().insert(*code_hash, Bits(d.clone()));
75+
self.jump_destinations.lock().insert(*code_hash, d.clone());
6976
}
7077

71-
d
78+
(d.jump_destination.0, d.sub_entrypoint.0)
7279
}
7380

74-
fn find_jump_destinations(code: &[u8]) -> Arc<BitSet> {
81+
fn find_jump_and_sub_destinations(code: &[u8]) -> CacheItem {
7582
let mut jump_dests = BitSet::with_capacity(code.len());
83+
let mut sub_entrypoints = BitSet::with_capacity(code.len());
7684
let mut position = 0;
7785

7886
while position < code.len() {
7987
let instruction = Instruction::from_u8(code[position]);
8088

8189
if let Some(instruction) = instruction {
82-
if instruction == instructions::JUMPDEST {
83-
jump_dests.insert(position);
84-
} else if let Some(push_bytes) = instruction.push_bytes() {
85-
position += push_bytes;
90+
match instruction {
91+
instructions::JUMPDEST => { jump_dests.insert(position); },
92+
instructions::BEGINSUB => { sub_entrypoints.insert(position); },
93+
_ => {
94+
if let Some(push_bytes) = instruction.push_bytes() {
95+
position += push_bytes;
96+
}
97+
},
8698
}
8799
}
88100
position += 1;
89101
}
90102

91103
jump_dests.shrink_to_fit();
92-
Arc::new(jump_dests)
104+
CacheItem {
105+
jump_destination: Bits(Arc::new(jump_dests)),
106+
sub_entrypoint: Bits(Arc::new(sub_entrypoints)),
107+
}
93108
}
94109
}
95110

@@ -102,11 +117,75 @@ impl Default for SharedCache {
102117
#[test]
103118
fn test_find_jump_destinations() {
104119
// given
120+
121+
// 0000 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
122+
// 0021 7F PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
123+
// 0042 5B JUMPDEST
124+
// 0043 01 ADD
125+
// 0044 60 PUSH1 0x00
126+
// 0046 55 SSTORE
105127
let code = hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055");
106128

107129
// when
108-
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
130+
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
131+
132+
// then
133+
assert!(cache_item.jump_destination.0.iter().eq(vec![66].into_iter()));
134+
assert!(cache_item.sub_entrypoint.0.is_empty());
135+
}
136+
137+
#[test]
138+
fn test_find_jump_destinations_not_in_data_segments() {
139+
// given
140+
141+
// 0000 60 06 PUSH1 06
142+
// 0002 56 JUMP
143+
// 0003 50 5B PUSH1 0x5B
144+
// 0005 56 STOP
145+
// 0006 5B JUMPDEST
146+
// 0007 60 04 PUSH1 04
147+
// 0009 56 JUMP
148+
let code = hex!("600656605B565B6004");
149+
150+
// when
151+
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
152+
153+
// then
154+
assert!(cache_item.jump_destination.0.iter().eq(vec![6].into_iter()));
155+
assert!(cache_item.sub_entrypoint.0.is_empty());
156+
}
157+
158+
#[test]
159+
fn test_find_sub_entrypoints() {
160+
// given
161+
162+
// see https://eips.ethereum.org/EIPS/eip-2315 for disassembly
163+
let code = hex!("6800000000000000000cb300b26011b3b7b2b7");
164+
165+
// when
166+
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
167+
168+
// then
169+
assert!(cache_item.jump_destination.0.is_empty());
170+
assert!(cache_item.sub_entrypoint.0.iter().eq(vec![12, 17].into_iter()));
171+
}
172+
173+
#[test]
174+
fn test_find_jump_and_sub_allowing_unknown_opcodes() {
175+
// precondition
176+
assert!(Instruction::from_u8(0xcc) == None);
177+
178+
// given
179+
180+
// 0000 5B JUMPDEST
181+
// 0001 CC ???
182+
// 0002 B2 BEGINSUB
183+
let code = hex!("5BCCB2");
184+
185+
// when
186+
let cache_item = SharedCache::find_jump_and_sub_destinations(&code);
109187

110188
// then
111-
assert!(valid_jump_destinations.contains(66));
189+
assert!(cache_item.jump_destination.0.iter().eq(vec![0].into_iter()));
190+
assert!(cache_item.sub_entrypoint.0.iter().eq(vec![2].into_iter()));
112191
}

0 commit comments

Comments
 (0)