The output buffer¶
Host-owned storage for a frame or precompile output that must outlive the mutable EVM memory from which it was produced. EVM returndata state and frame transitions remain entirely in Sail.
Non-normative
This page documents the model's host interface — internal contracts of the executable specification, not protocol rules.
val output_buffer_store_memory¶
Copies frame memory into the host output buffer; the flag reports acceptance.
val output_buffer_store_memory = impure { c: "output_buffer_store_memory" } : EvmMemorySlice -> boolAn EVM-memory range with its coordinate and length packed existentially.
type EvmMemorySlice = {
'off 'len,
memory_region_valid_range('off, 'len).
EvmMemorySliceFields('off, 'len)
}val output_buffer_store_input¶
Copies top-level calldata into the host output buffer. This is used by the identity precompile without first materializing the input in EVM memory.
val output_buffer_store_input = impure { c: "output_buffer_store_input" } : StatelessInputSlice -> boolA stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}val output_buffer_store_word¶
Stores one 32-byte word as the buffer contents; the axiom behind output_buffer_word.
val output_buffer_store_word = impure { c: "output_buffer_store_word" } : word -> boolThe 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)val output_buffer_store_words¶
Stores two consecutive words as the buffer contents; the axiom behind output_buffer_words.
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)val public_output_write¶
Appends one byte to the guest's public output — the committed result stream the verifier sees.
val public_output_write = impure { c: "public_output_write" } : ScratchSlice -> boolA scratch-arena range with its coordinate and length packed existentially.
type ScratchSlice = {
'off 'len,
scratch_valid_range('off, 'len).
ScratchSliceFields('off, 'len)
}function output_buffer_slice¶
function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}function output_slice(off, len) =
struct { bytes = off, len = len }let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)function freeze_memory_output¶
Copies frame output into the host buffer so it survives frame teardown; the canonical returndata source.
function freeze_memory_output(data : EvmMemorySlice) -> OutputSlice = {
let len = data.len;
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
let stored = output_buffer_store_memory(data);
if stored then {
output_buffer_slice(len)
} else {
EMPTY_OUTPUT_SLICE
}
}
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}Copies frame memory into the host output buffer; the flag reports acceptance.
val output_buffer_store_memory = impure { c: "output_buffer_store_memory" } : EvmMemorySlice -> boollet EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)An EVM-memory range with its coordinate and length packed existentially.
type EvmMemorySlice = {
'off 'len,
memory_region_valid_range('off, 'len).
EvmMemorySliceFields('off, 'len)
}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 freeze_input_output¶
Copies stateless-input-backed output into the host buffer so it survives frame teardown.
function freeze_input_output(data : StatelessInputSlice) -> OutputSlice = {
let len = data.len;
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
let stored = output_buffer_store_input(data);
if stored then {
output_buffer_slice(len)
} else {
EMPTY_OUTPUT_SLICE
}
}
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}Copies top-level calldata into the host output buffer. This is used by the identity precompile without first materializing the input in EVM memory.
val output_buffer_store_input = impure { c: "output_buffer_store_input" } : StatelessInputSlice -> boollet EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)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 stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}function freeze_calldata_output¶
Freezes an output taken directly from calldata, dispatching on whether the calldata is input-backed or memory-backed.
function freeze_calldata_output(data : CalldataSlice) -> OutputSlice =
match data {
InputCalldata(bytes) => freeze_input_output(bytes),
MemoryCalldata(bytes) => freeze_memory_output(bytes),
}Copies stateless-input-backed output into the host buffer so it survives frame teardown.
function freeze_input_output(data : StatelessInputSlice) -> OutputSlice = {
let len = data.len;
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
let stored = output_buffer_store_input(data);
if stored then {
output_buffer_slice(len)
} else {
EMPTY_OUTPUT_SLICE
}
}
}Copies frame output into the host buffer so it survives frame teardown; the canonical returndata source.
function freeze_memory_output(data : EvmMemorySlice) -> OutputSlice = {
let len = data.len;
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
let stored = output_buffer_store_memory(data);
if stored then {
output_buffer_slice(len)
} else {
EMPTY_OUTPUT_SLICE
}
}
}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 frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}function output_buffer_word¶
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
}
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}Stores one 32-byte word as the buffer contents; the axiom behind output_buffer_word.
val output_buffer_store_word = impure { c: "output_buffer_store_word" } : word -> boollet EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)let WORD_BYTE_LENGTH : int(32) = 32A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}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 output_buffer_words¶
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
}
}function output_buffer_slice(len) =
if len == 0 then {
EMPTY_OUTPUT_SLICE
} else {
output_slice(0, len)
}Stores two consecutive words as the buffer contents; the axiom behind output_buffer_words.
val output_buffer_store_words = impure { c: "output_buffer_store_words" } : (word, word) -> boollet DOUBLE_WORD_BYTE_LENGTH : int(64) = 64let EMPTY_OUTPUT_SLICE : OutputSliceFields(0, 0) = output_slice(0, 0)A frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}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)