def mulmod(evm: Evm) -> None: """ Modulo multiplication of the top 2 elements with the 3rd element. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `3`. OutOfGasError If `evm.gas_left` is less than `8`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_MID) x = Uint(pop(evm.stack)) y = Uint(pop(evm.stack)) z = Uint(pop(evm.stack)) if z == 0: result = U256(0) else: result = U256((x * y) % z) push(evm.stack, result)
def exp(evm: Evm) -> None: """ Exponential operation of the top 2 elements. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. """ base = Uint(pop(evm.stack)) exponent = Uint(pop(evm.stack)) gas_used = GAS_EXPONENTIATION if exponent != 0: # This is equivalent to 1 + floor(log(y, 256)). But in python the log # function is inaccurate leading to wrong results. exponent_bits = exponent.bit_length() exponent_bytes = (exponent_bits + 7) // 8 gas_used += GAS_EXPONENTIATION * exponent_bytes evm.gas_left = subtract_gas(evm.gas_left, gas_used) result = U256(pow(base, exponent, U256_CEIL_VALUE)) push(evm.stack, result)
def mod(evm: Evm) -> None: """ Modulo remainder of the top two elements of the stack. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `5`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW) x = pop(evm.stack) y = pop(evm.stack) if y == 0: remainder = U256(0) else: remainder = x % y push(evm.stack, remainder)
def smod(evm: Evm) -> None: """ Signed modulo remainder of the top two elements of the stack. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `5`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW) x = pop(evm.stack).to_signed() y = pop(evm.stack).to_signed() if y == 0: remainder = 0 else: remainder = get_sign(x) * (abs(x) % abs(y)) push(evm.stack, U256.from_signed(remainder))
def sdiv(evm: Evm) -> None: """ Signed integer division of the top two elements of the stack. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `5`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW) dividend = pop(evm.stack).to_signed() divisor = pop(evm.stack).to_signed() if divisor == 0: quotient = 0 elif dividend == -U255_CEIL_VALUE and divisor == -1: quotient = -U255_CEIL_VALUE else: sign = get_sign(dividend * divisor) quotient = sign * (abs(dividend) // abs(divisor)) push(evm.stack, U256.from_signed(quotient))
def div(evm: Evm) -> None: """ Integer division of the top two elements of the stack. Pushes the result back on the stack. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `5`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW) dividend = pop(evm.stack) divisor = pop(evm.stack) if divisor == 0: quotient = U256(0) else: quotient = dividend // divisor push(evm.stack, quotient)
def push_n(evm: Evm, num_bytes: int) -> None: """ Pushes a N-byte immediate onto the stack. Parameters ---------- evm : The current EVM frame. num_bytes : The number of immediate bytes to be read from the code and pushed to the stack. Raises ------ StackOverflowError If `len(stack)` is equals `1024`. OutOfGasError If `evm.gas_left` is less than `3`. """ assert evm.pc + num_bytes < len(evm.code) evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW) data_to_push = U256.from_be_bytes(evm.code[evm.pc + 1:evm.pc + num_bytes + 1]) push(evm.stack, data_to_push) evm.pc += num_bytes
def signextend(evm: Evm) -> None: """ Sign extend operation. In other words, extend a signed number which fits in N bytes to 32 bytes. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `5`. """ evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW) # byte_num would be 0-indexed when inserted to the stack. byte_num = pop(evm.stack) value = pop(evm.stack) if byte_num > 31: # Can't extend any further result = value else: # U256(0).to_be_bytes() gives b'' instead b'\x00'. # noqa: SC100 value_bytes = value.to_be_bytes() or b"\x00" # Now among the obtained value bytes, consider only # N `least significant bytes`, where N is `byte_num + 1`. value_bytes = value_bytes[len(value_bytes) - 1 - byte_num:] sign_bit = value_bytes[0] >> 7 if sign_bit == 0: result = U256.from_be_bytes(value_bytes) else: num_bytes_prepend = 32 - (byte_num + 1) result = U256.from_be_bytes( bytearray([0xFF] * num_bytes_prepend) + value_bytes) push(evm.stack, result)
def json_to_state(raw: Any) -> State: state = {} for (addr, acc_state) in raw.items(): account = Account( nonce=hex2uint(acc_state.get("nonce", "0x0")), balance=hex2uint(acc_state.get("balance", "0x0")), code=hex2bytes(acc_state.get("code", "")), storage={}, ) for (k, v) in acc_state.get("storage", {}).items(): account.storage[hex2bytes32(k)] = U256.from_be_bytes( hex2bytes32(v) ) state[hex2address(addr)] = account return state
def sstore(evm: Evm) -> None: """ Stores a value at a certain key in the current context's storage. Parameters ---------- evm : The current EVM frame. Raises ------ StackUnderflowError If `len(stack)` is less than `2`. OutOfGasError If `evm.gas_left` is less than `20000`. """ key = pop(evm.stack).to_be_bytes32() new_value = pop(evm.stack) current_value = evm.env.state[evm.current].storage.get(key, U256(0)) # TODO: SSTORE gas usage hasn't been tested yet. Testing this needs # other opcodes to be implemented. # Calculating the gas needed for the storage if new_value != 0 and current_value == 0: gas_cost = GAS_STORAGE_SET else: gas_cost = GAS_STORAGE_UPDATE evm.gas_left = subtract_gas(evm.gas_left, gas_cost) # TODO: Refund counter hasn't been tested yet. Testing this needs other # Opcodes to be implemented if new_value == 0 and current_value != 0: evm.refund_counter += GAS_STORAGE_CLEAR_REFUND if new_value == 0: # Deletes a k-v pair from dict if key is present, else does nothing evm.env.state[evm.current].storage.pop(key, None) else: evm.env.state[evm.current].storage[key] = new_value
def test_u256_to_be_bytes32_max_value() -> None: encoded = U256(2**256 - 1).to_be_bytes32() assert encoded == bytes([ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ])
def test_u256_rdivmod_overflow() -> None: with pytest.raises(ValueError): divmod(2**256, U256(2))
def test_u256_rdivmod_negative() -> None: with pytest.raises(ValueError): divmod(-5, U256(2))
def test_u256_divmod_negative() -> None: with pytest.raises(ValueError): divmod(U256(5), -2)
def test_u256_divmod_float() -> None: quotient, remainder = divmod(U256(5), 2.0) assert not isinstance(quotient, U256) assert not isinstance(remainder, U256) assert quotient == 2 assert remainder == 1
def test_u256_rdivmod_float() -> None: quotient, remainder = divmod(5.0, U256(2)) assert not isinstance(quotient, U256) assert not isinstance(remainder, U256) assert quotient == 2 assert remainder == 1
def test_u256_rpow_overflow() -> None: with pytest.raises(ValueError): (2**256)**U256(2)
def test_u256_pow_modulo_negative() -> None: with pytest.raises(ValueError): pow(U256(4), 2, -3)
def test_u256_pow_modulo() -> None: value = pow(U256(4), 2, 3) assert isinstance(value, U256) assert value == 1
def test_u256_pow_overflow() -> None: with pytest.raises(ValueError): U256(340282366920938463463374607431768211456)**3
def test_u256_pow() -> None: value = U256(3)**2 assert isinstance(value, U256) assert value == 9
def test_u256_imod() -> None: value = U256(5) value %= 4 assert isinstance(value, U256) assert value == 1
def test_u256_pow_negative() -> None: with pytest.raises(ValueError): U256(3)**-2
def test_u256_imod_overflow() -> None: value = U256(5) with pytest.raises(ValueError): value %= 2**256
def test_u256_pow_modulo_overflow() -> None: with pytest.raises(ValueError): pow(U256(4), 2, 2**257)
def test_u256_imod_negative() -> None: value = U256(5) with pytest.raises(ValueError): value %= -4
def test_u256_rpow() -> None: value = 3**U256(2) assert isinstance(value, U256) assert value == 9
def test_u256_imod_float() -> None: value = U256(5) value %= 1.0 # type: ignore assert not isinstance(value, int) assert value == 0.0
.. contents:: Table of Contents :backlinks: none :local: Introduction ------------ EVM gas constants and calculators. """ from ethereum.base_types import U256 from .error import OutOfGasError GAS_VERY_LOW = U256(3) GAS_STORAGE_SET = U256(20000) GAS_STORAGE_UPDATE = U256(5000) GAS_STORAGE_CLEAR_REFUND = U256(15000) GAS_LOW = U256(5) GAS_MID = U256(8) GAS_EXPONENTIATION = U256(10) def subtract_gas(gas_left: U256, amount: U256) -> U256: """ Subtracts `amount` from `gas_left`. Parameters ---------- gas_left :
def test_u256_divmod_overflow() -> None: with pytest.raises(ValueError): divmod(U256(5), 2**256)