From c7a1baf5e5fe91d370c52d174b0469eac4aae494 Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Wed, 1 Aug 2018 11:52:36 +0200 Subject: [PATCH] Implement SAR opcode for Constantinople Closes #1104 --- eth/constants.py | 1 + eth/vm/forks/constantinople/opcodes.py | 5 ++ eth/vm/logic/arithmetic.py | 15 ++++ eth/vm/mnemonics.py | 1 + eth/vm/opcode_values.py | 1 + tests/core/opcodes/test_opcodes.py | 113 +++++++++++++++++++++++++ 6 files changed, 136 insertions(+) diff --git a/eth/constants.py b/eth/constants.py index a5a5932f18..941b9effbd 100644 --- a/eth/constants.py +++ b/eth/constants.py @@ -13,6 +13,7 @@ UINT_256_CEILING = 2**256 UINT_255_MAX = 2**255 - 1 UINT_255_CEILING = 2**255 +UINT_255_NEGATIVE_ONE = -1 + UINT_256_CEILING NULL_BYTE = b'\x00' EMPTY_WORD = NULL_BYTE * 32 diff --git a/eth/vm/forks/constantinople/opcodes.py b/eth/vm/forks/constantinople/opcodes.py index a3a5392f81..a63821c0a1 100644 --- a/eth/vm/forks/constantinople/opcodes.py +++ b/eth/vm/forks/constantinople/opcodes.py @@ -32,6 +32,11 @@ mnemonic=mnemonics.SHR, gas_cost=constants.GAS_VERYLOW, ), + opcode_values.SAR: as_opcode( + logic_fn=arithmetic.sar, + mnemonic=mnemonics.SAR, + gas_cost=constants.GAS_VERYLOW, + ), } CONSTANTINOPLE_OPCODES = merge( diff --git a/eth/vm/logic/arithmetic.py b/eth/vm/logic/arithmetic.py index a667ba4eae..f52aa628bd 100644 --- a/eth/vm/logic/arithmetic.py +++ b/eth/vm/logic/arithmetic.py @@ -205,3 +205,18 @@ def shr(computation): result = (value >> shift_length) & constants.UINT_256_MAX computation.stack_push(result) + + +def sar(computation): + """ + Arithmetic bitwise right shift + """ + shift_length, value = computation.stack_pop(num_items=2, type_hint=constants.UINT256) + value = unsigned_to_signed(value) + + if shift_length >= 256: + result = 0 if value >= 0 else constants.UINT_255_NEGATIVE_ONE + else: + result = (value >> shift_length) & constants.UINT_256_MAX + + computation.stack_push(result) diff --git a/eth/vm/mnemonics.py b/eth/vm/mnemonics.py index a830f37f1d..09611a4126 100644 --- a/eth/vm/mnemonics.py +++ b/eth/vm/mnemonics.py @@ -15,6 +15,7 @@ SIGNEXTEND = 'SIGNEXTEND' SHL = 'SHL' SHR = 'SHR' +SAR = 'SAR' # # Comparisons # diff --git a/eth/vm/opcode_values.py b/eth/vm/opcode_values.py index 264fe2dbe7..baac7f4024 100644 --- a/eth/vm/opcode_values.py +++ b/eth/vm/opcode_values.py @@ -31,6 +31,7 @@ BYTE = 0x1a SHL = 0x1b SHR = 0x1c +SAR = 0x1d # diff --git a/tests/core/opcodes/test_opcodes.py b/tests/core/opcodes/test_opcodes.py index 897ace3cee..5ecd0f0e21 100644 --- a/tests/core/opcodes/test_opcodes.py +++ b/tests/core/opcodes/test_opcodes.py @@ -265,3 +265,116 @@ def test_shr(vm_class, val1, val2, expected): result = computation.stack_pop(type_hint=constants.UINT256) assert encode_hex(pad32(int_to_big_endian(result))) == expected + + +@pytest.mark.parametrize( + # EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right + 'vm_class, val1, val2, expected', + ( + ( + ConstantinopleVM, + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x00', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ), + ( + ConstantinopleVM, + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x01', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ), + ( + ConstantinopleVM, + '0x8000000000000000000000000000000000000000000000000000000000000000', + '0x01', + '0xc000000000000000000000000000000000000000000000000000000000000000', + ), + ( + ConstantinopleVM, + '0x8000000000000000000000000000000000000000000000000000000000000000', + '0xff', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0x8000000000000000000000000000000000000000000000000000000000000000', + '0x0100', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0x8000000000000000000000000000000000000000000000000000000000000000', + '0x0101', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0x00', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0x01', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0xff', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0x0100', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + ), + ( + ConstantinopleVM, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x01', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ), + + ( + ConstantinopleVM, + '0x4000000000000000000000000000000000000000000000000000000000000000', + '0xfe', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ), + ( + ConstantinopleVM, + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0xf8', + '0x000000000000000000000000000000000000000000000000000000000000007f', + ), + ( + ConstantinopleVM, + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0xfe', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ), + ( + ConstantinopleVM, + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0xff', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ), + ( + ConstantinopleVM, + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0x0100', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ), + ) +) +def test_sar(vm_class, val1, val2, expected): + computation = prepare_computation(vm_class) + computation.stack_push(decode_hex(val1)) + computation.stack_push(decode_hex(val2)) + computation.opcodes[opcode_values.SAR](computation) + + result = computation.stack_pop(type_hint=constants.UINT256) + assert encode_hex(pad32(int_to_big_endian(result))) == expected