Host Loop Pseudocode#
Constants:
ACCOUNT_ADDR_SIZE = 20
SLOT_ID_SIZE = 32
Data Encoding Functions:
write_byte_slice(arr):
write_u32_le len(arr)
write_bytes arr
read_slice():
len := read_u32_le
data := read_bytes(len)
return data
Multi-Host#
GenVM supports multiple host connections. Each host method is routed to a
specific host connection based on a method_hosts mapping in
ExecutionData. The mapping is a byte array where each index corresponds to a
method ID and the value is the host connection index. When the index is out of
bounds or the array is empty, host 0 is used as the default.
The executor accepts multiple --host arguments. Each host connection
independently runs the protocol described below.
In the manager deployment, host 0 is the node’s host loop and host 1 is a
socketpair to the manager. The manager routes consume_result to itself
(host 1) while all other methods go to host 0.
Protocol Loop#
The host processes requests in a loop. Each host connection runs independently, handling only the methods routed to it:
loop:
method_id := read_byte
match method_id
json/methods/storage_read:
read_type := read_byte as json/storage_type
address := read_bytes(ACCOUNT_ADDR_SIZE)
slot := read_bytes(SLOT_ID_SIZE)
index := read_u32_le
len := read_u32_le
data, err := host_storage_read(read_type, address, slot, index, len)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_bytes data # must be exactly len in size
json/methods/consume_result:
host_result := read_slice()
# this is needed to ensure that genvm doesn't close socket before all data is read
write_byte 0x00
json/methods/consume_fuel:
gas := read_u64_le
host_consume_fuel(gas)
# note: this method doesn't send any response
json/methods/eth_call:
address := read_bytes(ACCOUNT_ADDR_SIZE)
calldata := read_slice()
result, err := host_eth_call(address, calldata)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_byte_slice result
json/methods/get_balance:
address := read_bytes(ACCOUNT_ADDR_SIZE)
balance, err := host_get_balance(address)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_bytes balance.to_le_bytes(32) # 256-bit integer
json/methods/remaining_fuel_as_gen:
fuel, err := host_remaining_fuel_as_gen()
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_bytes fuel.to_le_bytes(8) # 64-bit integer, must be safe integer (fits in double)
json/methods/notify_nondet_disagreement:
call_no := read_u32_le
host_notify_nondet_disagreement(call_no)
# note: this method doesn't send any response
json/methods/notify_finished:
# signals that all execution is complete
write_byte 0x00