EEVM.Precompiles.ModExp (eevm v0.1.0)

Copy Markdown View Source

Modular exponentiation precompile — address 0x05 (EIP-198).

Computes base^exp mod modulus over arbitrarily large integers.

Gas schedule (EIP-198, Byzantium through Istanbul)

gas = floor(multiplication_complexity * iteration_count / 20)

where multiplication_complexity of x = max(base_length, mod_length) is x^2 for x <= 64, x^2 / 4 + 96x - 3072 for x <= 1024, and x^2 / 16 + 480x - 199680 above, and iteration_count is computed as for EIP-2565 below.

Gas schedule (EIP-2565, Berlin through Prague)

gas = max(200, floor(multiplication_complexity * iteration_count / 3))

where:

  • multiplication_complexity = ceil(max(base_length, mod_length) / 8)^2 (equivalently ((max(base_length, mod_length) + 7) div 8)^2 in integer arithmetic).
  • iteration_count is derived from the exponent length and the top bits of the exponent:
    • if exp_length <= 32: 0 when exponent is zero, otherwise the 0-indexed highest set bit of the exponent.
    • if exp_length > 32: 8 * (exp_length - 32) + highest_bit_index(exp[0..31]) where the highest bit index of an all-zero 32-byte prefix is treated as 0.
    • the result is floored at 1.

Gas schedule (EIP-7883, Osaka onward)

gas = max(500, multiplication_complexity * iteration_count)

where:

  • multiplication_complexity is 16 when max(base_length, mod_length) <= 32, otherwise 2 * ceil(max(base_length, mod_length) / 8)^2.
  • iteration_count is computed as above, except the per-byte multiplier for exponents longer than 32 bytes is 16 instead of 8.

Input bounds (EIP-7823, Osaka onward)

If base_length, exp_length, or mod_length exceeds 1024 bytes, the call fails with {:error, :invalid_input}, consuming all gas it was given.

execute/3 picks the gas schedule and bounds from the hardfork it is given.

Gas is charged from the 96-byte header and at most the first 32 exponent bytes before any operand is read, so oversized declared lengths fail with out-of-gas instead of allocating.

Input format

  • bytes 0..31: base_length (big-endian uint256)
  • bytes 32..63: exp_length (big-endian uint256)
  • bytes 64..95: mod_length (big-endian uint256)
  • bytes 96..: concatenated base || exponent || modulus

If the input is shorter than required by those lengths, missing bytes are treated as zero (right-padding behavior).

Output is the big-endian value of base^exp mod modulus, left-padded with zeros to exactly mod_length bytes. If modulus == 0 or mod_length == 0, the precompile returns mod_length zero bytes.

Summary

Functions

Executes the ModExp precompile with the gas schedule and input bounds of hardfork (default: Cancun).

Functions

execute(input, gas_limit, hardfork \\ HardforkConfig.new(:cancun))

@spec execute(binary(), non_neg_integer(), EEVM.HardforkConfig.t()) ::
  {:ok, binary(), non_neg_integer()} | {:error, :out_of_gas | :invalid_input}

Executes the ModExp precompile with the gas schedule and input bounds of hardfork (default: Cancun).