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)