Skip to content

Commit

Permalink
EIP 145: Bitwise shifting instructions in EVM (openethereum#8451)
Browse files Browse the repository at this point in the history
* Add SHL, SHR, SAR opcodes

* Add have_bitwise_shifting schedule flag

* Add all EIP tests for SHL

* Add SHR implementation and tests

* Implement SAR and add tests

* Add eip145transition config param

* Change map_or to map_or_else when possible
  • Loading branch information
sorpaas authored and VladLupashevskyi committed May 23, 2018
1 parent 2f84096 commit 387e037
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 20 deletions.
10 changes: 9 additions & 1 deletion ethcore/evm/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ lazy_static! {
arr[OR as usize] = InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow);
arr[XOR as usize] = InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow);
arr[BYTE as usize] = InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow);
arr[SHL as usize] = InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow);
arr[SHR as usize] = InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow);
arr[SAR as usize] = InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow);
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid);
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid);
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low);
Expand Down Expand Up @@ -354,6 +357,12 @@ pub const XOR: Instruction = 0x18;
pub const NOT: Instruction = 0x19;
/// retrieve single byte from word
pub const BYTE: Instruction = 0x1a;
/// shift left operation
pub const SHL: Instruction = 0x1b;
/// logical shift right operation
pub const SHR: Instruction = 0x1c;
/// arithmetic shift right operation
pub const SAR: Instruction = 0x1d;

/// compute SHA3-256 hash
pub const SHA3: Instruction = 0x20;
Expand Down Expand Up @@ -589,4 +598,3 @@ pub const REVERT: Instruction = 0xfd;
pub const STATICCALL: Instruction = 0xfa;
/// halt execution and register account for later deletion
pub const SUICIDE: Instruction = 0xff;

55 changes: 54 additions & 1 deletion ethcore/evm/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ impl<Cost: CostType> Interpreter<Cost> {
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
(instruction == instructions::REVERT && !schedule.have_revert) {
(instruction == instructions::REVERT && !schedule.have_revert) ||
((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) {

return Err(vm::Error::BadInstruction {
instruction: instruction
Expand Down Expand Up @@ -871,6 +872,58 @@ impl<Cost: CostType> Interpreter<Cost> {
});
}
},
instructions::SHL => {
const CONST_256: U256 = U256([256, 0, 0, 0]);

let shift = stack.pop_back();
let value = stack.pop_back();

let result = if shift >= CONST_256 {
U256::zero()
} else {
value << (shift.as_u32() as usize)
};
stack.push(result);
},
instructions::SHR => {
const CONST_256: U256 = U256([256, 0, 0, 0]);

let shift = stack.pop_back();
let value = stack.pop_back();

let result = if shift >= CONST_256 {
U256::zero()
} else {
value >> (shift.as_u32() as usize)
};
stack.push(result);
},
instructions::SAR => {
// We cannot use get_and_reset_sign/set_sign here, because the rounding looks different.

const CONST_256: U256 = U256([256, 0, 0, 0]);
const CONST_HIBIT: U256 = U256([0, 0, 0, 0x8000000000000000]);

let shift = stack.pop_back();
let value = stack.pop_back();
let sign = value & CONST_HIBIT != U256::zero();

let result = if shift >= CONST_256 {
if sign {
U256::max_value()
} else {
U256::zero()
}
} else {
let shift = shift.as_u32() as usize;
let mut shifted = value >> shift;
if sign {
shifted = shifted | (U256::max_value() << (256 - shift));
}
shifted
};
stack.push(result);
},
_ => {
return Err(vm::Error::BadInstruction {
instruction: instruction
Expand Down
268 changes: 267 additions & 1 deletion ethcore/evm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,273 @@ fn test_create_in_staticcall(factory: super::Factory) {
assert_eq!(ext.calls.len(), 0);
}

evm_test!{test_shl: test_shl_int}
fn test_shl(factory: super::Factory) {
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000001",
"00",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000001",
"01",
"0000000000000000000000000000000000000000000000000000000000000002");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000001",
"ff",
"8000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000001",
"0100",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000001",
"0101",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"00",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"01",
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ff",
"8000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0100",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"0000000000000000000000000000000000000000000000000000000000000000",
"01",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1b,
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"01",
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
}

evm_test!{test_shr: test_shr_int}
fn test_shr(factory: super::Factory) {
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"0000000000000000000000000000000000000000000000000000000000000001",
"00",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"0000000000000000000000000000000000000000000000000000000000000001",
"01",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"8000000000000000000000000000000000000000000000000000000000000000",
"01",
"4000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"8000000000000000000000000000000000000000000000000000000000000000",
"ff",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"8000000000000000000000000000000000000000000000000000000000000000",
"0100",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"8000000000000000000000000000000000000000000000000000000000000000",
"0101",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"00",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"01",
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ff",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0100",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1c,
"0000000000000000000000000000000000000000000000000000000000000000",
"01",
"0000000000000000000000000000000000000000000000000000000000000000");
}

evm_test!{test_sar: test_sar_int}
fn test_sar(factory: super::Factory) {
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"0000000000000000000000000000000000000000000000000000000000000001",
"00",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"0000000000000000000000000000000000000000000000000000000000000001",
"01",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"8000000000000000000000000000000000000000000000000000000000000000",
"01",
"c000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"8000000000000000000000000000000000000000000000000000000000000000",
"ff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"8000000000000000000000000000000000000000000000000000000000000000",
"0100",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"8000000000000000000000000000000000000000000000000000000000000000",
"0101",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"00",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"01",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0100",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"0000000000000000000000000000000000000000000000000000000000000000",
"01",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"4000000000000000000000000000000000000000000000000000000000000000",
"fe",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"f8",
"000000000000000000000000000000000000000000000000000000000000007f");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"fe",
"0000000000000000000000000000000000000000000000000000000000000001");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ff",
"0000000000000000000000000000000000000000000000000000000000000000");
push_two_pop_one_constantinople_test(
&factory,
0x1d,
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0100",
"0000000000000000000000000000000000000000000000000000000000000000");
}

fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, push1: &str, push2: &str, result: &str) {
let mut push1 = push1.from_hex().unwrap();
let mut push2 = push2.from_hex().unwrap();
assert!(push1.len() <= 32 && push1.len() != 0);
assert!(push2.len() <= 32 && push2.len() != 0);

let mut code = Vec::new();
code.push(0x60 + ((push1.len() - 1) as u8));
code.append(&mut push1);
code.push(0x60 + ((push2.len() - 1) as u8));
code.append(&mut push2);
code.push(opcode);
code.append(&mut vec![0x60, 0x00, 0x55]);

let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_constantinople();

let _ = {
let mut vm = factory.create(&params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};

assert_store(&ext, 0, result);
}

fn assert_set_contains<T : Debug + Eq + PartialEq + Hash>(set: &HashSet<T>, val: &T) {
let contains = set.contains(val);
if !contains {
Expand All @@ -799,4 +1066,3 @@ fn assert_set_contains<T : Debug + Eq + PartialEq + Hash>(set: &HashSet<T>, val:
fn assert_store(ext: &FakeExt, pos: u64, val: &str) {
assert_eq!(ext.store.get(&H256::from(pos)).unwrap(), &H256::from_str(val).unwrap());
}

Loading

0 comments on commit 387e037

Please sign in to comment.