The guest entry point¶
The zkVM guest's main: decode the stateless input, verify the payload,
and emit the public validation result. Fatal failures publish the invalid
result and terminate inside fatal_error; only success returns here.
function fatal_error_set_input¶
function fatal_error_set_input(_input_ref) = ()function fatal_error_set_input(_input_ref) = ()function main¶
Decodes, verifies, and reports the successful validation result.
function main() -> unit = {
scratch_reset();
validation_debug_reset();
let input_bytes = stateless_input();
let input_ref = decode_stateless_input_ref(input_bytes);
fatal_error_set_input(input_ref);
verify_stateless_payload(input_ref);
write_validation_result(input_ref, true)
}Resolves the input's offset tables into a
StatelessInputRef, validating the schema id
and every region bound; a malformed frame is InvalidConfig.
function decode_stateless_input_ref(input : StatelessInputSlice) -> StatelessInputRef = {
let fixed_length = STATELESS_INPUT_FIXED_LENGTH;
let body_offset_value = SSZ_BODY;
let input_fields : StatelessInputSliceAtLeast(18) =
if fixed_length <= input.len then input else fatal_error(InvalidConfig);
let schema_version = slice_byte(input_fields, 1);
if schema_version != 0x01 then {
fatal_error(InvalidConfig)
};
let schema_fork = slice_byte(input_fields, 0);
let schema_matches = schema_protocol_profile_forwards_matches(schema_fork);
let schema_mismatch = not_bool(schema_matches);
if schema_mismatch then {
fatal_error(InvalidConfig)
};
let protocol : ProtocolProfile = schema_protocol_profile(schema_fork);
let body_bytes = slice_suffix(input_fields, body_offset_value);
let body = ssz_container_bytes(body_bytes, STATELESS_INPUT_BODY_FIXED_LENGTH);
let container_start : int(0) = 0;
let new_payload_request_position = ssz_field_offset(container_start, IN_NPR_OFF);
let new_payload_request_offset = ssz_u32(body, new_payload_request_position);
let witness_position = ssz_field_offset(container_start, IN_WITNESS_OFF);
let witness_offset = ssz_u32(body, witness_position);
let chain_config_position = ssz_field_offset(container_start, IN_CHAIN_CONFIG_OFF);
let chain_config_offset = ssz_u32(body, chain_config_position);
let public_keys_position = ssz_field_offset(container_start, IN_PUBLIC_KEYS_OFF);
let public_keys_offset = ssz_u32(body, public_keys_position);
let body_fixed_length = STATELESS_INPUT_BODY_FIXED_LENGTH;
if new_payload_request_offset != body_fixed_length then {
fatal_error(InvalidConfig)
};
let body_cursor = ssz_container_cursor(body, STATELESS_INPUT_BODY_FIXED_LENGTH);
let (new_payload_request_bytes, body_after_payload_request) = ssz_take(body_cursor, witness_offset);
let new_payload_request = ssz_container_bytes(new_payload_request_bytes, NEW_PAYLOAD_REQUEST_FIXED_LENGTH);
let (execution_witness_bytes, body_after_witness) = ssz_take(body_after_payload_request, chain_config_offset);
let execution_witness = ssz_container_bytes(execution_witness_bytes, EXECUTION_WITNESS_FIXED_LENGTH);
let (chain_config, body_after_chain_config) = ssz_take(body_after_witness, public_keys_offset);
let public_keys = ssz_finish(body_after_chain_config);
let npr_start : int(0) = 0;
let payload_position = ssz_field_offset(npr_start, NPR_PAYLOAD_OFF);
let payload_offset = ssz_u32(new_payload_request, payload_position);
let versioned_hashes_position = ssz_field_offset(npr_start, NPR_VHASHES_OFF);
let versioned_hashes_offset = ssz_u32(new_payload_request, versioned_hashes_position);
let requests_position = ssz_field_offset(npr_start, NPR_REQUESTS_OFF);
let requests_offset = ssz_u32(new_payload_request, requests_position);
let npr_fixed_length = NEW_PAYLOAD_REQUEST_FIXED_LENGTH;
if payload_offset != npr_fixed_length then {
fatal_error(InvalidConfig)
};
let npr_cursor = ssz_container_cursor(new_payload_request, NEW_PAYLOAD_REQUEST_FIXED_LENGTH);
let (execution_payload_bytes, npr_after_payload) = ssz_take(npr_cursor, versioned_hashes_offset);
let (versioned_hashes, npr_after_versioned_hashes) = ssz_take(npr_after_payload, requests_offset);
let execution_requests_bytes = ssz_finish(npr_after_versioned_hashes);
let execution_requests = ssz_container_bytes(execution_requests_bytes, EXECUTION_REQUESTS_FIXED_LENGTH);
let execution_payload = ssz_container_bytes(execution_payload_bytes, EXECUTION_PAYLOAD_FIXED_LENGTH);
let payload_start : int(0) = 0;
let extra_data_position = ssz_field_offset(payload_start, PL_EXTRA_OFF);
let extra_data_offset = ssz_u32(execution_payload, extra_data_position);
let transactions_position = ssz_field_offset(payload_start, PL_TXS_OFF);
let transactions_offset = ssz_u32(execution_payload, transactions_position);
let withdrawals_position = ssz_field_offset(payload_start, PL_WDS_OFF);
let withdrawals_offset = ssz_u32(execution_payload, withdrawals_position);
let block_access_list_position = ssz_field_offset(payload_start, PL_BAL_OFF);
let block_access_list_offset = ssz_u32(execution_payload, block_access_list_position);
let payload_fixed_length = EXECUTION_PAYLOAD_FIXED_LENGTH;
if extra_data_offset != payload_fixed_length then {
fatal_error(InvalidConfig)
};
let payload_cursor = ssz_container_cursor(execution_payload, EXECUTION_PAYLOAD_FIXED_LENGTH);
let (extra_data_bytes, payload_after_extra_data) = ssz_take(payload_cursor, transactions_offset);
let (transaction_bytes, payload_after_transactions) = ssz_take(payload_after_extra_data, withdrawals_offset);
let transactions = ssz_bounded_variable_list_ref(
transaction_bytes,
MAX_TRANSACTIONS_PER_PAYLOAD,
MAX_TRANSACTION_LENGTH,
);
let (withdrawal_bytes, payload_after_withdrawals) = ssz_take(payload_after_transactions, block_access_list_offset);
let withdrawals = ssz_bounded_fixed_list_ref(withdrawal_bytes, WD_SIZE, MAX_WITHDRAWALS_PER_PAYLOAD);
let block_access_list_bytes = ssz_finish(payload_after_withdrawals);
let extra_data : StatelessInputSliceAtMost(extra_data_length_bound) =
if extra_data_bytes.len <= MAX_EXTRA_DATA_LENGTH then extra_data_bytes else fatal_error(InvalidConfig);
let block_access_list : StatelessInputSliceAtMost(block_access_list_length_bound) =
if block_access_list_bytes.len <= MAX_BLOCK_ACCESS_LIST_LENGTH
then block_access_list_bytes
else fatal_error(InvalidConfig);
let requests_start : int(0) = 0;
let deposits_position = ssz_field_offset(requests_start, REQ_DEPOSITS_OFF);
let deposits_offset = ssz_u32(execution_requests, deposits_position);
let withdrawal_requests_position = ssz_field_offset(requests_start, REQ_WITHDRAWALS_OFF);
let withdrawal_requests_offset = ssz_u32(execution_requests, withdrawal_requests_position);
let consolidation_requests_position = ssz_field_offset(requests_start, REQ_CONSOLIDATIONS_OFF);
let consolidation_requests_offset = ssz_u32(execution_requests, consolidation_requests_position);
let builder_deposit_requests_position = ssz_field_offset(requests_start, REQ_BUILDER_DEPOSITS_OFF);
let builder_deposit_requests_offset = ssz_u32(execution_requests, builder_deposit_requests_position);
let builder_exit_requests_position = ssz_field_offset(requests_start, REQ_BUILDER_EXITS_OFF);
let builder_exit_requests_offset = ssz_u32(execution_requests, builder_exit_requests_position);
let requests_fixed_length = EXECUTION_REQUESTS_FIXED_LENGTH;
if deposits_offset != requests_fixed_length then {
fatal_error(InvalidConfig)
};
let requests_cursor = ssz_container_cursor(execution_requests, EXECUTION_REQUESTS_FIXED_LENGTH);
let (deposits, requests_after_deposits) = ssz_take(requests_cursor, withdrawal_requests_offset);
let (withdrawal_requests, requests_after_withdrawals) = ssz_take(
requests_after_deposits,
consolidation_requests_offset,
);
let (consolidation_requests, requests_after_consolidations) = ssz_take(
requests_after_withdrawals,
builder_deposit_requests_offset,
);
let (builder_deposit_requests, requests_after_builder_deposits) = ssz_take(
requests_after_consolidations,
builder_exit_requests_offset,
);
let builder_exit_requests = ssz_finish(requests_after_builder_deposits);
…function fatal_error_set_input(_input_ref) = ()Empties the arena (per-block lifetime).
function scratch_reset() -> unit = {
scratch_arena = EMPTY_SCRATCH_SLICE;
host_scratch_truncate(0)
}The complete private input supplied to the stateless validator. The host acquires its pointer once and all derived spans reuse offsets into it.
val stateless_input = impure { c: "stateless_input" } : unit -> StatelessInputSliceClears the recorded failure at the start of a validation attempt.
function validation_debug_reset() -> unit = ()The stateless verification pipeline: decode the semantic envelope,
index the witness, validate the payload commitments, execute the
block body one transaction at a time, and validate the execution results.
Any failure terminates through fatal_error; normal return means valid.
function verify_stateless_payload(input_ref : StatelessInputRef) -> unit = {
scratch_reset();
let input = decode_stateless_input(input_ref);
let witness = index_execution_witness(input_ref);
validate_execution_payload(input, input_ref, witness);
let block = input.payload.block;
let result = execute_block_body(block.body, input_ref);
validate_executed_block(block, result)
}Emits the full public output for a decoded input: the request root computed from the input itself, the verdict, and the input's chain configuration echoed byte for byte.
function write_validation_result(input_ref : StatelessInputRef, success : bool) -> unit = {
let root = htr_new_payload_request(input_ref);
commit_validation_result(root, success, input_ref.chain_config)
}