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 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 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 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_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_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_rdivmod_overflow() -> None: with pytest.raises(ValueError): divmod(2**256, U256(2))
def test_u256_mod_overflow() -> None: with pytest.raises(ValueError): U256(5) % (2**256)
def test_u256_rmod_float() -> None: value = (6.0) % U256(5) assert not isinstance(value, int) assert value == 1.0
def test_u256_rmod() -> None: value = 6 % U256(5) assert isinstance(value, U256) assert value == 1
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_mod_negative() -> None: with pytest.raises(ValueError): U256(5) % (-4)
def test_u256_rdivmod_negative() -> None: with pytest.raises(ValueError): divmod(-5, U256(2))
def test_u256_mod_float() -> None: value = U256(5) % (1.0) assert not isinstance(value, int) assert value == 0.0
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)