Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/genvm-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ jobs:
# Contracts pinned to runners newer than the released linter's bundled
# SDK cannot be loaded by genvm-linter (<= 0.11.0): the genvm-main
# runner (1zr6nqk...) fails with "No module named 'genlayer.py'", and
# the v0.6 fee-era runner (9b8kjyda2...) fails with
# "filename 'runners/py-genlayer/9b/8kjy....tar' not found". Skip both
# the v0.3.0-rc7 runner (bq43ya7v...) fails with
# "filename 'runners/py-genlayer/bq/43ya....zip' not found". Skip both
# until a linter release bundles them (genvm-linter dxp-694), then
# drop this filter.
- name: Lint example contracts
run: |
failed=0
for f in examples/contracts/*.py; do
echo "::group::$f"
if grep -qE "py-genlayer:(1zr6nqk597d97kg0dyxg0shhrykx5v02zjgnyrajapy4wlqvfvwh|9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0)" "$f"; then
if grep -qE "py-genlayer:(1zr6nqk597d97kg0dyxg0shhrykx5v02zjgnyrajapy4wlqvfvwh|5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng)" "$f"; then
echo "SKIP: pinned to a runner unsupported by released genvm-linter"
elif ! genvm-lint check "$f"; then
failed=1
Expand All @@ -59,7 +59,7 @@ jobs:
for f in tests/load/contracts/*.py tests/direct/contracts/*.py; do
[ -f "$f" ] || continue
echo "::group::$f"
if grep -qE "py-genlayer:(1zr6nqk597d97kg0dyxg0shhrykx5v02zjgnyrajapy4wlqvfvwh|9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0)" "$f"; then
if grep -qE "py-genlayer:(1zr6nqk597d97kg0dyxg0shhrykx5v02zjgnyrajapy4wlqvfvwh|5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng)" "$f"; then
echo "SKIP: pinned to a runner unsupported by released genvm-linter"
elif ! genvm-lint check "$f"; then
failed=1
Expand Down
2 changes: 1 addition & 1 deletion backend/consensus/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
from backend.rollup.consensus_service import ConsensusService

import backend.validators as validators
from backend.node.genvm.origin.public_abi import ResultCode
from backend.node.genvm.origin.host_fns import ResultCode
from backend.consensus.types import ConsensusResult, ConsensusRound
from backend.consensus.utils import determine_consensus_from_votes
from backend.consensus.decisions import (
Expand Down
7 changes: 4 additions & 3 deletions backend/node/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from backend.node.types import Receipt, ExecutionMode, Vote, ExecutionResultStatus
from backend.protocol_rpc.message_handler.base import IMessageHandler
from .genvm.origin import logger as genvm_logger
from .genvm.origin import host_fns
from .genvm.origin import public_abi

from .types import Address
Expand Down Expand Up @@ -734,7 +735,7 @@ def _set_vote(self, receipt: Receipt) -> Receipt:
result_code = receipt.result[0]

# 1. Timeout: VM-level timeout or GenVM internal error
if result_code == public_abi.ResultCode.VM_ERROR:
if result_code == host_fns.ResultCode.VM_ERROR:
error_message = receipt.result[1:]
if error_message == b"timeout" or error_message.startswith(
b"GenVM internal error"
Expand Down Expand Up @@ -1045,7 +1046,7 @@ async def _run_genvm(
}
if transaction_datetime is not None:
assert transaction_datetime.tzinfo is not None
message["datetime"] = transaction_datetime.isoformat()
message["transaction_timestamp"] = transaction_datetime.isoformat()
perms = "rcn" # read/call/spawn nondet
if not readonly:
perms += "ws" # write/send
Expand Down Expand Up @@ -1087,7 +1088,7 @@ async def _run_genvm(
result = genvmbase.ExecutionResult(
result=genvmbase.ExecutionError(
message=str(e),
kind=public_abi.ResultCode.USER_ERROR,
kind=host_fns.ResultCode.USER_ERROR,
error_code=e.__class__.__name__,
raw_error={
"fatal": False,
Expand Down
49 changes: 31 additions & 18 deletions backend/node/genvm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
from .origin.public_abi import *
from .origin import base_host
from .origin import host_fns

# The wire result code lives in `host_fns`; `public_abi` only carries the
# subset the SDK exposes, so import it last to win over the star import.
from .origin.host_fns import ResultCode
from .origin import logger as genvm_logger
from .error_codes import (
extract_error_code,
Expand Down Expand Up @@ -135,7 +139,7 @@ def get_balance(self, addr: Address) -> int: ...
def genvm_executor_selector_for(self, addr: Address) -> str | None:
"""Executor selector this contract is pinned to, or None if unpinned.

Backs the nested cross-major `resolve_callcontract_executor` hook: the
Backs the nested cross-major `resolve_call_contract_executor` hook: the
genvm asks which executor a call target runs on, and a pinned contract
answers with its stored `genvm_executor_selector`. Proxies without a
contract store (e.g. deploy-time `_StateProxyNone`) inherit this None
Expand Down Expand Up @@ -194,7 +198,7 @@ def is_valid_executor_selector(value: str) -> bool:

Both submit-time validation
(`protocol_rpc.endpoints._validate_genvm_executor_selector`) and
nested-call resolution (`Host.resolve_callcontract_executor`) must use
nested-call resolution (`Host.resolve_call_contract_executor`) must use
this same grammar so a value that is accepted (or backfilled) on one
boundary never gets rejected at the other.
"""
Expand Down Expand Up @@ -417,6 +421,15 @@ def _emission_int(emission: dict, name: str) -> int:
return int(_emission_value(emission, name) or 0)


def _emission_on(emission: dict) -> typing.Literal["accepted", "finalized"]:
"""GenVM calls the pre-finalization lifecycle `decided`; Studio calls it `accepted`.

Both executor lines normalize to `decided` at the host boundary, so `accepted`
never arrives on the wire — this maps one way, GenVM → Studio.
"""
return "finalized" if emission["on"] == "finalized" else "accepted"


def _emission_hex(emission: dict, name: str) -> str:
value = _emission_value(emission, name)
if value is None:
Expand Down Expand Up @@ -589,43 +602,43 @@ def provide_result(
# are ephemeral VM-side effects: discard them instead of tripping the
# storage_write readonly assertion.
if not getattr(state, "readonly", False):
apply_storage_changes(res.result_storage_changes, state)
apply_storage_changes(res.result_storage_deltas, state)

# Extract pending_transactions from result_emissions
pending_transactions = []
for emission in res.result_emissions:
match emission["type"]:
case "PostMessage":
case "InternalMessage":
pending_transactions.append(
PendingTransaction(
emission["address"].as_hex,
gvm_calldata.encode(emission["calldata"]),
code=None,
salt_nonce=0,
value=emission["value"],
on=emission["on"],
on=_emission_on(emission),
fee_params=_emission_internal_fee_params(emission),
declared_budget=_emission_int(emission, "declaredBudget"),
call_key=_emission_hex(emission, "callKey"),
allocation_subtree=_emission_allocation_subtree(emission),
)
)
case "DeployContract":
case "InternalDeployMessage":
pending_transactions.append(
PendingTransaction(
address="0x",
calldata=gvm_calldata.encode(emission["calldata"]),
code=emission["code"],
salt_nonce=emission["salt_nonce"],
value=emission["value"],
on=emission["on"],
on=_emission_on(emission),
fee_params=_emission_internal_fee_params(emission),
declared_budget=_emission_int(emission, "declaredBudget"),
call_key=_emission_hex(emission, "callKey"),
allocation_subtree=_emission_allocation_subtree(emission),
)
)
case "EthSend":
case "ExternalMessage":
pending_transactions.append(
PendingTransaction(
address=emission["address"].as_hex,
Expand Down Expand Up @@ -780,17 +793,17 @@ async def close_connections(self) -> None:
self.sock = None

async def storage_read(
self, type: StorageType, account: bytes, slot: bytes, index: int, le: int, /
self, type: StorageView, address: bytes, slot: bytes, offset: int, le: int, /
) -> bytes:
assert type != StorageType.LATEST_FINAL
assert type != StorageView.LATEST_FINALIZED
return await asyncio.to_thread(
self._state_proxy.storage_read, Address(account), slot, index, le
self._state_proxy.storage_read, Address(address), slot, offset, le
)

async def resolve_callcontract_executor(
async def resolve_call_contract_executor(
self,
contract_address: Address,
state_mode: StorageType,
state_mode: StorageView,
advisory_major: int,
/,
) -> bytes | None:
Expand Down Expand Up @@ -818,20 +831,20 @@ async def resolve_callcontract_executor(
)
return gvm_calldata.encode({"kind": "version", "version": reroute})

async def consume_gas(self, gas: int, /) -> None:
async def consume_time_fee_gen_wei(self, time_fee_gen_wei: int, /) -> None:
pass

async def eth_call(self, account: bytes, calldata: bytes, /) -> bytes:
async def external_call(self, address: bytes, calldata: bytes, /) -> bytes:
# FIXME(core-team): #748
assert False

async def get_balance(self, account: bytes, /) -> int:
return await asyncio.to_thread(self._state_proxy.get_balance, Address(account))
async def get_balance_gen_wei(self, address: bytes, /) -> int:
return await asyncio.to_thread(self._state_proxy.get_balance, Address(address))

async def notify_nondet_disagreement(self, call_no: int, /) -> None:
self._nondet_disagreement = call_no

async def remaining_fuel_as_gen(self, /) -> int:
async def get_remaining_time_fee_gen_wei(self, /) -> int:
return 2**60


Expand Down
Loading
Loading