Precompiled contracts¶
EVM precompile semantics (YP Appendix E and the precompile EIPs). Sail selects the active address, validates the Ethereum input encoding, and returns the EVM-visible output; the host functions called here are only raw zkVM cryptographic accelerators.
Constants¶
The fixed offsets and lengths describe the canonical byte layouts accepted by each precompile; the field modulus and blob element count validate their respective algebraic inputs.
let ACCELERATOR_INPUT_MAX¶
let ACCELERATOR_INPUT_MAX : int(2097152) = 2097152let FIELD_ELEMENTS_PER_BLOB¶
let FIELD_ELEMENTS_PER_BLOB : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000001000)Interprets one full-width bitvector as an EVM word. This named boundary keeps protocol constants readable without hiding conversion chains.
function word_from_bits(value : bits(256)) -> word = {
let natural_value = unsigned(value);
u256(natural_value)
}The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)let BLS_MODULUS¶
let BLS_MODULUS : word = word_from_bits(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001)Interprets one full-width bitvector as an EVM word. This named boundary keeps protocol constants readable without hiding conversion chains.
function word_from_bits(value : bits(256)) -> word = {
let natural_value = unsigned(value);
u256(natural_value)
}The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)let BLAKE2F_INPUT_LENGTH¶
let BLAKE2F_INPUT_LENGTH : int(213) = 213let BLAKE2F_FINAL_BLOCK_OFFSET¶
let BLAKE2F_FINAL_BLOCK_OFFSET : int(212) = 212let BLAKE2F_OUTPUT_LENGTH¶
let BLAKE2F_OUTPUT_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHlet DOUBLE_WORD_BYTE_LENGTH : int(64) = 64let KZG_INPUT_LENGTH¶
let KZG_INPUT_LENGTH : int(192) = 192let KZG_COMMITMENT_OFFSET¶
let KZG_COMMITMENT_OFFSET : int(96) = 96let KZG_COMMITMENT_LENGTH¶
let KZG_COMMITMENT_LENGTH : int(48) = 48let BLS_FIELD_PADDING_LENGTH¶
let BLS_FIELD_PADDING_LENGTH : int(16) = 16let BLS_PADDED_FIELD_LENGTH¶
let BLS_PADDED_FIELD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHlet DOUBLE_WORD_BYTE_LENGTH : int(64) = 64let BLS_G1_POINT_LENGTH¶
let BLS_G1_POINT_LENGTH : int(128) = 128let BLS_G2_POINT_LENGTH¶
let BLS_G2_POINT_LENGTH : int(256) = 256let BLS_G2_FINAL_FIELD_OFFSET¶
let BLS_G2_FINAL_FIELD_OFFSET : int(192) = 192let BLS_G1_ADD_INPUT_LENGTH¶
let BLS_G1_ADD_INPUT_LENGTH : int(256) = 256let BLS_G1_MSM_ITEM_LENGTH¶
let BLS_G1_MSM_ITEM_LENGTH : int(160) = 160let BLS_G2_ADD_INPUT_LENGTH¶
let BLS_G2_ADD_INPUT_LENGTH : int(512) = 512let BLS_G2_MSM_ITEM_LENGTH¶
let BLS_G2_MSM_ITEM_LENGTH : int(288) = 288let BLS_PAIRING_ITEM_LENGTH¶
let BLS_PAIRING_ITEM_LENGTH : int(384) = 384let P256_INPUT_LENGTH¶
let P256_INPUT_LENGTH : int(160) = 160let BN254_PAIRING_ITEM_LENGTH¶
let BN254_PAIRING_ITEM_LENGTH : int(192) = 192let PRECOMPILE_WORD_LENGTH¶
let PRECOMPILE_WORD_LENGTH : int(32) = WORD_BYTE_LENGTHlet WORD_BYTE_LENGTH : int(32) = 32let PRECOMPILE_DOUBLE_WORD_LENGTH¶
let PRECOMPILE_DOUBLE_WORD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHlet DOUBLE_WORD_BYTE_LENGTH : int(64) = 64let PRECOMPILE_WORD_OFFSET¶
let PRECOMPILE_WORD_OFFSET : int(32) = 32let PRECOMPILE_DOUBLE_WORD_OFFSET¶
let PRECOMPILE_DOUBLE_WORD_OFFSET : int(64) = 64let ECRECOVER_S_OFFSET¶
let ECRECOVER_S_OFFSET : source_pointer = 96An absolute byte position in a named source region.
type source_pointer = range(0, default_host_region_bound)let TWO_COMPONENTS¶
let TWO_COMPONENTS : int(2) = 2let BLS_G2_POINT_OFFSET¶
let BLS_G2_POINT_OFFSET : int(128) = 128let PRECOMPILE_ADDRESS_1¶
let PRECOMPILE_ADDRESS_1 : address = address_from_nat(1)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_2¶
let PRECOMPILE_ADDRESS_2 : address = address_from_nat(2)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_3¶
let PRECOMPILE_ADDRESS_3 : address = address_from_nat(3)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_4¶
let PRECOMPILE_ADDRESS_4 : address = address_from_nat(4)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_5¶
let PRECOMPILE_ADDRESS_5 : address = address_from_nat(5)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_6¶
let PRECOMPILE_ADDRESS_6 : address = address_from_nat(6)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_7¶
let PRECOMPILE_ADDRESS_7 : address = address_from_nat(7)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_8¶
let PRECOMPILE_ADDRESS_8 : address = address_from_nat(8)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_9¶
let PRECOMPILE_ADDRESS_9 : address = address_from_nat(9)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_10¶
let PRECOMPILE_ADDRESS_10 : address = address_from_nat(10)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_11¶
let PRECOMPILE_ADDRESS_11 : address = address_from_nat(11)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_12¶
let PRECOMPILE_ADDRESS_12 : address = address_from_nat(12)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_13¶
let PRECOMPILE_ADDRESS_13 : address = address_from_nat(13)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_14¶
let PRECOMPILE_ADDRESS_14 : address = address_from_nat(14)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_15¶
let PRECOMPILE_ADDRESS_15 : address = address_from_nat(15)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_16¶
let PRECOMPILE_ADDRESS_16 : address = address_from_nat(16)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_17¶
let PRECOMPILE_ADDRESS_17 : address = address_from_nat(17)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)let PRECOMPILE_ADDRESS_256¶
let PRECOMPILE_ADDRESS_256 : address = address_from_nat(256)function address_from_nat(value) = {
let word_value = u256(value);
word_to_address(word_value)
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)type PrecompileResult¶
A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}function precompile_success¶
A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function precompile_failure¶
The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function accelerator_result¶
function accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }function copied_result¶
IDENTITY (0x04): the input, copied through the output buffer.
function copied_result(data : CalldataSlice) -> PrecompileResult = {
let output = freeze_output(data);
let input_length = calldata_slice_length(data);
if output.len == input_length then {
precompile_success(output)
} else {
precompile_failure()
}
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function boolean_result¶
A 32-byte 0/1 result word (pairing checks).
function boolean_result(value : bool) -> PrecompileResult = {
let encoded_word =
if value then WORD_ONE else WORD_ZERO;
let output = output_buffer_word(encoded_word);
precompile_success(output)
}Stores one word as the output (32-byte precompile results).
function output_buffer_word(value : word) -> OutputSlice = {
let stored = output_buffer_store_word(value);
if stored then {
output_buffer_slice(WORD_BYTE_LENGTH)
} else {
EMPTY_OUTPUT_SLICE
}
}A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }let WORD_ONE : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000001)let WORD_ZERO : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function precompile_active_at_fork¶
Whether address n is an active precompile at the current fork:
1–4 always; 5–8 from Byzantium; 9 from Istanbul; 10 from Cancun
(EIP-4844); 11–17 from Prague (EIP-2537); 0x100 from Osaka
(EIP-7951).
function precompile_active_at_fork(n : precompile_id) -> bool = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
match n {
NotPrecompile => false,
Ecrecover => true,
Sha256 => true,
Ripemd160 => true,
Identity => true,
Modexp => profile.fork >= Byzantium,
Bn254Add => profile.fork >= Byzantium,
Bn254Mul => profile.fork >= Byzantium,
Bn254Pairing => profile.fork >= Byzantium,
Blake2f => profile.fork >= Istanbul,
KzgPointEvaluation => profile.fork >= Cancun,
BlsG1Add => profile.fork >= Prague,
BlsG1Msm => profile.fork >= Prague,
BlsG2Add => profile.fork >= Prague,
BlsG2Msm => profile.fork >= Prague,
BlsPairing => profile.fork >= Prague,
BlsMapFpToG1 => profile.fork >= Prague,
BlsMapFp2ToG2 => profile.fork >= Prague,
P256Verify => profile.fork >= Osaka,
}
}EIP-140 REVERT, EIP-211 returndata; precompiles 0x05-0x08.
let Byzantium : int(byzantium_fork_value) = sizeof(byzantium_fork_value)EIP-1153/4844; precompiles 0x01-0x0a.
let Cancun : int(first_blob_fork_value) = sizeof(first_blob_fork_value)EIP-1344 CHAINID, EIP-2200 SSTORE metering.
let Istanbul : int(istanbul_fork_value) = sizeof(istanbul_fork_value)EIP-7883 modexp gas, EIP-7825 cap; precompile 0x100.
let Osaka : int(osaka_fork_value) = sizeof(osaka_fork_value)EIP-7623 calldata floor; BLS precompiles 0x0b-0x11.
let Prague : int(prague_fork_value) = sizeof(prague_fork_value)The active protocol policy and all gas limits derived from the executing header, selected together while decoding the stateless input.
register k_execution_profile : ExecutionProfile = DEFAULT_EXECUTION_PROFILEThe closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}The quantity alias carried by the precompile interpreters for the closed selector above.
type precompile_id = PrecompileIdfunction precompile_id_if_active¶
Returns the active precompile represented by target, or
NotPrecompile when it is an ordinary address at the current fork.
function precompile_id_if_active(candidate : precompile_id) -> precompile_id = {
let active = precompile_active_at_fork(candidate);
if active then {
candidate
} else {
NotPrecompile
}
}Whether address n is an active precompile at the current fork:
1–4 always; 5–8 from Byzantium; 9 from Istanbul; 10 from Cancun
(EIP-4844); 11–17 from Prague (EIP-2537); 0x100 from Osaka
(EIP-7951).
function precompile_active_at_fork(n : precompile_id) -> bool = {
let execution_profile = k_execution_profile;
let profile = execution_profile.protocol;
match n {
NotPrecompile => false,
Ecrecover => true,
Sha256 => true,
Ripemd160 => true,
Identity => true,
Modexp => profile.fork >= Byzantium,
Bn254Add => profile.fork >= Byzantium,
Bn254Mul => profile.fork >= Byzantium,
Bn254Pairing => profile.fork >= Byzantium,
Blake2f => profile.fork >= Istanbul,
KzgPointEvaluation => profile.fork >= Cancun,
BlsG1Add => profile.fork >= Prague,
BlsG1Msm => profile.fork >= Prague,
BlsG2Add => profile.fork >= Prague,
BlsG2Msm => profile.fork >= Prague,
BlsPairing => profile.fork >= Prague,
BlsMapFpToG1 => profile.fork >= Prague,
BlsMapFp2ToG2 => profile.fork >= Prague,
P256Verify => profile.fork >= Osaka,
}
}The closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}The quantity alias carried by the precompile interpreters for the closed selector above.
type precompile_id = PrecompileIdfunction precompile_id_for_address¶
Maps an address to its active precompile identifier; any other address,
including one whose precompile is not yet active at the current fork,
is NotPrecompile.
function precompile_id_for_address(bytes : address) -> precompile_id = {
match bytes {
_ if bytes == PRECOMPILE_ADDRESS_1 => precompile_id_if_active(Ecrecover),
_ if bytes == PRECOMPILE_ADDRESS_2 => precompile_id_if_active(Sha256),
_ if bytes == PRECOMPILE_ADDRESS_3 => precompile_id_if_active(Ripemd160),
_ if bytes == PRECOMPILE_ADDRESS_4 => precompile_id_if_active(Identity),
_ if bytes == PRECOMPILE_ADDRESS_5 => precompile_id_if_active(Modexp),
_ if bytes == PRECOMPILE_ADDRESS_6 => precompile_id_if_active(Bn254Add),
_ if bytes == PRECOMPILE_ADDRESS_7 => precompile_id_if_active(Bn254Mul),
_ if bytes == PRECOMPILE_ADDRESS_8 => precompile_id_if_active(Bn254Pairing),
_ if bytes == PRECOMPILE_ADDRESS_9 => precompile_id_if_active(Blake2f),
_ if bytes == PRECOMPILE_ADDRESS_10 => precompile_id_if_active(KzgPointEvaluation),
_ if bytes == PRECOMPILE_ADDRESS_11 => precompile_id_if_active(BlsG1Add),
_ if bytes == PRECOMPILE_ADDRESS_12 => precompile_id_if_active(BlsG1Msm),
_ if bytes == PRECOMPILE_ADDRESS_13 => precompile_id_if_active(BlsG2Add),
_ if bytes == PRECOMPILE_ADDRESS_14 => precompile_id_if_active(BlsG2Msm),
_ if bytes == PRECOMPILE_ADDRESS_15 => precompile_id_if_active(BlsPairing),
_ if bytes == PRECOMPILE_ADDRESS_16 => precompile_id_if_active(BlsMapFpToG1),
_ if bytes == PRECOMPILE_ADDRESS_17 => precompile_id_if_active(BlsMapFp2ToG2),
_ if bytes == PRECOMPILE_ADDRESS_256 => precompile_id_if_active(P256Verify),
_ => NotPrecompile,
}
}Maps an address to its active precompile identifier; any other address,
including one whose precompile is not yet active at the current fork,
is NotPrecompile.
function precompile_id_for_address(bytes : address) -> precompile_id = {
match bytes {
_ if bytes == PRECOMPILE_ADDRESS_1 => precompile_id_if_active(Ecrecover),
_ if bytes == PRECOMPILE_ADDRESS_2 => precompile_id_if_active(Sha256),
_ if bytes == PRECOMPILE_ADDRESS_3 => precompile_id_if_active(Ripemd160),
_ if bytes == PRECOMPILE_ADDRESS_4 => precompile_id_if_active(Identity),
_ if bytes == PRECOMPILE_ADDRESS_5 => precompile_id_if_active(Modexp),
_ if bytes == PRECOMPILE_ADDRESS_6 => precompile_id_if_active(Bn254Add),
_ if bytes == PRECOMPILE_ADDRESS_7 => precompile_id_if_active(Bn254Mul),
_ if bytes == PRECOMPILE_ADDRESS_8 => precompile_id_if_active(Bn254Pairing),
_ if bytes == PRECOMPILE_ADDRESS_9 => precompile_id_if_active(Blake2f),
_ if bytes == PRECOMPILE_ADDRESS_10 => precompile_id_if_active(KzgPointEvaluation),
_ if bytes == PRECOMPILE_ADDRESS_11 => precompile_id_if_active(BlsG1Add),
_ if bytes == PRECOMPILE_ADDRESS_12 => precompile_id_if_active(BlsG1Msm),
_ if bytes == PRECOMPILE_ADDRESS_13 => precompile_id_if_active(BlsG2Add),
_ if bytes == PRECOMPILE_ADDRESS_14 => precompile_id_if_active(BlsG2Msm),
_ if bytes == PRECOMPILE_ADDRESS_15 => precompile_id_if_active(BlsPairing),
_ if bytes == PRECOMPILE_ADDRESS_16 => precompile_id_if_active(BlsMapFpToG1),
_ if bytes == PRECOMPILE_ADDRESS_17 => precompile_id_if_active(BlsMapFp2ToG2),
_ if bytes == PRECOMPILE_ADDRESS_256 => precompile_id_if_active(P256Verify),
_ => NotPrecompile,
}
}Returns the active precompile represented by target, or
NotPrecompile when it is an ordinary address at the current fork.
function precompile_id_if_active(candidate : precompile_id) -> precompile_id = {
let active = precompile_active_at_fork(candidate);
if active then {
candidate
} else {
NotPrecompile
}
}let PRECOMPILE_ADDRESS_1 : address = address_from_nat(1)let PRECOMPILE_ADDRESS_10 : address = address_from_nat(10)let PRECOMPILE_ADDRESS_11 : address = address_from_nat(11)let PRECOMPILE_ADDRESS_12 : address = address_from_nat(12)let PRECOMPILE_ADDRESS_13 : address = address_from_nat(13)let PRECOMPILE_ADDRESS_14 : address = address_from_nat(14)let PRECOMPILE_ADDRESS_15 : address = address_from_nat(15)let PRECOMPILE_ADDRESS_16 : address = address_from_nat(16)let PRECOMPILE_ADDRESS_17 : address = address_from_nat(17)let PRECOMPILE_ADDRESS_2 : address = address_from_nat(2)let PRECOMPILE_ADDRESS_256 : address = address_from_nat(256)let PRECOMPILE_ADDRESS_3 : address = address_from_nat(3)let PRECOMPILE_ADDRESS_4 : address = address_from_nat(4)let PRECOMPILE_ADDRESS_5 : address = address_from_nat(5)let PRECOMPILE_ADDRESS_6 : address = address_from_nat(6)let PRECOMPILE_ADDRESS_7 : address = address_from_nat(7)let PRECOMPILE_ADDRESS_8 : address = address_from_nat(8)let PRECOMPILE_ADDRESS_9 : address = address_from_nat(9)The closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The quantity alias carried by the precompile interpreters for the closed selector above.
type precompile_id = PrecompileIdfunction run_ecrecover¶
ECRECOVER (0x01): recovers the signer address; any invalid input
yields a successful call with empty output.
function run_ecrecover(input : CalldataSlice) -> PrecompileResult = {
let v = slice_load(input, PRECOMPILE_WORD_OFFSET);
let v_27 = u256(27);
let v_28 = u256(28);
let valid_v = (v == v_27) | (v == v_28);
if valid_v then {
let parity : y_parity =
if v == v_27 then 0 else 1;
let message_word = slice_load(input, 0);
let message_hash = word_to_hash(message_word);
let r = slice_load(input, PRECOMPILE_DOUBLE_WORD_OFFSET);
let s = slice_load(input, ECRECOVER_S_OFFSET);
let recovered = ecrecover_addr(message_hash, parity, r, s);
if recovered.success then {
let address_word = address_to_word(recovered.address);
let output = output_buffer_word(address_word);
precompile_success(output)
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
}Embeds a canonical-order address into the low 160 bits of an EVM word.
function address_to_word(bytes : address) -> word =
unsigned(
bytes[0]
@ bytes[1]
@ bytes[2]
@ bytes[3]
@ bytes[4]
@ bytes[5]
@ bytes[6]
@ bytes[7]
@ bytes[8]
@ bytes[9]
@ bytes[10]
@ bytes[11]
@ bytes[12]
@ bytes[13]
@ bytes[14]
@ bytes[15]
@ bytes[16]
@ bytes[17]
@ bytes[18]
@ bytes[19],
)Recovers the signer address from (h, y_parity, r, s), returning
recovery success and the recovered address (used by EIP-7702).
function ecrecover_addr(h : hash, yparity : y_parity, r : word, s : word) -> AddressResult = {
host_ecrecover(h, yparity, r, s)
}Stores one word as the output (32-byte precompile results).
function output_buffer_word(value : word) -> OutputSlice = {
let stored = output_buffer_store_word(value);
if stored then {
output_buffer_slice(WORD_BYTE_LENGTH)
} else {
EMPTY_OUTPUT_SLICE
}
}A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }function u256(value) = valueSerializes an EVM word as a 32-byte big-endian digest.
function word_to_hash(value : word) -> hash = {
let zero_bytes = vector_init(32, 0x00);
var result : hash = B256(zero_bytes);
result[0] = get_slice_int(8, value, 248);
result[1] = get_slice_int(8, value, 240);
result[2] = get_slice_int(8, value, 232);
result[3] = get_slice_int(8, value, 224);
result[4] = get_slice_int(8, value, 216);
result[5] = get_slice_int(8, value, 208);
result[6] = get_slice_int(8, value, 200);
result[7] = get_slice_int(8, value, 192);
result[8] = get_slice_int(8, value, 184);
result[9] = get_slice_int(8, value, 176);
result[10] = get_slice_int(8, value, 168);
result[11] = get_slice_int(8, value, 160);
result[12] = get_slice_int(8, value, 152);
result[13] = get_slice_int(8, value, 144);
result[14] = get_slice_int(8, value, 136);
result[15] = get_slice_int(8, value, 128);
result[16] = get_slice_int(8, value, 120);
result[17] = get_slice_int(8, value, 112);
result[18] = get_slice_int(8, value, 104);
result[19] = get_slice_int(8, value, 96);
result[20] = get_slice_int(8, value, 88);
result[21] = get_slice_int(8, value, 80);
result[22] = get_slice_int(8, value, 72);
result[23] = get_slice_int(8, value, 64);
result[24] = get_slice_int(8, value, 56);
result[25] = get_slice_int(8, value, 48);
result[26] = get_slice_int(8, value, 40);
result[27] = get_slice_int(8, value, 32);
result[28] = get_slice_int(8, value, 24);
result[29] = get_slice_int(8, value, 16);
result[30] = get_slice_int(8, value, 8);
result[31] = get_slice_int(8, value, 0);
result
}let ECRECOVER_S_OFFSET : source_pointer = 96let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)let PRECOMPILE_DOUBLE_WORD_OFFSET : int(64) = 64let PRECOMPILE_WORD_OFFSET : int(32) = 32Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The parity bit used by transaction signatures.
type y_parity = range(0, 1)function run_sha256¶
SHA256 (0x02).
function run_sha256(input : CalldataSlice) -> PrecompileResult = {
let digest = sha256(input);
let digest_word = hash_to_word(digest);
let output = output_buffer_word(digest_word);
precompile_success(output)
}Interprets a digest as the corresponding big-endian EVM word.
function hash_to_word(bytes : hash) -> word =
unsigned(
bytes[0]
@ bytes[1]
@ bytes[2]
@ bytes[3]
@ bytes[4]
@ bytes[5]
@ bytes[6]
@ bytes[7]
@ bytes[8]
@ bytes[9]
@ bytes[10]
@ bytes[11]
@ bytes[12]
@ bytes[13]
@ bytes[14]
@ bytes[15]
@ bytes[16]
@ bytes[17]
@ bytes[18]
@ bytes[19]
@ bytes[20]
@ bytes[21]
@ bytes[22]
@ bytes[23]
@ bytes[24]
@ bytes[25]
@ bytes[26]
@ bytes[27]
@ bytes[28]
@ bytes[29]
@ bytes[30]
@ bytes[31],
)Stores one word as the output (32-byte precompile results).
function output_buffer_word(value : word) -> OutputSlice = {
let stored = output_buffer_store_word(value);
if stored then {
output_buffer_slice(WORD_BYTE_LENGTH)
} else {
EMPTY_OUTPUT_SLICE
}
}A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_ripemd160¶
RIPEMD160 (0x03): 20-byte digest, left-padded to 32.
function run_ripemd160(input : CalldataSlice) -> PrecompileResult = {
let success = accelerator_ripemd160(input);
accelerator_result(success, PRECOMPILE_WORD_LENGTH)
}function accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}RIPEMD-160 over the input, writing the left-padded 32-byte digest to the output arena; the flag reports acceleration success.
val accelerator_ripemd160 = impure { c: "accelerator_ripemd160" } : CalldataSlice -> boollet PRECOMPILE_WORD_LENGTH : int(32) = WORD_BYTE_LENGTHCalldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_modexp¶
MODEXP (0x05, EIP-198): arbitrary-precision modular
exponentiation; a zero-length modulus yields empty output, and inputs
beyond the accelerator bound fail the call.
function run_modexp(input : CalldataSlice) -> PrecompileResult = {
let base_len : word = pc_word(input, 0, 32);
let exponent_len : word = pc_word(input, 32, 32);
let modulus_len : word = pc_word(input, 64, 32);
if modulus_len == 0 then {
precompile_success(EMPTY_OUTPUT_SLICE)
} else if ACCELERATOR_INPUT_MAX
< base_len
| ACCELERATOR_INPUT_MAX
< exponent_len
| ACCELERATOR_INPUT_MAX
< modulus_len then {
precompile_failure()
} else {
let bounded_base : range(0, 2097152) = base_len;
let bounded_exponent : range(0, 2097152) = exponent_len;
let bounded_modulus : range(0, 2097152) = modulus_len;
let exponent_end : range(96, 96 + 2 * 2097152) = 96 + bounded_base + bounded_exponent;
let input_end : range(96, 96 + 3 * 2097152) = exponent_end + bounded_modulus;
if ACCELERATOR_INPUT_MAX < input_end then {
precompile_failure()
} else {
let success = accelerator_modexp(input, bounded_base, bounded_exponent, bounded_modulus);
accelerator_result(success, bounded_modulus)
}
}
}Modular exponentiation over the concatenated base, exponent, and modulus fields, writing a modulus-sized result; the flag reports acceleration success.
val accelerator_modexp = impure { c: "accelerator_modexp" } : (
CalldataSlice,
calldata_length,
calldata_length,
calldata_length,
) -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function pc_word(input, start, byte_count) = {
var value : word = ZERO_WORD;
let start_offset = start;
let count = byte_count;
let input_length = calldata_slice_length(input);
foreach (byte_index from 0 to 31) {
let offset : range(0, 31) = byte_index;
if offset < count then {
let next_byte =
if start_offset < input_length then {
let remaining = input_length - start_offset;
if offset < remaining then {
let cursor = start_offset + offset;
slice_byte(input, cursor)
} else {
0x00
}
} else {
0x00
};
let shifted = word_shift_left(value, 8);
let byte_value = unsigned(next_byte);
value = word_add(shifted, byte_value)
}
};
value
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }let ACCELERATOR_INPUT_MAX : int(2097152) = 2097152let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function pairing_result¶
Decodes an accelerator pairing result: values below two denote malformed input; the low-order parity of a valid value is the pairing outcome.
function pairing_result(result : pairing_check_result) -> PrecompileResult =
if result < 2 then {
precompile_failure()
} else {
let parity = tmod_nat(result, 2);
boolean_result(parity == 1)
}A 32-byte 0/1 result word (pairing checks).
function boolean_result(value : bool) -> PrecompileResult = {
let encoded_word =
if value then WORD_ONE else WORD_ZERO;
let output = output_buffer_word(encoded_word);
precompile_success(output)
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }Remainder specialized to a non-negative dividend and positive divisor. Singleton operands determine the exact natural-number result.
val tmod_nat = pure {smt: "mod", ocaml: "modulus", interpreter: "modulus", lem: "integerMod", c: "tmod_int", cpp: "tmod_int", systemverilog: "tmod_int", coq: "Z.rem", lean: "Nat.mod", _: "tmod_int"}: forall ('n : Int) ('m : Int), ('n >= 0 & 'm >= 1).
(int('n), int('m)) -> int(mod('n, 'm))A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}Encoded pairing-check status: values 0/1 are malformed input and
values 2/3 are valid input with a false/true pairing result.
type pairing_check_result = range(0, 3)function run_blake2f¶
BLAKE2F (0x09, EIP-152): the compression function; the input must
be exactly 213 bytes with a 0/1 final-block flag.
function run_blake2f(input : CalldataSlice) -> PrecompileResult = {
let final_byte = slice_byte(input, BLAKE2F_FINAL_BLOCK_OFFSET);
let input_length = calldata_slice_length(input);
if (input_length != BLAKE2F_INPUT_LENGTH) | ((final_byte != 0x00) & (final_byte != 0x01)) then {
precompile_failure()
} else {
let final_block : y_parity =
if final_byte == 0x00 then 0 else 1;
let rounds = pc_blake2_rounds(input);
let success = accelerator_blake2f(input, rounds, final_block);
accelerator_result(success, BLAKE2F_OUTPUT_LENGTH)
}
}The BLAKE2 compression function over its fixed-width input, using the supplied round count and final-block flag and writing the 64-byte result.
val accelerator_blake2f = impure { c: "accelerator_blake2f" } : (CalldataSlice, blake2_rounds, y_parity) -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}Reads the BLAKE2F rounds count: the big-endian 32-bit value in the
input's first four bytes (EIP-152).
function pc_blake2_rounds(input : CalldataSlice) -> blake2_rounds = {
let rounds_word = pc_word(input, 0, 4);
tmod_nat(rounds_word, 2 ^ 32)
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLAKE2F_FINAL_BLOCK_OFFSET : int(212) = 212let BLAKE2F_INPUT_LENGTH : int(213) = 213let BLAKE2F_OUTPUT_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHCalldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}The parity bit used by transaction signatures.
type y_parity = range(0, 1)function kzg_versioned_hash_matches¶
The EIP-4844 versioned-hash binding: the input's claimed hash must
equal 0x01 ‖ sha256(commitment)[1:].
function kzg_versioned_hash_matches(input : CalldataSlice) -> bool = {
let commitment = sub_slice(input, KZG_COMMITMENT_OFFSET, KZG_COMMITMENT_LENGTH);
let commitment_hash = sha256(commitment);
var expected = commitment_hash;
expected[0] = 0x01;
let claimed_word = slice_load(input, 0);
let claimed_hash = word_to_hash(claimed_word);
claimed_hash == expected
}Serializes an EVM word as a 32-byte big-endian digest.
function word_to_hash(value : word) -> hash = {
let zero_bytes = vector_init(32, 0x00);
var result : hash = B256(zero_bytes);
result[0] = get_slice_int(8, value, 248);
result[1] = get_slice_int(8, value, 240);
result[2] = get_slice_int(8, value, 232);
result[3] = get_slice_int(8, value, 224);
result[4] = get_slice_int(8, value, 216);
result[5] = get_slice_int(8, value, 208);
result[6] = get_slice_int(8, value, 200);
result[7] = get_slice_int(8, value, 192);
result[8] = get_slice_int(8, value, 184);
result[9] = get_slice_int(8, value, 176);
result[10] = get_slice_int(8, value, 168);
result[11] = get_slice_int(8, value, 160);
result[12] = get_slice_int(8, value, 152);
result[13] = get_slice_int(8, value, 144);
result[14] = get_slice_int(8, value, 136);
result[15] = get_slice_int(8, value, 128);
result[16] = get_slice_int(8, value, 120);
result[17] = get_slice_int(8, value, 112);
result[18] = get_slice_int(8, value, 104);
result[19] = get_slice_int(8, value, 96);
result[20] = get_slice_int(8, value, 88);
result[21] = get_slice_int(8, value, 80);
result[22] = get_slice_int(8, value, 72);
result[23] = get_slice_int(8, value, 64);
result[24] = get_slice_int(8, value, 56);
result[25] = get_slice_int(8, value, 48);
result[26] = get_slice_int(8, value, 40);
result[27] = get_slice_int(8, value, 32);
result[28] = get_slice_int(8, value, 24);
result[29] = get_slice_int(8, value, 16);
result[30] = get_slice_int(8, value, 8);
result[31] = get_slice_int(8, value, 0);
result
}let KZG_COMMITMENT_LENGTH : int(48) = 48let KZG_COMMITMENT_OFFSET : int(96) = 96Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}function run_kzg_point_evaluation¶
POINT_EVALUATION (0x0a, EIP-4844): verifies a KZG proof; success
returns the field-elements-per-blob and BLS modulus constants.
function run_kzg_point_evaluation(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != KZG_INPUT_LENGTH then {
precompile_failure()
} else {
let versioned_hash_matches = kzg_versioned_hash_matches(input);
let invalid_versioned_hash = not_bool(versioned_hash_matches);
if invalid_versioned_hash then {
precompile_failure()
} else {
let valid_proof = accelerator_kzg_point_evaluation(input);
if valid_proof then {
let output = output_buffer_words(FIELD_ELEMENTS_PER_BLOB, BLS_MODULUS);
precompile_success(output)
} else {
precompile_failure()
}
}
}
}KZG point-evaluation proof verification (EIP-4844) over the 192-byte versioned-hash/point/proof input, writing the constant 64-byte return payload on success.
val accelerator_kzg_point_evaluation = impure { c: "accelerator_kzg_point_evaluation" } : CalldataSlice -> boolThe byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}The EIP-4844 versioned-hash binding: the input's claimed hash must
equal 0x01 ‖ sha256(commitment)[1:].
function kzg_versioned_hash_matches(input : CalldataSlice) -> bool = {
let commitment = sub_slice(input, KZG_COMMITMENT_OFFSET, KZG_COMMITMENT_LENGTH);
let commitment_hash = sha256(commitment);
var expected = commitment_hash;
expected[0] = 0x01;
let claimed_word = slice_load(input, 0);
let claimed_hash = word_to_hash(claimed_word);
claimed_hash == expected
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))Stores two words as the output (64-byte precompile results, e.g.
ecrecover-style pairs).
function output_buffer_words(first : word, second : word) -> OutputSlice = {
let stored = output_buffer_store_words(first, second);
if stored then {
output_buffer_slice(DOUBLE_WORD_BYTE_LENGTH)
} else {
EMPTY_OUTPUT_SLICE
}
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }let BLS_MODULUS : word = word_from_bits(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001)let FIELD_ELEMENTS_PER_BLOB : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000001000)let KZG_INPUT_LENGTH : int(192) = 192Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function bls_g1_padding¶
function bls_g1_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count) &
slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
}function bls_g1_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count) &
slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
}function slice_strided_zero(s, start, stride, width, count) =
match s {
InputCalldata(bytes) => stateless_input_slice_strided_zero(bytes, start, stride, width, count),
MemoryCalldata(bytes) => memory_slice_strided_zero_value(bytes, start, stride, width, count),
}let BLS_FIELD_PADDING_LENGTH : int(16) = 16let BLS_PADDED_FIELD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHfunction bls_g2_padding¶
function bls_g2_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G1_POINT_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G2_FINAL_FIELD_OFFSET, stride, BLS_FIELD_PADDING_LENGTH, count)
}function bls_g2_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G1_POINT_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G2_FINAL_FIELD_OFFSET, stride, BLS_FIELD_PADDING_LENGTH, count)
}function slice_strided_zero(s, start, stride, width, count) =
match s {
InputCalldata(bytes) => stateless_input_slice_strided_zero(bytes, start, stride, width, count),
MemoryCalldata(bytes) => memory_slice_strided_zero_value(bytes, start, stride, width, count),
}let BLS_FIELD_PADDING_LENGTH : int(16) = 16let BLS_G1_POINT_LENGTH : int(128) = 128let BLS_G2_FINAL_FIELD_OFFSET : int(192) = 192let BLS_PADDED_FIELD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHfunction run_bls_g1_add¶
BLS12_G1ADD (0x0b, EIP-2537).
function run_bls_g1_add(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G1_ADD_INPUT_LENGTH then {
precompile_failure()
} else {
let valid_padding = bls_g1_padding(input, 0, BLS_G1_POINT_LENGTH, TWO_COMPONENTS);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g1_add(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}BLS12-381 G1 point addition (EIP-2537) over two 128-byte encoded
points, writing the 128-byte sum; false on a malformed input.
val accelerator_bls_g1_add = impure { c: "accelerator_bls_g1_add" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function bls_g1_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count) &
slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLS_G1_ADD_INPUT_LENGTH : int(256) = 256let BLS_G1_POINT_LENGTH : int(128) = 128let TWO_COMPONENTS : int(2) = 2Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_bls_g1_msm¶
BLS12_G1MSM (0x0c, EIP-2537): input is k 160-byte pairs.
function run_bls_g1_msm(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_G1_MSM_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let valid_padding = bls_g1_padding(input, 0, BLS_G1_MSM_ITEM_LENGTH, pairs);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g1_msm(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}BLS12-381 G1 multi-scalar multiplication (EIP-2537), writing the
128-byte result; false on a malformed input.
val accelerator_bls_g1_msm = impure { c: "accelerator_bls_g1_msm" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function bls_g1_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count) &
slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLS_G1_MSM_ITEM_LENGTH : int(160) = 160let BLS_G1_POINT_LENGTH : int(128) = 128Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_bls_g2_add¶
BLS12_G2ADD (0x0d, EIP-2537).
function run_bls_g2_add(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G2_ADD_INPUT_LENGTH then {
precompile_failure()
} else {
let valid_padding = bls_g2_padding(input, 0, BLS_G2_POINT_LENGTH, TWO_COMPONENTS);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g2_add(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}BLS12-381 G2 point addition (EIP-2537) over two 256-byte encoded
points, writing the 256-byte sum; false on a malformed input.
val accelerator_bls_g2_add = impure { c: "accelerator_bls_g2_add" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function bls_g2_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G1_POINT_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G2_FINAL_FIELD_OFFSET, stride, BLS_FIELD_PADDING_LENGTH, count)
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLS_G2_ADD_INPUT_LENGTH : int(512) = 512let BLS_G2_POINT_LENGTH : int(256) = 256let TWO_COMPONENTS : int(2) = 2Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_bls_g2_msm¶
BLS12_G2MSM (0x0e, EIP-2537): input is k 288-byte pairs.
function run_bls_g2_msm(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_G2_MSM_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let valid_padding = bls_g2_padding(input, 0, BLS_G2_MSM_ITEM_LENGTH, pairs);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g2_msm(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}BLS12-381 G2 multi-scalar multiplication (EIP-2537), writing the
256-byte result; false on a malformed input.
val accelerator_bls_g2_msm = impure { c: "accelerator_bls_g2_msm" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}function bls_g2_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G1_POINT_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G2_FINAL_FIELD_OFFSET, stride, BLS_FIELD_PADDING_LENGTH, count)
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLS_G2_MSM_ITEM_LENGTH : int(288) = 288let BLS_G2_POINT_LENGTH : int(256) = 256Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_bls_pairing¶
BLS12_PAIRING_CHECK (0x0f, EIP-2537): input is k 384-byte
G1×G2 pairs.
function run_bls_pairing(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_PAIRING_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let pair_count : source_length = pairs;
let valid_g1_padding = bls_g1_padding(input, 0, BLS_PAIRING_ITEM_LENGTH, pair_count);
let valid_g2_padding = bls_g2_padding(input, BLS_G2_POINT_OFFSET, BLS_PAIRING_ITEM_LENGTH, pair_count);
if valid_g1_padding & valid_g2_padding then {
let result = accelerator_bls_pairing(input);
pairing_result(result)
} else {
precompile_failure()
}
}
}BLS12-381 pairing check (EIP-2537): bit 1 flags input validity, bit 0 the pairing outcome (decoded by pairing_result).
val accelerator_bls_pairing = impure { c: "accelerator_bls_pairing" } : CalldataSlice -> pairing_check_resultfunction bls_g1_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count) &
slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
}function bls_g2_padding(input, base, stride, count) = {
slice_strided_zero(input, base, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_PADDED_FIELD_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G1_POINT_LENGTH, stride, BLS_FIELD_PADDING_LENGTH, count)
& slice_strided_zero(input, base + BLS_G2_FINAL_FIELD_OFFSET, stride, BLS_FIELD_PADDING_LENGTH, count)
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}Decodes an accelerator pairing result: values below two denote malformed input; the low-order parity of a valid value is the pairing outcome.
function pairing_result(result : pairing_check_result) -> PrecompileResult =
if result < 2 then {
precompile_failure()
} else {
let parity = tmod_nat(result, 2);
boolean_result(parity == 1)
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }let BLS_G2_POINT_OFFSET : int(128) = 128let BLS_PAIRING_ITEM_LENGTH : int(384) = 384Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}A byte length or regular-layout count derived from a source region.
type source_length = range(0, default_host_region_bound)function run_bls_map_fp_to_g1¶
BLS12_MAP_FP_TO_G1 (0x10, EIP-2537).
function run_bls_map_fp_to_g1(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_PADDED_FIELD_LENGTH then {
precompile_failure()
} else {
let valid_padding = slice_strided_zero(input, 0, BLS_PADDED_FIELD_LENGTH, BLS_FIELD_PADDING_LENGTH, 1);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_map_fp_to_g1(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}Maps a 64-byte encoded field element to a G1 point (EIP-2537), writing the 128-byte result.
val accelerator_bls_map_fp_to_g1 = impure { c: "accelerator_bls_map_fp_to_g1" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }function slice_strided_zero(s, start, stride, width, count) =
match s {
InputCalldata(bytes) => stateless_input_slice_strided_zero(bytes, start, stride, width, count),
MemoryCalldata(bytes) => memory_slice_strided_zero_value(bytes, start, stride, width, count),
}let BLS_FIELD_PADDING_LENGTH : int(16) = 16let BLS_G1_POINT_LENGTH : int(128) = 128let BLS_PADDED_FIELD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHCalldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_bls_map_fp2_to_g2¶
BLS12_MAP_FP2_TO_G2 (0x11, EIP-2537).
function run_bls_map_fp2_to_g2(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G1_POINT_LENGTH then {
precompile_failure()
} else {
let valid_padding = slice_strided_zero(
input,
0,
BLS_PADDED_FIELD_LENGTH,
BLS_FIELD_PADDING_LENGTH,
TWO_COMPONENTS,
);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_map_fp2_to_g2(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}Maps a 128-byte encoded Fp2 element to a G2 point (EIP-2537), writing the 256-byte result.
val accelerator_bls_map_fp2_to_g2 = impure { c: "accelerator_bls_map_fp2_to_g2" } : CalldataSlice -> boolfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}val not_bool = pure {coq: "negb", lean: "_lean_not", _: "not"}: forall ('p : Bool). bool('p) -> bool(not('p))The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }function slice_strided_zero(s, start, stride, width, count) =
match s {
InputCalldata(bytes) => stateless_input_slice_strided_zero(bytes, start, stride, width, count),
MemoryCalldata(bytes) => memory_slice_strided_zero_value(bytes, start, stride, width, count),
}let BLS_FIELD_PADDING_LENGTH : int(16) = 16let BLS_G1_POINT_LENGTH : int(128) = 128let BLS_G2_POINT_LENGTH : int(256) = 256let BLS_PADDED_FIELD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHlet TWO_COMPONENTS : int(2) = 2Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_p256_verify¶
P256VERIFY (0x100, EIP-7951): every malformed or invalid signature
is a successful call with empty output; only a valid signature
returns the word one.
function run_p256_verify(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
let verified =
if input_length == P256_INPUT_LENGTH then accelerator_p256_verify(input) else false;
if verified then {
let output = output_buffer_word(WORD_ONE);
precompile_success(output)
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
}P-256 (secp256r1) signature verification over the 160-byte
(hash, r, s, x, y) input (EIP-7951); the flag is the verification
outcome.
val accelerator_p256_verify = impure { c: "accelerator_p256_verify" } : CalldataSlice -> boolThe byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}Stores one word as the output (32-byte precompile results).
function output_buffer_word(value : word) -> OutputSlice = {
let stored = output_buffer_store_word(value);
if stored then {
output_buffer_slice(WORD_BYTE_LENGTH)
} else {
EMPTY_OUTPUT_SLICE
}
}A successful result carrying output.
function precompile_success(output : OutputSlice) -> PrecompileResult =
struct { success = true, output = output }let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)let P256_INPUT_LENGTH : int(160) = 160let WORD_ONE : word = word_from_bits(0x0000000000000000000000000000000000000000000000000000000000000001)Calldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}function run_precompile_slice¶
The precompile dispatch: address to implementation. Gas has already been charged by the caller (precompile_gas).
function run_precompile_slice(num : precompile_id, input : CalldataSlice) -> PrecompileResult =
match num {
NotPrecompile => precompile_failure(),
Ecrecover => run_ecrecover(input),
Sha256 => run_sha256(input),
Ripemd160 => run_ripemd160(input),
Identity => copied_result(input),
Modexp => run_modexp(input),
Bn254Add => {
let success = accelerator_bn254_add(input);
accelerator_result(success, PRECOMPILE_DOUBLE_WORD_LENGTH)
},
Bn254Mul => {
let success = accelerator_bn254_mul(input);
accelerator_result(success, PRECOMPILE_DOUBLE_WORD_LENGTH)
},
Bn254Pairing => {
let input_length = calldata_slice_length(input);
let item_length = BN254_PAIRING_ITEM_LENGTH;
if input_length == (input_length / item_length) * item_length then {
let result = accelerator_bn254_pairing(input);
pairing_result(result)
} else {
precompile_failure()
}
},
Blake2f => run_blake2f(input),
KzgPointEvaluation => run_kzg_point_evaluation(input),
BlsG1Add => run_bls_g1_add(input),
BlsG1Msm => run_bls_g1_msm(input),
BlsG2Add => run_bls_g2_add(input),
BlsG2Msm => run_bls_g2_msm(input),
BlsPairing => run_bls_pairing(input),
BlsMapFpToG1 => run_bls_map_fp_to_g1(input),
BlsMapFp2ToG2 => run_bls_map_fp2_to_g2(input),
P256Verify => run_p256_verify(input),
}BN254 G1 point addition over the 128-byte two-point input, writing
the 64-byte sum; false on a malformed point.
val accelerator_bn254_add = impure { c: "accelerator_bn254_add" } : CalldataSlice -> boolBN254 G1 scalar multiplication over the 96-byte point-and-scalar
input, writing the 64-byte product; false on a malformed point.
val accelerator_bn254_mul = impure { c: "accelerator_bn254_mul" } : CalldataSlice -> boolBN254 pairing check over the (G1, G2) pair list: bit 1 flags input
validity, bit 0 the pairing outcome (decoded by pairing_result).
val accelerator_bn254_pairing = impure { c: "accelerator_bn254_pairing" } : CalldataSlice -> pairing_check_resultfunction accelerator_result(success, output_len) =
if success then {
let output = output_buffer_slice(output_len);
precompile_success(output)
} else {
precompile_failure()
}The byte length of a calldata slice, independent of its provenance.
function calldata_slice_length(s : CalldataSlice) -> source_length =
match s {
InputCalldata(bytes) => bytes.len,
MemoryCalldata(bytes) => bytes.len,
}IDENTITY (0x04): the input, copied through the output buffer.
function copied_result(data : CalldataSlice) -> PrecompileResult = {
let output = freeze_output(data);
let input_length = calldata_slice_length(data);
if output.len == input_length then {
precompile_success(output)
} else {
precompile_failure()
}
}Decodes an accelerator pairing result: values below two denote malformed input; the low-order parity of a valid value is the pairing outcome.
function pairing_result(result : pairing_check_result) -> PrecompileResult =
if result < 2 then {
precompile_failure()
} else {
let parity = tmod_nat(result, 2);
boolean_result(parity == 1)
}The failed result (empty output; the call reports failure).
function precompile_failure() -> PrecompileResult =
struct { success = false, output = EMPTY_OUTPUT_SLICE }BLAKE2F (0x09, EIP-152): the compression function; the input must
be exactly 213 bytes with a 0/1 final-block flag.
function run_blake2f(input : CalldataSlice) -> PrecompileResult = {
let final_byte = slice_byte(input, BLAKE2F_FINAL_BLOCK_OFFSET);
let input_length = calldata_slice_length(input);
if (input_length != BLAKE2F_INPUT_LENGTH) | ((final_byte != 0x00) & (final_byte != 0x01)) then {
precompile_failure()
} else {
let final_block : y_parity =
if final_byte == 0x00 then 0 else 1;
let rounds = pc_blake2_rounds(input);
let success = accelerator_blake2f(input, rounds, final_block);
accelerator_result(success, BLAKE2F_OUTPUT_LENGTH)
}
}BLS12_G1ADD (0x0b, EIP-2537).
function run_bls_g1_add(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G1_ADD_INPUT_LENGTH then {
precompile_failure()
} else {
let valid_padding = bls_g1_padding(input, 0, BLS_G1_POINT_LENGTH, TWO_COMPONENTS);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g1_add(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}BLS12_G1MSM (0x0c, EIP-2537): input is k 160-byte pairs.
function run_bls_g1_msm(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_G1_MSM_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let valid_padding = bls_g1_padding(input, 0, BLS_G1_MSM_ITEM_LENGTH, pairs);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g1_msm(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}BLS12_G2ADD (0x0d, EIP-2537).
function run_bls_g2_add(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G2_ADD_INPUT_LENGTH then {
precompile_failure()
} else {
let valid_padding = bls_g2_padding(input, 0, BLS_G2_POINT_LENGTH, TWO_COMPONENTS);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g2_add(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}BLS12_G2MSM (0x0e, EIP-2537): input is k 288-byte pairs.
function run_bls_g2_msm(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_G2_MSM_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let valid_padding = bls_g2_padding(input, 0, BLS_G2_MSM_ITEM_LENGTH, pairs);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_g2_msm(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}BLS12_MAP_FP2_TO_G2 (0x11, EIP-2537).
function run_bls_map_fp2_to_g2(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_G1_POINT_LENGTH then {
precompile_failure()
} else {
let valid_padding = slice_strided_zero(
input,
0,
BLS_PADDED_FIELD_LENGTH,
BLS_FIELD_PADDING_LENGTH,
TWO_COMPONENTS,
);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_map_fp2_to_g2(input);
accelerator_result(success, BLS_G2_POINT_LENGTH)
}
}
}BLS12_MAP_FP_TO_G1 (0x10, EIP-2537).
function run_bls_map_fp_to_g1(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != BLS_PADDED_FIELD_LENGTH then {
precompile_failure()
} else {
let valid_padding = slice_strided_zero(input, 0, BLS_PADDED_FIELD_LENGTH, BLS_FIELD_PADDING_LENGTH, 1);
let invalid_padding = not_bool(valid_padding);
if invalid_padding then {
precompile_failure()
} else {
let success = accelerator_bls_map_fp_to_g1(input);
accelerator_result(success, BLS_G1_POINT_LENGTH)
}
}
}BLS12_PAIRING_CHECK (0x0f, EIP-2537): input is k 384-byte
G1×G2 pairs.
function run_bls_pairing(input : CalldataSlice) -> PrecompileResult = {
let length = calldata_slice_length(input);
let item_length = BLS_PAIRING_ITEM_LENGTH;
let pairs = length / item_length;
if (length == 0) | (length != pairs * item_length) then {
precompile_failure()
} else {
let pair_count : source_length = pairs;
let valid_g1_padding = bls_g1_padding(input, 0, BLS_PAIRING_ITEM_LENGTH, pair_count);
let valid_g2_padding = bls_g2_padding(input, BLS_G2_POINT_OFFSET, BLS_PAIRING_ITEM_LENGTH, pair_count);
if valid_g1_padding & valid_g2_padding then {
let result = accelerator_bls_pairing(input);
pairing_result(result)
} else {
precompile_failure()
}
}
}ECRECOVER (0x01): recovers the signer address; any invalid input
yields a successful call with empty output.
function run_ecrecover(input : CalldataSlice) -> PrecompileResult = {
let v = slice_load(input, PRECOMPILE_WORD_OFFSET);
let v_27 = u256(27);
let v_28 = u256(28);
let valid_v = (v == v_27) | (v == v_28);
if valid_v then {
let parity : y_parity =
if v == v_27 then 0 else 1;
let message_word = slice_load(input, 0);
let message_hash = word_to_hash(message_word);
let r = slice_load(input, PRECOMPILE_DOUBLE_WORD_OFFSET);
let s = slice_load(input, ECRECOVER_S_OFFSET);
let recovered = ecrecover_addr(message_hash, parity, r, s);
if recovered.success then {
let address_word = address_to_word(recovered.address);
let output = output_buffer_word(address_word);
precompile_success(output)
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
}POINT_EVALUATION (0x0a, EIP-4844): verifies a KZG proof; success
returns the field-elements-per-blob and BLS modulus constants.
function run_kzg_point_evaluation(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
if input_length != KZG_INPUT_LENGTH then {
precompile_failure()
} else {
let versioned_hash_matches = kzg_versioned_hash_matches(input);
let invalid_versioned_hash = not_bool(versioned_hash_matches);
if invalid_versioned_hash then {
precompile_failure()
} else {
let valid_proof = accelerator_kzg_point_evaluation(input);
if valid_proof then {
let output = output_buffer_words(FIELD_ELEMENTS_PER_BLOB, BLS_MODULUS);
precompile_success(output)
} else {
precompile_failure()
}
}
}
}MODEXP (0x05, EIP-198): arbitrary-precision modular
exponentiation; a zero-length modulus yields empty output, and inputs
beyond the accelerator bound fail the call.
function run_modexp(input : CalldataSlice) -> PrecompileResult = {
let base_len : word = pc_word(input, 0, 32);
let exponent_len : word = pc_word(input, 32, 32);
let modulus_len : word = pc_word(input, 64, 32);
if modulus_len == 0 then {
precompile_success(EMPTY_OUTPUT_SLICE)
} else if ACCELERATOR_INPUT_MAX
< base_len
| ACCELERATOR_INPUT_MAX
< exponent_len
| ACCELERATOR_INPUT_MAX
< modulus_len then {
precompile_failure()
} else {
let bounded_base : range(0, 2097152) = base_len;
let bounded_exponent : range(0, 2097152) = exponent_len;
let bounded_modulus : range(0, 2097152) = modulus_len;
let exponent_end : range(96, 96 + 2 * 2097152) = 96 + bounded_base + bounded_exponent;
let input_end : range(96, 96 + 3 * 2097152) = exponent_end + bounded_modulus;
if ACCELERATOR_INPUT_MAX < input_end then {
precompile_failure()
} else {
let success = accelerator_modexp(input, bounded_base, bounded_exponent, bounded_modulus);
accelerator_result(success, bounded_modulus)
}
}
}P256VERIFY (0x100, EIP-7951): every malformed or invalid signature
is a successful call with empty output; only a valid signature
returns the word one.
function run_p256_verify(input : CalldataSlice) -> PrecompileResult = {
let input_length = calldata_slice_length(input);
let verified =
if input_length == P256_INPUT_LENGTH then accelerator_p256_verify(input) else false;
if verified then {
let output = output_buffer_word(WORD_ONE);
precompile_success(output)
} else {
precompile_success(EMPTY_OUTPUT_SLICE)
}
}RIPEMD160 (0x03): 20-byte digest, left-padded to 32.
function run_ripemd160(input : CalldataSlice) -> PrecompileResult = {
let success = accelerator_ripemd160(input);
accelerator_result(success, PRECOMPILE_WORD_LENGTH)
}SHA256 (0x02).
function run_sha256(input : CalldataSlice) -> PrecompileResult = {
let digest = sha256(input);
let digest_word = hash_to_word(digest);
let output = output_buffer_word(digest_word);
precompile_success(output)
}let BN254_PAIRING_ITEM_LENGTH : int(192) = 192let PRECOMPILE_DOUBLE_WORD_LENGTH : int(64) = DOUBLE_WORD_BYTE_LENGTHCalldata is either the immutable top-level transaction input or a frozen range of the suspended caller's memory. The variants state the only two protocol-valid provenances instead of exposing the host's region enum.
union CalldataSlice = {
/* the immutable top-level transaction input */
InputCalldata : StatelessInputSlice,
/* a frozen range of the suspended caller's memory */
MemoryCalldata : EvmMemorySlice,
}The closed first-order selector for the precompile catalog. Availability, gas pricing, and execution are separate interpreters of this identifier so their protocol equations remain explicit without function-valued records.
enum PrecompileId = {
/* the closed sentinel: the address is not a precompiled contract */
NotPrecompile,
/* addresses 0x01-0x04: the original Frontier precompiles */
Ecrecover,
Sha256,
Ripemd160,
Identity,
/* address 0x05: EIP-198 modular exponentiation */
Modexp,
/* addresses 0x06-0x08: the EIP-196/EIP-197 BN254 curve precompiles */
Bn254Add,
Bn254Mul,
Bn254Pairing,
/* address 0x09: EIP-152 BLAKE2 compression */
Blake2f,
/* address 0x0a: EIP-4844 KZG point evaluation */
KzgPointEvaluation,
/* addresses 0x0b-0x11: the EIP-2537 BLS12-381 precompiles */
BlsG1Add,
BlsG1Msm,
BlsG2Add,
BlsG2Msm,
BlsPairing,
BlsMapFpToG1,
BlsMapFp2ToG2,
/* address 0x100: EIP-7951 secp256r1 signature verification */
P256Verify,
}A precompile's outcome: success and the EVM-visible output bytes. Failure consumes the frame's gas like any exceptional call.
struct PrecompileResult = {
success : bool,
output : OutputSlice
}The quantity alias carried by the precompile interpreters for the closed selector above.
type precompile_id = PrecompileId