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
Protocol Loop#
The host processes requests in a loop until consume_result:
loop:
method_id := read_byte
match method_id
json/methods/get_calldata:
calldata, err := host_get_calldata()
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_byte_slice calldata
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/storage_write:
slot := read_bytes(SLOT_ID_SIZE)
index := read_u32_le
len := read_u32_le
data := read_bytes(len)
err := host_storage_write(slot, index, data)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
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
break
json/methods/get_leader_nondet_result:
call_no := read_u32_le
data, err := host_get_leader_nondet_result(call_no)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
write_byte_slice data
json/methods/post_nondet_result:
call_no := read_u32_le
result := read_slice()
err := host_post_nondet_result(call_no, result)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
json/methods/post_message:
address := read_bytes(ACCOUNT_ADDR_SIZE)
calldata := read_slice()
message_data := read_slice() # JSON string
err := host_post_message(address, calldata, message_data)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
json/methods/consume_fuel:
gas := read_u64_le
host_consume_fuel(gas)
# note: this method doesn't send any response
json/methods/deploy_contract:
calldata := read_slice()
code := read_slice()
message_data := read_slice() # JSON string
err := host_deploy_contract(calldata, code, message_data)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
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/eth_send:
address := read_bytes(ACCOUNT_ADDR_SIZE)
calldata := read_slice()
message_data := read_slice() # JSON string
err := host_eth_send(address, calldata, message_data)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
json/methods/post_event:
read_byte topics_len
topics := []
for i in range(topics_len):
topic := read_bytes(32) # 32 bytes each
topics.append(topic)
blob := read_slice
err := host_post_event(topics, blob)
if err != json/errors/ok:
write_byte err
else:
write_byte json/errors/ok
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)