Receipt RLP codec¶
The logs-bloom construction (YP ยง4.4.1) and the receipts trie (EIP-2718 typed receipt encoding).
function topics_rlp_content_size¶
Sizes the RLP content of one retained record's topic list.
function topics_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var topic : log_store_index = 0;
let topic_count = log_topics_count(index);
let word_size = rlp_word_size();
let encoded_word_length = rlp_scratch_small_length(word_size);
while topic < topic_count termination_measure(topic_count - topic) do {
size = rlp_scratch_length_add(size, encoded_word_length);
topic = log_store_index_increment(topic)
};
size
}Advances a valid log cursor without fixed-width wrapping.
function log_store_index_increment(value : log_store_index) -> log_store_index =
if value < sizeof(log_store_index_bound) then {
value + 1
} else {
assert(false, "log store index overflow");
0
}Number of topics attached to an indexed retained log.
val log_topics_count = impure { c: "log_topic_count" } : log_store_index -> log_store_indexfunction rlp_scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
fatal_error(RlpDecode)
}function rlp_scratch_small_length(value) = valuefunction rlp_word_size() -> int(33) = RLP_ENCODED_WORD_LENGTHA position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function topics_rlp_size¶
Sizes one retained record's topic list with its list prefix.
function topics_rlp_size(index : log_store_index) -> rlp_scratch_length = {
let content_size = topics_rlp_content_size(index);
rlp_scratch_list_size(content_size)
}Adds the canonical list prefix to a materializable RLP content length.
function rlp_scratch_list_size(content_len : rlp_scratch_length) -> rlp_scratch_length = {
let prefix_size = rlp_length_prefix_len(content_len);
let prefix_length = rlp_scratch_small_length(prefix_size);
rlp_scratch_length_add(content_len, prefix_length)
}Sizes the RLP content of one retained record's topic list.
function topics_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var topic : log_store_index = 0;
let topic_count = log_topics_count(index);
let word_size = rlp_word_size();
let encoded_word_length = rlp_scratch_small_length(word_size);
while topic < topic_count termination_measure(topic_count - topic) do {
size = rlp_scratch_length_add(size, encoded_word_length);
topic = log_store_index_increment(topic)
};
size
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function log_entry_rlp_content_size¶
Sizes one retained log entry.
function log_entry_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
let address_size = rlp_addr_size();
let address_length = rlp_scratch_small_length(address_size);
let topics_length = topics_rlp_size(index);
let data = read_log_data(index);
let data_length = rlp_scratch_slice_size(data);
let address_and_topics_length = rlp_scratch_length_add(address_length, topics_length);
rlp_scratch_length_add(address_and_topics_length, data_length)
}Returns the source-backed payload of one retained log record.
function read_log_data(index : log_store_index) -> LogDataSlice = {
let off = log_data_offset(index);
let len = log_data_length(index);
if len <= sizeof(log_data_region_bound) - off then {
log_data_slice(off, len)
} else {
assert(false, "log data slice overflow");
EMPTY_LOG_DATA_SLICE
}
}function rlp_addr_size() -> int(21) = RLP_ENCODED_ADDRESS_LENGTHfunction rlp_scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
fatal_error(RlpDecode)
}function rlp_scratch_small_length(value) = valueSizes one retained record's topic list with its list prefix.
function topics_rlp_size(index : log_store_index) -> rlp_scratch_length = {
let content_size = topics_rlp_content_size(index);
rlp_scratch_list_size(content_size)
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function log_entry_rlp_size¶
Sizes one retained log entry with its list prefix.
function log_entry_rlp_size(index : log_store_index) -> rlp_scratch_length = {
let content_size = log_entry_rlp_content_size(index);
rlp_scratch_list_size(content_size)
}Sizes one retained log entry.
function log_entry_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
let address_size = rlp_addr_size();
let address_length = rlp_scratch_small_length(address_size);
let topics_length = topics_rlp_size(index);
let data = read_log_data(index);
let data_length = rlp_scratch_slice_size(data);
let address_and_topics_length = rlp_scratch_length_add(address_length, topics_length);
rlp_scratch_length_add(address_and_topics_length, data_length)
}Adds the canonical list prefix to a materializable RLP content length.
function rlp_scratch_list_size(content_len : rlp_scratch_length) -> rlp_scratch_length = {
let prefix_size = rlp_length_prefix_len(content_len);
let prefix_length = rlp_scratch_small_length(prefix_size);
rlp_scratch_length_add(content_len, prefix_length)
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function logs_rlp_content_size¶
Sizes the RLP content of a retained log range.
function logs_rlp_content_size(logs : LogSeriesRef) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var offset : log_store_index = 0;
while offset < logs.count termination_measure(logs.count - offset) do {
let index = log_store_index_add(logs.start, offset);
let entry_size = log_entry_rlp_size(index);
size = rlp_scratch_length_add(size, entry_size);
offset = log_store_index_increment(offset)
};
size
}Sizes one retained log entry with its list prefix.
function log_entry_rlp_size(index : log_store_index) -> rlp_scratch_length = {
let content_size = log_entry_rlp_content_size(index);
rlp_scratch_list_size(content_size)
}Adds a relative log offset to its series start without wrapping.
function log_store_index_add(left : log_store_index, right : log_store_index) -> log_store_index =
if right <= sizeof(log_store_index_bound) - left then {
left + right
} else {
assert(false, "log store index overflow");
0
}Advances a valid log cursor without fixed-width wrapping.
function log_store_index_increment(value : log_store_index) -> log_store_index =
if value < sizeof(log_store_index_bound) then {
value + 1
} else {
assert(false, "log store index overflow");
0
}function rlp_scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
fatal_error(RlpDecode)
}A consecutive transaction-local view into the block-lifetime host log store. Reverted frame records are removed before this view is captured.
struct LogSeriesRef = {
start : log_store_index,
count : log_store_index,
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function logs_rlp_size¶
Sizes a retained log range with its list prefix.
function logs_rlp_size(logs : LogSeriesRef) -> rlp_scratch_length = {
let content_size = logs_rlp_content_size(logs);
rlp_scratch_list_size(content_size)
}Sizes the RLP content of a retained log range.
function logs_rlp_content_size(logs : LogSeriesRef) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var offset : log_store_index = 0;
while offset < logs.count termination_measure(logs.count - offset) do {
let index = log_store_index_add(logs.start, offset);
let entry_size = log_entry_rlp_size(index);
size = rlp_scratch_length_add(size, entry_size);
offset = log_store_index_increment(offset)
};
size
}Adds the canonical list prefix to a materializable RLP content length.
function rlp_scratch_list_size(content_len : rlp_scratch_length) -> rlp_scratch_length = {
let prefix_size = rlp_length_prefix_len(content_len);
let prefix_length = rlp_scratch_small_length(prefix_size);
rlp_scratch_length_add(content_len, prefix_length)
}A consecutive transaction-local view into the block-lifetime host log store. Reverted frame records are removed before this view is captured.
struct LogSeriesRef = {
start : log_store_index,
count : log_store_index,
}An RLP byte count that can be materialized in the scratch arena. The host scratch-region limit is enforced while recursive collection totals are accumulated.
type rlp_scratch_length = range(0, scratch_region_bound)function rlp_write_topics¶
Writes one retained record's topic list.
function rlp_write_topics(index : log_store_index) -> unit = {
let content_size = topics_rlp_content_size(index);
rlp_write_list_prefix(content_size);
var topic : log_store_index = 0;
let topic_count = log_topics_count(index);
while topic < topic_count termination_measure(topic_count - topic) do {
let value = log_topic(index, topic);
rlp_write_word(value);
topic = log_store_index_increment(topic)
}
}Advances a valid log cursor without fixed-width wrapping.
function log_store_index_increment(value : log_store_index) -> log_store_index =
if value < sizeof(log_store_index_bound) then {
value + 1
} else {
assert(false, "log store index overflow");
0
}Reads one topic from an indexed retained log.
val log_topic = impure { c: "log_topic" } : (log_store_index, log_store_index) -> wordNumber of topics attached to an indexed retained log.
val log_topics_count = impure { c: "log_topic_count" } : log_store_index -> log_store_indexfunction rlp_write_list_prefix(content_len) = {
if content_len <= RLP_SHORT_LENGTH_LIMIT then {
let length_byte = rlp_length_byte(content_len);
let prefix = add_bits(0xc0, length_byte);
scratch_push_byte(prefix)
} else {
let length_word = rlp_length_word(content_len);
let length_len = rlp_minimal_word_len(length_word);
let length_byte = rlp_length_byte(length_len);
let prefix = add_bits(0xf7, length_byte);
scratch_push_byte(prefix);
scratch_push_word_be(length_word, length_len)
}
}Appends one full-width EVM word as an RLP byte string.
function rlp_write_word(w : word) -> unit = {
rlp_write_string_prefix(WORD_BYTE_LENGTH, 0x00);
scratch_push_word_be(w, WORD_BYTE_LENGTH)
}Sizes the RLP content of one retained record's topic list.
function topics_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var topic : log_store_index = 0;
let topic_count = log_topics_count(index);
let word_size = rlp_word_size();
let encoded_word_length = rlp_scratch_small_length(word_size);
while topic < topic_count termination_measure(topic_count - topic) do {
size = rlp_scratch_length_add(size, encoded_word_length);
topic = log_store_index_increment(topic)
};
size
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)function rlp_write_log_entry¶
Writes one canonical retained receipt log entry.
function rlp_write_log_entry(index : log_store_index) -> unit = {
let content_size = log_entry_rlp_content_size(index);
rlp_write_list_prefix(content_size);
let address = log_address(index);
rlp_write_addr(address);
rlp_write_topics(index);
let data = read_log_data(index);
rlp_write_slice(data)
}Indexed access to a retained log record.
val log_address = impure { c: "log_addr" } : log_store_index -> addressSizes one retained log entry.
function log_entry_rlp_content_size(index : log_store_index) -> rlp_scratch_length = {
let address_size = rlp_addr_size();
let address_length = rlp_scratch_small_length(address_size);
let topics_length = topics_rlp_size(index);
let data = read_log_data(index);
let data_length = rlp_scratch_slice_size(data);
let address_and_topics_length = rlp_scratch_length_add(address_length, topics_length);
rlp_scratch_length_add(address_and_topics_length, data_length)
}Returns the source-backed payload of one retained log record.
function read_log_data(index : log_store_index) -> LogDataSlice = {
let off = log_data_offset(index);
let len = log_data_length(index);
if len <= sizeof(log_data_region_bound) - off then {
log_data_slice(off, len)
} else {
assert(false, "log data slice overflow");
EMPTY_LOG_DATA_SLICE
}
}Appends one Ethereum address as an RLP byte string.
function rlp_write_addr(a : address) -> unit = {
rlp_write_string_prefix(ADDRESS_BYTE_LENGTH, a[0]);
scratch_push_address(a)
}function rlp_write_list_prefix(content_len) = {
if content_len <= RLP_SHORT_LENGTH_LIMIT then {
let length_byte = rlp_length_byte(content_len);
let prefix = add_bits(0xc0, length_byte);
scratch_push_byte(prefix)
} else {
let length_word = rlp_length_word(content_len);
let length_len = rlp_minimal_word_len(length_word);
let length_byte = rlp_length_byte(length_len);
let prefix = add_bits(0xf7, length_byte);
scratch_push_byte(prefix);
scratch_push_word_be(length_word, length_len)
}
}Writes one retained record's topic list.
function rlp_write_topics(index : log_store_index) -> unit = {
let content_size = topics_rlp_content_size(index);
rlp_write_list_prefix(content_size);
var topic : log_store_index = 0;
let topic_count = log_topics_count(index);
while topic < topic_count termination_measure(topic_count - topic) do {
let value = log_topic(index, topic);
rlp_write_word(value);
topic = log_store_index_increment(topic)
}
}A 20-byte account address (YP ยง4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)function rlp_write_logs¶
Writes the RLP list of a retained log range.
function rlp_write_logs(logs : LogSeriesRef) -> unit = {
let content_size = logs_rlp_content_size(logs);
rlp_write_list_prefix(content_size);
var offset : log_store_index = 0;
while offset < logs.count termination_measure(logs.count - offset) do {
let index = log_store_index_add(logs.start, offset);
rlp_write_log_entry(index);
offset = log_store_index_increment(offset)
}
}Adds a relative log offset to its series start without wrapping.
function log_store_index_add(left : log_store_index, right : log_store_index) -> log_store_index =
if right <= sizeof(log_store_index_bound) - left then {
left + right
} else {
assert(false, "log store index overflow");
0
}Advances a valid log cursor without fixed-width wrapping.
function log_store_index_increment(value : log_store_index) -> log_store_index =
if value < sizeof(log_store_index_bound) then {
value + 1
} else {
assert(false, "log store index overflow");
0
}Sizes the RLP content of a retained log range.
function logs_rlp_content_size(logs : LogSeriesRef) -> rlp_scratch_length = {
var size : rlp_scratch_length = 0;
var offset : log_store_index = 0;
while offset < logs.count termination_measure(logs.count - offset) do {
let index = log_store_index_add(logs.start, offset);
let entry_size = log_entry_rlp_size(index);
size = rlp_scratch_length_add(size, entry_size);
offset = log_store_index_increment(offset)
};
size
}function rlp_write_list_prefix(content_len) = {
if content_len <= RLP_SHORT_LENGTH_LIMIT then {
let length_byte = rlp_length_byte(content_len);
let prefix = add_bits(0xc0, length_byte);
scratch_push_byte(prefix)
} else {
let length_word = rlp_length_word(content_len);
let length_len = rlp_minimal_word_len(length_word);
let length_byte = rlp_length_byte(length_len);
let prefix = add_bits(0xf7, length_byte);
scratch_push_byte(prefix);
scratch_push_word_be(length_word, length_len)
}
}Writes one canonical retained receipt log entry.
function rlp_write_log_entry(index : log_store_index) -> unit = {
let content_size = log_entry_rlp_content_size(index);
rlp_write_list_prefix(content_size);
let address = log_address(index);
rlp_write_addr(address);
rlp_write_topics(index);
let data = read_log_data(index);
rlp_write_slice(data)
}A consecutive transaction-local view into the block-lifetime host log store. Reverted frame records are removed before this view is captured.
struct LogSeriesRef = {
start : log_store_index,
count : log_store_index,
}A position in the block-lifetime host log store.
type log_store_index = range(0, log_store_index_bound)function rlp_write_logs_bloom¶
Writes a bloom as its fixed-width RLP byte string without constructing a temporary Sail list.
function rlp_write_logs_bloom(bloom : LogsBloom) -> unit = {
rlp_write_string_prefix(LOGS_BLOOM_BYTE_LENGTH, 0x00);
scratch_push_fixed_bytes_256(bloom)
}function rlp_write_string_prefix(len, first) = {
if (len == 1) & (first[7] == bitzero) then {
return ()
};
if len <= RLP_SHORT_LENGTH_LIMIT then {
let length_byte = rlp_length_byte(len);
let prefix = add_bits(0x80, length_byte);
scratch_push_byte(prefix)
} else {
let length_word = rlp_length_word(len);
let length_len = rlp_minimal_word_len(length_word);
let length_byte = rlp_length_byte(length_len);
let prefix = add_bits(0xb7, length_byte);
scratch_push_byte(prefix);
scratch_push_word_be(length_word, length_len)
}
}Appends a decreasing fixed 256-byte vector in canonical wire order.
function scratch_push_fixed_bytes_256(data : vector(256, dec, byte)) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_fixed_bytes_256(arena.len, data)
}let LOGS_BLOOM_BYTE_LENGTH : int(256) = 256The 2048-bit logs bloom filter (YP ยง4.4.1), as 256 bytes.
type LogsBloom = vector(256, dec, byte)function receipt_write_logs_bloom¶
Writes the bloom belonging to one receipt, constructed from its retained log range (YP ยง4.4.1). Keeping this small semantic hook separate lets an optimized backend populate the already-reserved RLP span directly from its log-store accumulator without materializing a Sail bloom value.
function receipt_write_logs_bloom(receipt : Receipt) -> unit = {
let bloom = logs_bloom_for_logs(receipt.logs);
rlp_write_logs_bloom(bloom)
}The bloom of one receipt's retained log range.
function logs_bloom_for_logs(logs : LogSeriesRef) -> LogsBloom =
bloom_add_logs(EMPTY_LOGS_BLOOM, logs)Writes a bloom as its fixed-width RLP byte string without constructing a temporary Sail list.
function rlp_write_logs_bloom(bloom : LogsBloom) -> unit = {
rlp_write_string_prefix(LOGS_BLOOM_BYTE_LENGTH, 0x00);
scratch_push_fixed_bytes_256(bloom)
}A receipt whose originating transaction limit is not needed.
type Receipt = {
'state_gas 'execution_gas 'gas_used 'regular_limit 'limit,
receipt_gas_relation(
'limit,
'regular_limit,
'gas_used,
'execution_gas,
'state_gas,
).
ReceiptFields(
'limit,
'regular_limit,
'gas_used,
'execution_gas,
'state_gas,
)
}function receipt_payload_content_size¶
function receipt_payload_content_size(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let status_size = rlp_uint_size(status);
let status_length = rlp_scratch_small_length(status_size);
let gas_word = word_of_nat_byte_count(cumulative_gas_used);
let gas_size = rlp_uint_word_size(gas_word);
let gas_length = rlp_scratch_small_length(gas_size);
let bloom_prefix_size = rlp_length_prefix_len(LOGS_BLOOM_BYTE_LENGTH);
let bloom_prefix_length = rlp_scratch_small_length(bloom_prefix_size);
let bloom_length = rlp_scratch_length_add(LOGS_BLOOM_BYTE_LENGTH, bloom_prefix_length);
let logs_length = logs_rlp_size(r.logs);
let fixed_length = rlp_scratch_length_add(status_length, gas_length);
let fixed_and_bloom_length = rlp_scratch_length_add(fixed_length, bloom_length);
rlp_scratch_length_add(fixed_and_bloom_length, logs_length)
}Sizes a retained log range with its list prefix.
function logs_rlp_size(logs : LogSeriesRef) -> rlp_scratch_length = {
let content_size = logs_rlp_content_size(logs);
rlp_scratch_list_size(content_size)
}function receipt_payload_content_size(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let status_size = rlp_uint_size(status);
let status_length = rlp_scratch_small_length(status_size);
let gas_word = word_of_nat_byte_count(cumulative_gas_used);
let gas_size = rlp_uint_word_size(gas_word);
let gas_length = rlp_scratch_small_length(gas_size);
let bloom_prefix_size = rlp_length_prefix_len(LOGS_BLOOM_BYTE_LENGTH);
let bloom_prefix_length = rlp_scratch_small_length(bloom_prefix_size);
let bloom_length = rlp_scratch_length_add(LOGS_BLOOM_BYTE_LENGTH, bloom_prefix_length);
let logs_length = logs_rlp_size(r.logs);
let fixed_length = rlp_scratch_length_add(status_length, gas_length);
let fixed_and_bloom_length = rlp_scratch_length_add(fixed_length, bloom_length);
rlp_scratch_length_add(fixed_and_bloom_length, logs_length)
}function rlp_length_prefix_len(len) =
if len <= RLP_SHORT_LENGTH_LIMIT then {
1
} else {
let length_word = rlp_length_word(len);
1 + rlp_minimal_word_len(length_word)
}function rlp_scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
fatal_error(RlpDecode)
}function rlp_scratch_small_length(value) = valuefunction rlp_uint_word_size(w) = {
let len = rlp_minimal_word_len(w);
let first =
if len == 1 then word_low_byte(w) else 0x00;
if (len == 1) & (first[7] == bitzero) then {
1
} else {
1 + len
}
}function word_of_nat_byte_count(value) =
if value < 2 ^ 256 then {
u256(value)
} else {
assert(false);
WORD_ZERO
}let LOGS_BLOOM_BYTE_LENGTH : int(256) = 256function receipt_encoded_length¶
function receipt_encoded_length(r, cumulative_gas_used) = {
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
let payload_len = rlp_scratch_list_size(content_len);
if typed then {
rlp_scratch_length_add(payload_len, 1)
} else {
payload_len
}
}function receipt_encoded_length(r, cumulative_gas_used) = {
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
let payload_len = rlp_scratch_list_size(content_len);
if typed then {
rlp_scratch_length_add(payload_len, 1)
} else {
payload_len
}
}function receipt_payload_content_size(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let status_size = rlp_uint_size(status);
let status_length = rlp_scratch_small_length(status_size);
let gas_word = word_of_nat_byte_count(cumulative_gas_used);
let gas_size = rlp_uint_word_size(gas_word);
let gas_length = rlp_scratch_small_length(gas_size);
let bloom_prefix_size = rlp_length_prefix_len(LOGS_BLOOM_BYTE_LENGTH);
let bloom_prefix_length = rlp_scratch_small_length(bloom_prefix_size);
let bloom_length = rlp_scratch_length_add(LOGS_BLOOM_BYTE_LENGTH, bloom_prefix_length);
let logs_length = logs_rlp_size(r.logs);
let fixed_length = rlp_scratch_length_add(status_length, gas_length);
let fixed_and_bloom_length = rlp_scratch_length_add(fixed_length, bloom_length);
rlp_scratch_length_add(fixed_and_bloom_length, logs_length)
}function rlp_scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
fatal_error(RlpDecode)
}Adds the canonical list prefix to a materializable RLP content length.
function rlp_scratch_list_size(content_len : rlp_scratch_length) -> rlp_scratch_length = {
let prefix_size = rlp_length_prefix_len(content_len);
let prefix_length = rlp_scratch_small_length(prefix_size);
rlp_scratch_length_add(content_len, prefix_length)
}The canonical EIP-2718 wire discriminant. Encoding is total over the closed transaction-type algebra; decoding handles the remaining byte values explicitly at its validation boundary.
function tx_envelope_type(t : TxType) -> byte = match t {
LegacyTx => 0x00,
AccessListTx => 0x01,
FeeMarketTx => 0x02,
BlobTx => 0x03,
SetCodeTx => 0x04,
}An 8-bit byte.
type byte = bits(8)function receipt_write_encoded¶
function receipt_write_encoded(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
if typed then {
scratch_push_byte(envelope_type)
};
rlp_write_list_prefix(content_len);
rlp_write_uint(status);
rlp_write_uint(cumulative_gas_used);
receipt_write_logs_bloom(r);
rlp_write_logs(r.logs)
}function receipt_payload_content_size(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let status_size = rlp_uint_size(status);
let status_length = rlp_scratch_small_length(status_size);
let gas_word = word_of_nat_byte_count(cumulative_gas_used);
let gas_size = rlp_uint_word_size(gas_word);
let gas_length = rlp_scratch_small_length(gas_size);
let bloom_prefix_size = rlp_length_prefix_len(LOGS_BLOOM_BYTE_LENGTH);
let bloom_prefix_length = rlp_scratch_small_length(bloom_prefix_size);
let bloom_length = rlp_scratch_length_add(LOGS_BLOOM_BYTE_LENGTH, bloom_prefix_length);
let logs_length = logs_rlp_size(r.logs);
let fixed_length = rlp_scratch_length_add(status_length, gas_length);
let fixed_and_bloom_length = rlp_scratch_length_add(fixed_length, bloom_length);
rlp_scratch_length_add(fixed_and_bloom_length, logs_length)
}function receipt_write_encoded(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
if typed then {
scratch_push_byte(envelope_type)
};
rlp_write_list_prefix(content_len);
rlp_write_uint(status);
rlp_write_uint(cumulative_gas_used);
receipt_write_logs_bloom(r);
rlp_write_logs(r.logs)
}Writes the bloom belonging to one receipt, constructed from its retained log range (YP ยง4.4.1). Keeping this small semantic hook separate lets an optimized backend populate the already-reserved RLP span directly from its log-store accumulator without materializing a Sail bloom value.
function receipt_write_logs_bloom(receipt : Receipt) -> unit = {
let bloom = logs_bloom_for_logs(receipt.logs);
rlp_write_logs_bloom(bloom)
}function rlp_write_list_prefix(content_len) = {
if content_len <= RLP_SHORT_LENGTH_LIMIT then {
let length_byte = rlp_length_byte(content_len);
let prefix = add_bits(0xc0, length_byte);
scratch_push_byte(prefix)
} else {
let length_word = rlp_length_word(content_len);
let length_len = rlp_minimal_word_len(length_word);
let length_byte = rlp_length_byte(length_len);
let prefix = add_bits(0xf7, length_byte);
scratch_push_byte(prefix);
scratch_push_word_be(length_word, length_len)
}
}Writes the RLP list of a retained log range.
function rlp_write_logs(logs : LogSeriesRef) -> unit = {
let content_size = logs_rlp_content_size(logs);
rlp_write_list_prefix(content_size);
var offset : log_store_index = 0;
while offset < logs.count termination_measure(logs.count - offset) do {
let index = log_store_index_add(logs.start, offset);
rlp_write_log_entry(index);
offset = log_store_index_increment(offset)
}
}Appends one byte without constructing a Sail list.
function scratch_push_byte(data : byte) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_byte(arena.len, data)
}The canonical EIP-2718 wire discriminant. Encoding is total over the closed transaction-type algebra; decoding handles the remaining byte values explicitly at its validation boundary.
function tx_envelope_type(t : TxType) -> byte = match t {
LegacyTx => 0x00,
AccessListTx => 0x01,
FeeMarketTx => 0x02,
BlobTx => 0x03,
SetCodeTx => 0x04,
}An 8-bit byte.
type byte = bits(8)function receipt_encoded¶
function receipt_encoded(r, cumulative_gas_used) = {
let encoded_len = receipt_encoded_length(r, cumulative_gas_used);
let encoder = rlp_encoder_begin(encoded_len);
receipt_write_encoded(r, cumulative_gas_used);
rlp_encoder_finish(encoder)
}function receipt_encoded(r, cumulative_gas_used) = {
let encoded_len = receipt_encoded_length(r, cumulative_gas_used);
let encoder = rlp_encoder_begin(encoded_len);
receipt_write_encoded(r, cumulative_gas_used);
rlp_encoder_finish(encoder)
}function receipt_encoded_length(r, cumulative_gas_used) = {
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
let payload_len = rlp_scratch_list_size(content_len);
if typed then {
rlp_scratch_length_add(payload_len, 1)
} else {
payload_len
}
}function receipt_write_encoded(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
if typed then {
scratch_push_byte(envelope_type)
};
rlp_write_list_prefix(content_len);
rlp_write_uint(status);
rlp_write_uint(cumulative_gas_used);
receipt_write_logs_bloom(r);
rlp_write_logs(r.logs)
}function rlp_encoder_begin(expected_len) =
struct { start = scratch_reserve(expected_len), expected_len = expected_len }Finishes an exact-size construction and retains its encoded bytes.
function rlp_encoder_finish(encoder : RlpEncoder) -> ScratchSlice = {
let encoded = scratch_finish(encoder.start);
assert(encoded.len == encoder.expected_len, "RLP encoder length");
encoded
}function receipt_record_write_length¶
function receipt_record_write_length(value) = {
let byte_0 = get_slice_int(8, value, 0);
scratch_push_byte(byte_0);
let byte_1 = get_slice_int(8, value, 8);
scratch_push_byte(byte_1);
let byte_2 = get_slice_int(8, value, 16);
scratch_push_byte(byte_2);
let byte_3 = get_slice_int(8, value, 24);
scratch_push_byte(byte_3);
let byte_4 = get_slice_int(8, value, 32);
scratch_push_byte(byte_4);
let byte_5 = get_slice_int(8, value, 40);
scratch_push_byte(byte_5);
let byte_6 = get_slice_int(8, value, 48);
scratch_push_byte(byte_6);
let byte_7 = get_slice_int(8, value, 56);
scratch_push_byte(byte_7)
}val get_slice_int = pure {_: "get_slice_int"}: forall ('w : Int). (int('w), int, int) -> bits('w)function receipt_record_write_length(value) = {
let byte_0 = get_slice_int(8, value, 0);
scratch_push_byte(byte_0);
let byte_1 = get_slice_int(8, value, 8);
scratch_push_byte(byte_1);
let byte_2 = get_slice_int(8, value, 16);
scratch_push_byte(byte_2);
let byte_3 = get_slice_int(8, value, 24);
scratch_push_byte(byte_3);
let byte_4 = get_slice_int(8, value, 32);
scratch_push_byte(byte_4);
let byte_5 = get_slice_int(8, value, 40);
scratch_push_byte(byte_5);
let byte_6 = get_slice_int(8, value, 48);
scratch_push_byte(byte_6);
let byte_7 = get_slice_int(8, value, 56);
scratch_push_byte(byte_7)
}Appends one byte without constructing a Sail list.
function scratch_push_byte(data : byte) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_byte(arena.len, data)
}function receipt_record_append¶
function receipt_record_append(r, cumulative_gas_used) = {
let encoded_len = receipt_encoded_length(r, cumulative_gas_used);
if 2 ^ 64 - 1 < encoded_len then {
fatal_error(RlpDecode)
};
let record_len = scratch_length_add(EIGHT_BYTE_LENGTH, encoded_len);
let _record_start = scratch_reserve(record_len);
receipt_record_write_length(encoded_len);
let encoder = rlp_encoder_begin(encoded_len);
receipt_write_encoded(r, cumulative_gas_used);
let _encoded = rlp_encoder_finish(encoder);
()
}function fatal_error(_reason) = exit(())We have special support for raising values to the power of two. Any Sail expression 2 ^ x will be compiled to this builtin.
val pow2 = pure {lean: "_lean_pow2i", _: "pow2"}: forall ('n : Int). int('n) -> int(2 ^ 'n)function receipt_encoded_length(r, cumulative_gas_used) = {
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
let payload_len = rlp_scratch_list_size(content_len);
if typed then {
rlp_scratch_length_add(payload_len, 1)
} else {
payload_len
}
}function receipt_record_append(r, cumulative_gas_used) = {
let encoded_len = receipt_encoded_length(r, cumulative_gas_used);
if 2 ^ 64 - 1 < encoded_len then {
fatal_error(RlpDecode)
};
let record_len = scratch_length_add(EIGHT_BYTE_LENGTH, encoded_len);
let _record_start = scratch_reserve(record_len);
receipt_record_write_length(encoded_len);
let encoder = rlp_encoder_begin(encoded_len);
receipt_write_encoded(r, cumulative_gas_used);
let _encoded = rlp_encoder_finish(encoder);
()
}function receipt_record_write_length(value) = {
let byte_0 = get_slice_int(8, value, 0);
scratch_push_byte(byte_0);
let byte_1 = get_slice_int(8, value, 8);
scratch_push_byte(byte_1);
let byte_2 = get_slice_int(8, value, 16);
scratch_push_byte(byte_2);
let byte_3 = get_slice_int(8, value, 24);
scratch_push_byte(byte_3);
let byte_4 = get_slice_int(8, value, 32);
scratch_push_byte(byte_4);
let byte_5 = get_slice_int(8, value, 40);
scratch_push_byte(byte_5);
let byte_6 = get_slice_int(8, value, 48);
scratch_push_byte(byte_6);
let byte_7 = get_slice_int(8, value, 56);
scratch_push_byte(byte_7)
}function receipt_write_encoded(r, cumulative_gas_used) = {
let status : range(0, 1) =
if r.success then 1 else 0;
let content_len = receipt_payload_content_size(r, cumulative_gas_used);
let envelope_type : byte = tx_envelope_type(r.tx_type);
let typed = envelope_type != 0x00;
if typed then {
scratch_push_byte(envelope_type)
};
rlp_write_list_prefix(content_len);
rlp_write_uint(status);
rlp_write_uint(cumulative_gas_used);
receipt_write_logs_bloom(r);
rlp_write_logs(r.logs)
}function rlp_encoder_begin(expected_len) =
struct { start = scratch_reserve(expected_len), expected_len = expected_len }Finishes an exact-size construction and retains its encoded bytes.
function rlp_encoder_finish(encoder : RlpEncoder) -> ScratchSlice = {
let encoded = scratch_finish(encoder.start);
assert(encoded.len == encoder.expected_len, "RLP encoder length");
encoded
}function scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
assert(false, "scratch length overflow");
0
}function scratch_reserve(len) = {
let arena = scratch_arena;
let reserved = host_scratch_reserve(arena.len, len);
assert(reserved, "scratch reserve");
arena.len
}let EIGHT_BYTE_LENGTH : int(8) = 8The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}