From badb7b00fd59422f268459d998d735e718bc48d8 Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Fri, 21 Aug 2026 19:35:09 +0900 Subject: [PATCH] =?UTF-8?q?chore(genvm):=20repin=20to=20the=20v1=20gl=5Fca?= =?UTF-8?q?ll=20ABI=20and=20re-vendor=20origin=20=E2=AC=86=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed: the v0.3.0-rc7 runner-hash bump and the gl_call v1 repin with the re-vendored backend/node/genvm/origin/ mirror. --- .github/workflows/genvm-lint.yml | 8 +- backend/consensus/base.py | 2 +- backend/node/base.py | 7 +- backend/node/genvm/base.py | 49 +++-- backend/node/genvm/origin/base_host.py | 136 +++++++------ backend/node/genvm/origin/fees.py | 9 +- backend/node/genvm/origin/host_fns.py | 36 ++-- backend/node/genvm/origin/log_asserts.py | 14 +- backend/node/genvm/origin/public_abi.py | 179 ++++++++++++------ backend/protocol_rpc/endpoints.py | 2 +- backend/protocol_rpc/fees.py | 5 +- docker/scripts/download_genvm.sh | 22 ++- examples/contracts/_hello_world.py | 2 +- examples/contracts/faucet.py | 2 +- .../contracts/football_prediction_market.py | 2 +- examples/contracts/llm_erc20.py | 2 +- examples/contracts/log_indexer.py | 4 +- examples/contracts/storage.py | 2 +- examples/contracts/tip_jar.py | 2 +- examples/contracts/user_storage.py | 2 +- examples/contracts/wizard_of_coin.py | 2 +- .../reroute_to_migration_backfill_test.py | 10 +- .../test_genvm_host_async_callbacks.py | 4 +- .../contracts/error_llm_contract_direct.py | 2 +- .../contracts/error_web_contract_direct.py | 2 +- tests/direct/storage_read_bench.py | 2 +- .../icontracts/contracts/company_naming.py | 2 +- .../contracts/error_execution_contract.py | 2 +- .../contracts/error_llm_contract.py | 2 +- .../contracts/error_web_contract.py | 2 +- .../icontracts/contracts/faucet.py | 2 +- .../icontracts/contracts/genvm_smoke_v1.py | 2 +- .../contracts/intelligent_oracle.py | 2 +- .../contracts/intelligent_oracle_factory.py | 2 +- .../contracts/multi_file_contract/other.py | 2 +- .../contracts/multi_file_contract/runner.json | 2 +- .../icontracts/contracts/multi_read_erc20.py | 2 +- .../contracts/multi_tenant_storage.py | 2 +- .../icontracts/contracts/payable_escrow.py | 2 +- .../icontracts/contracts/read_erc20.py | 2 +- .../contracts/utf8_roundtrip_contract.py | 2 +- tests/integration/test_deploy_reroute_to.py | 8 +- tests/integration/test_upgrade_contract.py | 10 +- tests/load/contracts/counter.py | 2 +- tests/test_linter_endpoint.py | 2 +- .../consensus/test_validator_exec_timeout.py | 2 +- tests/unit/test_consumed_result_decode.py | 24 +-- tests/unit/test_genvm_debug_mode_gate.py | 2 +- tests/unit/test_host_loop_fuel_widths.py | 25 +-- tests/unit/test_leader_llm_recovery.py | 2 +- tests/unit/test_node_state_proxy_metrics.py | 12 +- tests/unit/test_provide_result_readonly.py | 4 +- .../test_resolve_callcontract_executor.py | 18 +- tests/unit/test_run_request_major.py | 2 +- tests/unit/test_set_vote.py | 2 +- tests/unit/test_studio_fees.py | 6 +- tests/unit/test_usage_metrics_service.py | 6 +- third_party/genvm/version | 2 +- 58 files changed, 381 insertions(+), 287 deletions(-) diff --git a/.github/workflows/genvm-lint.yml b/.github/workflows/genvm-lint.yml index 80dbc900e..120b1c929 100644 --- a/.github/workflows/genvm-lint.yml +++ b/.github/workflows/genvm-lint.yml @@ -35,8 +35,8 @@ 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 @@ -44,7 +44,7 @@ jobs: 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 @@ -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 diff --git a/backend/consensus/base.py b/backend/consensus/base.py index cc0a4b74e..91e0601a1 100644 --- a/backend/consensus/base.py +++ b/backend/consensus/base.py @@ -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 ( diff --git a/backend/node/base.py b/backend/node/base.py index e0a81b152..ffa967cdb 100644 --- a/backend/node/base.py +++ b/backend/node/base.py @@ -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 @@ -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" @@ -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 @@ -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, diff --git a/backend/node/genvm/base.py b/backend/node/genvm/base.py index bc812f820..c46cbbadc 100644 --- a/backend/node/genvm/base.py +++ b/backend/node/genvm/base.py @@ -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, @@ -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 @@ -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. """ @@ -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: @@ -589,13 +602,13 @@ 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, @@ -603,14 +616,14 @@ def provide_result( 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", @@ -618,14 +631,14 @@ def provide_result( 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, @@ -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: @@ -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 diff --git a/backend/node/genvm/origin/base_host.py b/backend/node/genvm/origin/base_host.py index b5e98f5aa..7701c6dda 100644 --- a/backend/node/genvm/origin/base_host.py +++ b/backend/node/genvm/origin/base_host.py @@ -102,7 +102,8 @@ def __init__(self, error_code: host_fns.Errors, message: str = ""): @dataclass(frozen=True) class UnsafeOverrides: - """Request overrides that reach boundaries production traffic cannot. + """ + Request overrides that reach boundaries production traffic cannot. Each member states the `debug_mode` the manager requires before it applies: `reroute_to` from `safe`, `initial_recursion` from `unsafe`. With debugging @@ -127,7 +128,7 @@ class Message(typing.TypedDict): chain_id: int value: typing.NotRequired[int] is_init: bool - datetime: typing.NotRequired[str] + transaction_timestamp: typing.NotRequired[str] class FingerprintFrame(typing.TypedDict): @@ -140,8 +141,8 @@ class ResultFingerprint(typing.TypedDict): module_instances: dict[str, typing.Any] -class EthSendInner(typing.TypedDict): - type: typing.Literal["EthSend"] +class ExternalMessageInner(typing.TypedDict): + type: typing.Literal["ExternalMessage"] address: Address calldata: bytes value: int @@ -150,12 +151,12 @@ class EthSendInner(typing.TypedDict): fee_params: fees.ExternalMessageParams -class PostMessageInner(typing.TypedDict): - type: typing.Literal["PostMessage"] +class InternalMessageInner(typing.TypedDict): + type: typing.Literal["InternalMessage"] address: Address calldata: gvm_calldata.Decoded value: int - on: typing.Literal["finalized", "accepted"] + on: typing.Literal["finalized", "decided"] message_fee: int receipt_fee: int fee_params: fees.InternalMessageParams @@ -165,12 +166,12 @@ class PostMessageInner(typing.TypedDict): use_balance: bool -class DeployContractInner(typing.TypedDict): - type: typing.Literal["DeployContract"] +class InternalDeployMessageInner(typing.TypedDict): + type: typing.Literal["InternalDeployMessage"] calldata: gvm_calldata.Decoded code: bytes value: int - on: typing.Literal["finalized", "accepted"] + on: typing.Literal["finalized", "decided"] salt_nonce: int message_fee: int receipt_fee: int @@ -181,18 +182,18 @@ class DeployContractInner(typing.TypedDict): use_balance: bool -class EmitEventInner(typing.TypedDict): - type: typing.Literal["EmitEvent"] +class EventInner(typing.TypedDict): + type: typing.Literal["Event"] topics: list[bytes] blob: dict[str, gvm_calldata.Decoded] storage_fee: int type ResultEmission = typing.Union[ - EthSendInner, - PostMessageInner, - DeployContractInner, - EmitEventInner, + ExternalMessageInner, + InternalMessageInner, + InternalDeployMessageInner, + EventInner, ] @@ -203,31 +204,31 @@ async def loop_enter(self, cancellation: asyncio.Event) -> socket.socket: ... @abc.abstractmethod async def storage_read( self, - mode: public_abi.StorageType, - account: bytes, + mode: public_abi.StorageView, + address: bytes, slot: bytes, - index: int, + offset: int, le: int, /, ) -> bytes: ... - async def resolve_callcontract_executor( + async def resolve_call_contract_executor( self, contract_address: Address, - state_mode: public_abi.StorageType, + state_mode: public_abi.StorageView, advisory_major: int, /, ) -> bytes | None: return None @abc.abstractmethod - async def consume_gas(self, gas: int, /) -> None: ... + async def consume_time_fee_gen_wei(self, time_fee_gen_wei: int, /) -> None: ... @abc.abstractmethod - async def eth_call(self, account: bytes, calldata: bytes, /) -> bytes: ... + async def external_call(self, address: bytes, calldata: bytes, /) -> bytes: ... @abc.abstractmethod - async def get_balance(self, account: bytes, /) -> int: ... + async def get_balance_gen_wei(self, address: bytes, /) -> int: ... @abc.abstractmethod - async def remaining_fuel_as_gen(self, /) -> int: ... + async def get_remaining_time_fee_gen_wei(self, /) -> int: ... @abc.abstractmethod async def notify_nondet_disagreement(self, call_no: int, /) -> None: ... @@ -248,7 +249,7 @@ async def read_contract_major(handler: IHost, message: Message) -> int: if message.get("is_init", False): return UNDEPLOYED_MAJOR octet = await handler.storage_read( - public_abi.StorageType.LATEST_NON_FINAL, + public_abi.StorageView.LATEST_DECIDED, message["contract_address"].as_bytes, ZERO_SLOT, ROOT_OFFSET_MAJOR, @@ -404,26 +405,26 @@ def emit_host_loop_stats(): match meth_id: case host_fns.Methods.STORAGE_READ: mode = await read_exact(1) - mode = public_abi.StorageType(mode[0]) - account = await read_exact(ACCOUNT_ADDR_SIZE) + mode = public_abi.StorageView(mode[0]) + address = await read_exact(ACCOUNT_ADDR_SIZE) slot = await read_exact(SLOT_ID_SIZE) - index = await recv_int() + offset = await recv_int() le = await recv_int() try: - res = await handler.storage_read(mode, account, slot, index, le) + res = await handler.storage_read(mode, address, slot, offset, le) assert len(res) == le except HostException as e: await send_all(bytes([e.error_code])) else: await send_all(bytes([host_fns.Errors.OK])) await send_all(res) - case host_fns.Methods.RESOLVE_CALLCONTRACT_EXECUTOR: + case host_fns.Methods.RESOLVE_CALL_CONTRACT_EXECUTOR: contract_address = Address(await read_exact(ACCOUNT_ADDR_SIZE)) - state_mode = public_abi.StorageType((await read_exact(1))[0]) + state_mode = public_abi.StorageView((await read_exact(1))[0]) advisory_major = await recv_int(1) try: - res = await handler.resolve_callcontract_executor( + res = await handler.resolve_call_contract_executor( contract_address, state_mode, advisory_major, @@ -439,39 +440,41 @@ def emit_host_loop_stats(): raise Exception( "CONSUME_RESULT is not supported in this host loop implementation, use manager provided one" ) - case host_fns.Methods.CONSUME_FUEL: - gas = await recv_int(32) - await handler.consume_gas(gas) - case host_fns.Methods.ETH_CALL: - account = await read_exact(ACCOUNT_ADDR_SIZE) + case host_fns.Methods.CONSUME_TIME_FEE_GEN_WEI: + time_fee_gen_wei = await recv_int(32) + await handler.consume_time_fee_gen_wei(time_fee_gen_wei) + case host_fns.Methods.EXTERNAL_CALL: + address = await read_exact(ACCOUNT_ADDR_SIZE) calldata_len = await recv_int() calldata = await read_exact(calldata_len) try: - res = await handler.eth_call(account, calldata) + res = await handler.external_call(address, calldata) except HostException as e: await send_all(bytes([e.error_code])) else: await send_all(bytes([host_fns.Errors.OK])) await send_int(len(res)) await send_all(res) - case host_fns.Methods.GET_BALANCE: - account = await read_exact(ACCOUNT_ADDR_SIZE) + case host_fns.Methods.GET_BALANCE_GEN_WEI: + address = await read_exact(ACCOUNT_ADDR_SIZE) try: - res = await handler.get_balance(account) + res = await handler.get_balance_gen_wei(address) except HostException as e: await send_all(bytes([e.error_code])) else: await send_all(bytes([host_fns.Errors.OK])) await send_all(res.to_bytes(32, byteorder="little", signed=False)) - case host_fns.Methods.REMAINING_FUEL_AS_GEN: + case host_fns.Methods.GET_REMAINING_TIME_FEE_GEN_WEI: try: - res = await handler.remaining_fuel_as_gen() + time_fee_gen_wei = await handler.get_remaining_time_fee_gen_wei() except HostException as e: await send_all(bytes([e.error_code])) else: await send_all(bytes([host_fns.Errors.OK])) - await send_all(res.to_bytes(32, byteorder="little", signed=False)) + await send_all( + time_fee_gen_wei.to_bytes(32, byteorder="little", signed=False) + ) case host_fns.Methods.NOTIFY_NONDET_DISAGREEMENT: call_no = await recv_int() await handler.notify_nondet_disagreement(call_no) @@ -481,7 +484,8 @@ def emit_host_loop_stats(): class ConsumedResultDecodeError(Exception): - """`consumed_result` bytes did not parse into a `ConsumedResult` at all. + """ + `consumed_result` bytes did not parse into a `ConsumedResult` at all. Distinct from `ConsumedResult.internal_error(...)`, which is itself a valid (if unhappy) result value produced from bytes that *did* parse: @@ -497,10 +501,10 @@ class ConsumedResult: """The decoded `consumed_result` blob: a `ResultCode` byte plus calldata.""" execution_hash: bytes - result_kind: public_abi.ResultCode + result_kind: host_fns.ResultCode result_data: gvm_calldata.Decoded result_fingerprint: ResultFingerprint | None = None - result_storage_changes: list[tuple[bytes, bytes]] = field(default_factory=list) + result_storage_deltas: list[tuple[bytes, bytes]] = field(default_factory=list) result_emissions: list[ResultEmission] = field(default_factory=list) result_nondet_results: list[bytes] = field(default_factory=list) data_fees_remaining: list[int] = field(default_factory=list) @@ -509,7 +513,7 @@ class ConsumedResult: def internal_error(cls, message: str) -> "ConsumedResult": return cls( execution_hash=b"", - result_kind=public_abi.ResultCode.INTERNAL_ERROR, + result_kind=host_fns.ResultCode.INTERNAL_ERROR, result_data=message, ) @@ -528,7 +532,11 @@ def decode(cls, raw: typing.Any) -> "ConsumedResult": ) empty = not as_bytes if not empty: - result_kind = public_abi.ResultCode(as_bytes[0]) + result_kind = host_fns.ResultCode(as_bytes[0]) + if result_kind == host_fns.ResultCode.FATAL_VM_ERROR: + raise ValueError( + "fatal_vm_error crossed the top-level result boundary" + ) decoded = gvm_calldata.decode(as_bytes[1:]) except Exception as exc: # Unreadable bytes are a protocol violation rather than a result, so @@ -549,7 +557,7 @@ def decode(cls, raw: typing.Any) -> "ConsumedResult": result_kind=result_kind, result_data=decoded.get("data"), result_fingerprint=decoded.get("fingerprint"), - result_storage_changes=decoded.get("storage_changes", []), + result_storage_deltas=decoded.get("storage_deltas", []), result_emissions=decoded.get("emissions", []), result_nondet_results=decoded.get("nondet_results", []), data_fees_remaining=decoded.get("data_fees_remaining", []), @@ -566,10 +574,10 @@ class RunHostAndProgramRes: execution_hash: bytes - result_kind: public_abi.ResultCode + result_kind: host_fns.ResultCode result_data: gvm_calldata.Decoded result_fingerprint: ResultFingerprint | None - result_storage_changes: list[tuple[bytes, bytes]] + result_storage_deltas: list[tuple[bytes, bytes]] result_emissions: list[ResultEmission] result_nondet_results: list[bytes] data_fees_remaining: list[int] @@ -621,7 +629,8 @@ class ManagerRunLost(Exception): class ManagerRunNotStarted(Exception): - """A run never reached execution, so retrying it re-executes nothing. + """ + A run never reached execution, so retrying it re-executes nothing. Raised for the two ways the manager can refuse before the genvm runs -- a rejected RUN request and a `failed_to_start` terminal. Retry policy belongs @@ -639,7 +648,8 @@ def __init__(self, message: str, *, retryable: bool, reason: str): class TerminalResultUnavailable(Exception): - """A run reached a terminal `finished` state, but its result could not be + """ + A run reached a terminal `finished` state, but its result could not be retrieved or decoded -- artifact transfer failed, or `consumed_result` was empty/malformed. @@ -769,7 +779,8 @@ async def attach(self, boot_id: int, genvm_id: int) -> RunState: def _require_current_generation( self, boot_id: int, genvm_id: int, *, op: str ) -> None: - """Refuse to act on a run from a manager generation we have since left. + """ + Refuse to act on a run from a manager generation we have since left. `CANCEL`/`ACK`/`GET_ARTIFACT` identify a run to the manager by `genvm_id` alone -- the wire protocol has no `boot_id` field for them @@ -1134,7 +1145,8 @@ def _duration_string(seconds: float | None) -> str | None: def _decode_genvm_log(data: bytes) -> list[dict[str, typing.Any]]: - """Decodes the genvm log artifact, which is json lines. + """ + Decodes the genvm log artifact, which is json lines. Split on newline bytes only. `str.splitlines` also breaks on U+2028, U+2029 and U+0085, none of which json escapes and all of which therefore appear raw @@ -1192,7 +1204,7 @@ async def run_genvm( max_exec_mins = 20 if timeout is not None: max_exec_mins = int(max(max_exec_mins, (timeout * 1.5 + 59) // 60)) - timestamp = message.get("datetime", "2024-11-26T06:42:42.424242Z") + timestamp = message.get("transaction_timestamp", "2024-11-26T06:42:42.424242Z") deadline = _duration_string(timeout) host_genvm_id = typing.cast(str | None, request_extra.get("host_genvm_id")) if host_genvm_id is None: @@ -1342,9 +1354,9 @@ async def cancel_on_shutdown(): ) from exc if ( status.get("cause") == "deadline" - and consumed.result_kind != public_abi.ResultCode.RETURN + and consumed.result_kind != host_fns.ResultCode.RETURN ): - consumed.result_kind = public_abi.ResultCode.VM_ERROR + consumed.result_kind = host_fns.ResultCode.VM_ERROR consumed.result_data = str(public_abi.VmError.timeout()) else: # A failed_to_start terminal means the genvm was accepted but never @@ -1364,7 +1376,7 @@ async def cancel_on_shutdown(): raise ManagerRunNotStarted(error, retryable=retryable, reason=reason) vm_error_description: str | None = None - if consumed.result_kind == public_abi.ResultCode.VM_ERROR and isinstance( + if consumed.result_kind == host_fns.ResultCode.VM_ERROR and isinstance( consumed.result_data, str ): try: @@ -1389,7 +1401,7 @@ async def cancel_on_shutdown(): result_kind=consumed.result_kind, result_data=consumed.result_data, result_fingerprint=consumed.result_fingerprint, - result_storage_changes=consumed.result_storage_changes, + result_storage_deltas=consumed.result_storage_deltas, result_emissions=consumed.result_emissions, result_nondet_results=consumed.result_nondet_results, data_fees_remaining=consumed.data_fees_remaining, diff --git a/backend/node/genvm/origin/fees.py b/backend/node/genvm/origin/fees.py index f005a1ad2..4546185c5 100644 --- a/backend/node/genvm/origin/fees.py +++ b/backend/node/genvm/origin/fees.py @@ -1,4 +1,5 @@ -"""Message-fee allocation tree types. +""" +Message-fee allocation tree types. Mirrors the executor's `genvm_common::domain::fees` module: the fee parameters and the nested `MessageAllocationNode` tree that is passed alongside an @@ -44,7 +45,7 @@ class MessageAllocationNode(typing.TypedDict): call_key: bytes | None budget: int # Lifecycle the node matches against (only meaningful for internal messages). - on: typing.Literal["finalized", "accepted"] + on: typing.Literal["finalized", "decided"] fee_params: MessageAllocationNodeParams # Nested allocation subtree; the chain receives this flattened to # parent-pointer form. @@ -66,11 +67,11 @@ class MessageAllocationNode(typing.TypedDict): "children": [], } -DEFAULT_INTERNAL_ACC_MESSAGE_ALLOC: MessageAllocationNode = { +DEFAULT_INTERNAL_DEC_MESSAGE_ALLOC: MessageAllocationNode = { "budget": 2**200, "recipient": None, "call_key": None, - "on": "accepted", + "on": "decided", "fee_params": { "Internal": { "execution_budget_per_round": 2**10, diff --git a/backend/node/genvm/origin/host_fns.py b/backend/node/genvm/origin/host_fns.py index 5464eed07..ac74c9ea1 100644 --- a/backend/node/genvm/origin/host_fns.py +++ b/backend/node/genvm/origin/host_fns.py @@ -9,40 +9,30 @@ class Methods(IntEnum): STORAGE_READ = 0 - CONSUME_FUEL = 1 - ETH_CALL = 2 - GET_BALANCE = 3 - REMAINING_FUEL_AS_GEN = 4 + CONSUME_TIME_FEE_GEN_WEI = 1 + EXTERNAL_CALL = 2 + GET_BALANCE_GEN_WEI = 3 + GET_REMAINING_TIME_FEE_GEN_WEI = 4 NOTIFY_NONDET_DISAGREEMENT = 5 CONSUME_RESULT = 6 - RESOLVE_CALLCONTRACT_EXECUTOR = 7 + RESOLVE_CALL_CONTRACT_EXECUTOR = 7 RUN_NESTED = 8 +class ResultCode(IntEnum): + RETURN = 0 + USER_ERROR = 1 + VM_ERROR = 2 + INTERNAL_ERROR = 3 + FATAL_VM_ERROR = 4 + + class Errors(IntEnum): OK = 0 EVM_REVERTED = 1 FORBIDDEN = 2 -class VmErrorDetail: - __slots__ = ("value",) - - def __init__(self, value: str): - self.value = value - - def __str__(self) -> str: - return self.value - - @staticmethod - def internal() -> "VmErrorDetail": - return VmErrorDetail("internal") - - @staticmethod - def external() -> "VmErrorDetail": - return VmErrorDetail("external") - - CURRENT_MAJOR: typing.Final[int] = 0 diff --git a/backend/node/genvm/origin/log_asserts.py b/backend/node/genvm/origin/log_asserts.py index e1a03f9fa..4f955362d 100644 --- a/backend/node/genvm/origin/log_asserts.py +++ b/backend/node/genvm/origin/log_asserts.py @@ -1,4 +1,5 @@ -"""Assertions over the executor's structured log records. +""" +Assertions over the executor's structured log records. The manager captures every executor-emitted log line and surfaces them as ``RunHostAndProgramRes.genvm_log`` — a list of JSON objects shaped like @@ -6,15 +7,13 @@ under ``debug_mode >= safe-unbounded`` (integration tests run ``unsafe``), so no record is evicted regardless of how chatty the run is. -The ADR-012 load action emits one stable ``"runner load"`` record per load, -carrying: +The load action emits one stable ``"runner load"`` record per load, carrying: - ``runner`` — the canonical runner id (``chain:``/``custom:``/``name:hash``); -- ``runner_load_cost`` — the flat per-load constant - (``public_abi::memory_limiter_consts::RUNNER_LOAD_COST``); +- ``runner_load_cost`` — the flat per-load memory limiter constant; - ``size`` — the charged content size (archive ``total_size``); - ``status`` — ``"charged"`` (first load in this VM) or ``"cached"`` - (already in the VM's loaded set — free). + (already in the VM's loaded set — free). This module is deliberately message-agnostic: a matcher's ``message`` defaults to ``"runner load"`` but may name any message, so the same machinery serves @@ -62,7 +61,8 @@ def extract( match: dict | None = None, runner_prefix: str | None = None, ) -> list[dict]: - """Return the ``genvm_log`` records matching ``match`` (defaulting to the + """ + Return the ``genvm_log`` records matching ``match`` (defaulting to the ``runner load`` message) and an optional ``runner`` prefix.""" return _select(genvm_log, match or {}, runner_prefix) diff --git a/backend/node/genvm/origin/public_abi.py b/backend/node/genvm/origin/public_abi.py index ff6fda271..e03cd475d 100644 --- a/backend/node/genvm/origin/public_abi.py +++ b/backend/node/genvm/origin/public_abi.py @@ -11,13 +11,12 @@ class ResultCode(IntEnum): RETURN = 0 USER_ERROR = 1 VM_ERROR = 2 - INTERNAL_ERROR = 3 -class StorageType(IntEnum): +class StorageView(IntEnum): DEFAULT = 0 - LATEST_FINAL = 1 - LATEST_NON_FINAL = 2 + LATEST_FINALIZED = 1 + LATEST_DECIDED = 2 class EntryKind(IntEnum): @@ -30,17 +29,6 @@ class Permissions(IntEnum): CAN_USE_BALANCE_FOR_MESSAGE_FEES = 1 -class _MemoryLimiterConsts(typing.NamedTuple): - TABLE_ENTRY: int = 64 - FILE_MAPPING: int = 256 - FD_ALLOCATION: int = 96 - RUNNER_LOAD_COST: int = 4096 - VM_SPAWN_COST: int = 134217728 - - -memory_limiter_consts: typing.Final = _MemoryLimiterConsts() - - class _RootOffsets(typing.NamedTuple): MAJOR: int = 0 CONTRACT: int = 1 @@ -54,24 +42,45 @@ class _RootOffsets(typing.NamedTuple): root_offsets: typing.Final = _RootOffsets() -class _TopLimits(typing.NamedTuple): - NONDET_BLOCKS: int = 4096 - LOCKED_SLOTS: int = 256 - UPGRADERS: int = 32 - VM_RECURSION: int = 512 - WEB_REQUEST_MIN_SPACE: int = 65536 - WEB_RENDER_MIN_SPACE: int = 134217728 - MAX_FDS: int = 1024 - WASM_CALL_DEPTH: int = 1024 - WASM_STACK_VALUE_SLOTS: int = 65535 +class SpecialMethod(StrEnum): + GET_SCHEMA = "#get-schema" + ERRORED_MESSAGE = "#error" -top_limits: typing.Final = _TopLimits() +class _VmErrorLeaderFaultNondetOutput: + @staticmethod + def absent() -> "VmError": + return VmError("leader_fault nondet_output absent") + @staticmethod + def malformed() -> "VmError": + return VmError("leader_fault nondet_output malformed") -class SpecialMethod(StrEnum): - GET_SCHEMA = "#get-schema" - ERRORED_MESSAGE = "#error" + @staticmethod + def uses_this_error() -> "_VmErrorLeaderFaultNondetOutputUsesThisError": + return _VmErrorLeaderFaultNondetOutputUsesThisError() + + @staticmethod + def extra() -> "_VmErrorLeaderFaultNondetOutputExtra": + return _VmErrorLeaderFaultNondetOutputExtra() + + +class _VmErrorLeaderFaultNondetOutputUsesThisError: + @staticmethod + def val_str(v: str) -> "VmError": + return VmError(f"leader_fault nondet_output uses_this_error {v}") + + +class _VmErrorLeaderFaultNondetOutputExtra: + @staticmethod + def val_str(v: str) -> "VmError": + return VmError(f"leader_fault nondet_output extra {v}") + + +class _VmErrorLeaderFault: + @staticmethod + def nondet_output() -> "_VmErrorLeaderFaultNondetOutput": + return _VmErrorLeaderFaultNondetOutput() class _VmErrorWasmTrap: @@ -154,28 +163,66 @@ def wasm_table() -> "VmError": return VmError("out_of memory wasm_table") +class _VmErrorOutOfReceiptMessage: + @staticmethod + def val() -> "VmError": + return VmError("out_of receipt message") + + @staticmethod + def internal() -> "VmError": + return VmError("out_of receipt message # internal") + + class _VmErrorOutOfReceipt: @staticmethod def nondet_output() -> "VmError": return VmError("out_of receipt nondet_output") - @staticmethod - def message() -> "VmError": - return VmError("out_of receipt message") - @staticmethod def event() -> "VmError": return VmError("out_of receipt event") + @staticmethod + def message() -> "_VmErrorOutOfReceiptMessage": + return _VmErrorOutOfReceiptMessage() -class _VmErrorOutOfMessageFee: + +class _VmErrorOutOfMessageFeeTotal: @staticmethod - def total() -> "VmError": + def val() -> "VmError": return VmError("out_of message_fee total") @staticmethod - def node() -> "VmError": - return VmError("out_of message_fee node") + def internal() -> "VmError": + return VmError("out_of message_fee total # internal") + + @staticmethod + def external() -> "VmError": + return VmError("out_of message_fee total # external") + + +class _VmErrorOutOfMessageFeeAllocationBudget: + @staticmethod + def val() -> "VmError": + return VmError("out_of message_fee allocation_budget") + + @staticmethod + def internal() -> "VmError": + return VmError("out_of message_fee allocation_budget # internal") + + @staticmethod + def external() -> "VmError": + return VmError("out_of message_fee allocation_budget # external") + + +class _VmErrorOutOfMessageFee: + @staticmethod + def total() -> "_VmErrorOutOfMessageFeeTotal": + return _VmErrorOutOfMessageFeeTotal() + + @staticmethod + def allocation_budget() -> "_VmErrorOutOfMessageFeeAllocationBudget": + return _VmErrorOutOfMessageFeeAllocationBudget() class _VmErrorOutOf: @@ -184,8 +231,8 @@ def storage() -> "VmError": return VmError("out_of storage") @staticmethod - def vm_recursion() -> "VmError": - return VmError("out_of vm_recursion") + def subvm_recursion() -> "VmError": + return VmError("out_of subvm_recursion") @staticmethod def nondet_blocks() -> "VmError": @@ -216,11 +263,21 @@ def message_fee() -> "_VmErrorOutOfMessageFee": return _VmErrorOutOfMessageFee() -class _VmErrorFee: +class _VmErrorFeeNoMatchingAllocation: + @staticmethod + def val() -> "VmError": + return VmError("fee no_matching_allocation") + @staticmethod - def no_matching_node() -> "VmError": - return VmError("fee no_matching_node") + def internal() -> "VmError": + return VmError("fee no_matching_allocation # internal") + @staticmethod + def external() -> "VmError": + return VmError("fee no_matching_allocation # external") + + +class _VmErrorFee: @staticmethod def below_minimum() -> "VmError": return VmError("fee below_minimum") @@ -229,6 +286,10 @@ def below_minimum() -> "VmError": def too_many_rounds() -> "VmError": return VmError("fee too_many_rounds") + @staticmethod + def no_matching_allocation() -> "_VmErrorFeeNoMatchingAllocation": + return _VmErrorFeeNoMatchingAllocation() + class _VmErrorEvm: @staticmethod @@ -236,6 +297,16 @@ def reverted() -> "VmError": return VmError("evm reverted") +class _VmErrorInvalidContractRunner: + @staticmethod + def absent() -> "VmError": + return VmError("invalid_contract runner absent") + + @staticmethod + def malformed() -> "VmError": + return VmError("invalid_contract runner malformed") + + class _VmErrorInvalidContractWasm: @staticmethod def validating() -> "VmError": @@ -255,22 +326,18 @@ class _VmErrorInvalidContract: def val() -> "VmError": return VmError("invalid_contract") - @staticmethod - def absent_runner_comment() -> "VmError": - return VmError("invalid_contract absent_runner_comment") - @staticmethod def not_utf8_text() -> "VmError": return VmError("invalid_contract not_utf8_text") - @staticmethod - def malformed_runner() -> "VmError": - return VmError("invalid_contract malformed_runner") - @staticmethod def major_mismatch() -> "VmError": return VmError("invalid_contract major_mismatch") + @staticmethod + def runner() -> "_VmErrorInvalidContractRunner": + return _VmErrorInvalidContractRunner() + @staticmethod def wasm() -> "_VmErrorInvalidContractWasm": return _VmErrorInvalidContractWasm() @@ -296,12 +363,16 @@ def timeout() -> "VmError": return VmError("timeout") @staticmethod - def absent_leader_nondet_output() -> "VmError": - return VmError("absent_leader_nondet_output") + def malformed_entry() -> "VmError": + return VmError("malformed_entry") + + @staticmethod + def forbidden() -> "VmError": + return VmError("forbidden") @staticmethod - def host_forbidden() -> "VmError": - return VmError("host_forbidden") + def leader_fault() -> "_VmErrorLeaderFault": + return _VmErrorLeaderFault() @staticmethod def exit_code() -> "_VmErrorExitCode": diff --git a/backend/protocol_rpc/endpoints.py b/backend/protocol_rpc/endpoints.py index e652cf021..a5ff3945d 100644 --- a/backend/protocol_rpc/endpoints.py +++ b/backend/protocol_rpc/endpoints.py @@ -1913,7 +1913,7 @@ def _validate_genvm_executor_selector(sim_config: dict | None) -> None: data={"genvm_executor_selector": genvm_executor_selector}, ) # The pin is persisted on the contract and only read back much later, by - # `resolve_callcontract_executor` in the middle of a run. Validating it here + # `resolve_call_contract_executor` in the middle of a run. Validating it here # turns an unusable pin into a rejected transaction instead of a contract # that fails every call it takes part in. # `fullmatch`, not `match`: `$` also matches in front of a final newline, so diff --git a/backend/protocol_rpc/fees.py b/backend/protocol_rpc/fees.py index 5fd2d8f62..6d84752b4 100644 --- a/backend/protocol_rpc/fees.py +++ b/backend/protocol_rpc/fees.py @@ -2260,9 +2260,10 @@ def _genvm_message_allocation_node( def _genvm_message_on(node: dict[str, Any]) -> str: + # `decided` is GenVM's name for the lifecycle Studio calls `accepted`. if int(node["messageType"]) == MESSAGE_TYPE_EXTERNAL: return "finalized" - return "accepted" if bool(node["onAcceptance"]) else "finalized" + return "decided" if bool(node["onAcceptance"]) else "finalized" def _genvm_recipient( @@ -2327,7 +2328,7 @@ def _genvm_unmetered_message_fee_allocation() -> list[dict[str, Any]]: "recipient": None, "call_key": None, "budget": budget, - "on": "accepted", + "on": "decided", "fee_params": internal_fee_params, "children": [], }, diff --git a/docker/scripts/download_genvm.sh b/docker/scripts/download_genvm.sh index 7dcd160e2..17001140b 100755 --- a/docker/scripts/download_genvm.sh +++ b/docker/scripts/download_genvm.sh @@ -191,13 +191,17 @@ elif [[ -n "$REPO_ROOT" ]]; then | sed 's/^py-genlayer://' | sort -u || true) fi -runner_tar_for_pin() { - local pin=$1 candidate - candidate="$INSTALL_DIR/runners/py-genlayer/${pin:0:2}/${pin:2}.tar" - if [[ -f "$candidate" ]]; then - printf '%s\n' "$candidate" - return 0 - fi +runner_archive_for_pin() { + local pin=$1 candidate ext + # The shared tree moved from .tar to .zip in the v0.3.0-rc7 line; the + # legacy-runners tree of an older executor still ships .tar. + for ext in zip tar; do + candidate="$INSTALL_DIR/runners/py-genlayer/${pin:0:2}/${pin:2}.$ext" + if [[ -f "$candidate" ]]; then + printf '%s\n' "$candidate" + return 0 + fi + done for candidate in "$INSTALL_DIR"/executor/*/legacy-runners/py-genlayer/"${pin:0:2}"/"${pin:2}".tar; do if [[ -f "$candidate" ]]; then printf '%s\n' "$candidate" @@ -220,10 +224,10 @@ if [[ -n "$RUNNER_PINS_FILE" || -n "$REPO_ROOT" ]]; then fi while read -r RUNNER_PIN; do [[ -n "$RUNNER_PIN" ]] || continue - if ! RUNNER_TAR=$(runner_tar_for_pin "$RUNNER_PIN"); then + if ! RUNNER_ARCHIVE=$(runner_archive_for_pin "$RUNNER_PIN"); then error_exit "Pinned py-genlayer runner is missing: py-genlayer:$RUNNER_PIN (looked in $INSTALL_DIR/runners and $INSTALL_DIR/executor/*/legacy-runners)." fi - echo "Runner pin OK: py-genlayer:$RUNNER_PIN -> ${RUNNER_TAR#"$INSTALL_DIR"/}" + echo "Runner pin OK: py-genlayer:$RUNNER_PIN -> ${RUNNER_ARCHIVE#"$INSTALL_DIR"/}" done <<<"$RUNNER_PINS" fi diff --git a/examples/contracts/_hello_world.py b/examples/contracts/_hello_world.py index 55dec33a1..273df4263 100644 --- a/examples/contracts/_hello_world.py +++ b/examples/contracts/_hello_world.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } # Always put above lines as first in the contract file # In actual genlayer network `:latest` is not allowed and hash must be specified diff --git a/examples/contracts/faucet.py b/examples/contracts/faucet.py index d765b6e3f..075eae651 100644 --- a/examples/contracts/faucet.py +++ b/examples/contracts/faucet.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/examples/contracts/football_prediction_market.py b/examples/contracts/football_prediction_market.py index 287174d64..492c07bf2 100644 --- a/examples/contracts/football_prediction_market.py +++ b/examples/contracts/football_prediction_market.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/examples/contracts/llm_erc20.py b/examples/contracts/llm_erc20.py index 6f6a062f4..419794527 100644 --- a/examples/contracts/llm_erc20.py +++ b/examples/contracts/llm_erc20.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import json diff --git a/examples/contracts/log_indexer.py b/examples/contracts/log_indexer.py index 15fdbd17d..b36391db9 100644 --- a/examples/contracts/log_indexer.py +++ b/examples/contracts/log_indexer.py @@ -1,8 +1,8 @@ # v0.3.0 # { # "Seq": [ -# { "Depends": "py-lib-genlayer-embeddings:kr2rb2dcp01mw9khpg3tg2jasx4f82mcsy3eg08rjj1zdcm9q350" }, -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-lib-genlayer-embeddings:hqpree1t3470fnac2aeee1y5c2205k22bgk1p98sg8m3s1ndmxbg" }, +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } # ] # } diff --git a/examples/contracts/storage.py b/examples/contracts/storage.py index 0a95867c8..6affe74b3 100644 --- a/examples/contracts/storage.py +++ b/examples/contracts/storage.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/examples/contracts/tip_jar.py b/examples/contracts/tip_jar.py index 7c12d8b8f..b6c3e3226 100644 --- a/examples/contracts/tip_jar.py +++ b/examples/contracts/tip_jar.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/examples/contracts/user_storage.py b/examples/contracts/user_storage.py index 1ef7c9b1a..227c67ffb 100644 --- a/examples/contracts/user_storage.py +++ b/examples/contracts/user_storage.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/examples/contracts/wizard_of_coin.py b/examples/contracts/wizard_of_coin.py index 63a873a1c..947852cab 100644 --- a/examples/contracts/wizard_of_coin.py +++ b/examples/contracts/wizard_of_coin.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl import json diff --git a/tests/db-sqlalchemy/reroute_to_migration_backfill_test.py b/tests/db-sqlalchemy/reroute_to_migration_backfill_test.py index b4600bd33..c9f8dee89 100644 --- a/tests/db-sqlalchemy/reroute_to_migration_backfill_test.py +++ b/tests/db-sqlalchemy/reroute_to_migration_backfill_test.py @@ -21,7 +21,7 @@ import backend.node.genvm.origin.calldata as gvm_calldata from backend.database_handler.contract_snapshot import ContractSnapshot from backend.node.genvm.base import Host, is_valid_executor_selector -from backend.node.genvm.origin.public_abi import StorageType +from backend.node.genvm.origin.public_abi import StorageView from backend.node.types import Address from conftest import _alembic_config @@ -32,7 +32,7 @@ class _SingleContractStateProxy: """Minimal `StateProxy` stand-in: answers `genvm_executor_selector_for` - for one address, the way `Host.resolve_callcontract_executor` reads it + for one address, the way `Host.resolve_call_contract_executor` reads it mid-run.""" def __init__(self, address: str, genvm_executor_selector: str | None): @@ -122,7 +122,7 @@ def test_migration_backfills_legacy_selector_for_pre_existing_contracts( # The backfilled value must be usable end-to-end for a cross-version # call, not merely present in the column: it has to pass the same # selector grammar submit-time validation enforces, load back through - # `ContractSnapshot`, and let `resolve_callcontract_executor` answer a + # `ContractSnapshot`, and let `resolve_call_contract_executor` answer a # caller on another line with a routable selector instead of raising. assert is_valid_executor_selector(LEGACY_EXECUTOR_SELECTOR) @@ -135,8 +135,8 @@ def test_migration_backfills_legacy_selector_for_pre_existing_contracts( contract_address, snapshot.genvm_executor_selector ) resolved = asyncio.run( - host.resolve_callcontract_executor( - Address(contract_address), StorageType.DEFAULT, 6 + host.resolve_call_contract_executor( + Address(contract_address), StorageView.DEFAULT, 6 ) ) assert gvm_calldata.decode(resolved) == { diff --git a/tests/db-sqlalchemy/test_genvm_host_async_callbacks.py b/tests/db-sqlalchemy/test_genvm_host_async_callbacks.py index ca57a999a..ae123ddd5 100644 --- a/tests/db-sqlalchemy/test_genvm_host_async_callbacks.py +++ b/tests/db-sqlalchemy/test_genvm_host_async_callbacks.py @@ -4,7 +4,7 @@ import pytest from backend.node.genvm.base import Host -from backend.node.genvm.origin.public_abi import StorageType +from backend.node.genvm.origin.public_abi import StorageView class _SlowStateProxy: @@ -28,7 +28,7 @@ async def test_storage_read_runs_off_event_loop(): storage_task = asyncio.create_task( host.storage_read( - StorageType.DEFAULT, + StorageView.DEFAULT, b"\x01" * 20, b"\x02" * 32, 0, diff --git a/tests/direct/contracts/error_llm_contract_direct.py b/tests/direct/contracts/error_llm_contract_direct.py index 7d97d4302..2c42b2d31 100644 --- a/tests/direct/contracts/error_llm_contract_direct.py +++ b/tests/direct/contracts/error_llm_contract_direct.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl import json diff --git a/tests/direct/contracts/error_web_contract_direct.py b/tests/direct/contracts/error_web_contract_direct.py index bd7689686..2efde9b49 100644 --- a/tests/direct/contracts/error_web_contract_direct.py +++ b/tests/direct/contracts/error_web_contract_direct.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/direct/storage_read_bench.py b/tests/direct/storage_read_bench.py index 4d0af7abd..a257f79e7 100644 --- a/tests/direct/storage_read_bench.py +++ b/tests/direct/storage_read_bench.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/company_naming.py b/tests/integration/icontracts/contracts/company_naming.py index fb7077693..1c122fe7b 100644 --- a/tests/integration/icontracts/contracts/company_naming.py +++ b/tests/integration/icontracts/contracts/company_naming.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import json import genlayer as gl diff --git a/tests/integration/icontracts/contracts/error_execution_contract.py b/tests/integration/icontracts/contracts/error_execution_contract.py index 0af4d6b44..29bc0dc63 100644 --- a/tests/integration/icontracts/contracts/error_execution_contract.py +++ b/tests/integration/icontracts/contracts/error_execution_contract.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/error_llm_contract.py b/tests/integration/icontracts/contracts/error_llm_contract.py index ae091a7b7..8d945d921 100644 --- a/tests/integration/icontracts/contracts/error_llm_contract.py +++ b/tests/integration/icontracts/contracts/error_llm_contract.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl import json diff --git a/tests/integration/icontracts/contracts/error_web_contract.py b/tests/integration/icontracts/contracts/error_web_contract.py index e4f16c9af..a51934270 100644 --- a/tests/integration/icontracts/contracts/error_web_contract.py +++ b/tests/integration/icontracts/contracts/error_web_contract.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/integration/icontracts/contracts/faucet.py b/tests/integration/icontracts/contracts/faucet.py index b35fffd58..538364d98 100644 --- a/tests/integration/icontracts/contracts/faucet.py +++ b/tests/integration/icontracts/contracts/faucet.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/genvm_smoke_v1.py b/tests/integration/icontracts/contracts/genvm_smoke_v1.py index 487fbaa9b..0ccd80519 100644 --- a/tests/integration/icontracts/contracts/genvm_smoke_v1.py +++ b/tests/integration/icontracts/contracts/genvm_smoke_v1.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/integration/icontracts/contracts/intelligent_oracle.py b/tests/integration/icontracts/contracts/intelligent_oracle.py index 24dd959a8..17923a1ff 100644 --- a/tests/integration/icontracts/contracts/intelligent_oracle.py +++ b/tests/integration/icontracts/contracts/intelligent_oracle.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import json from enum import Enum diff --git a/tests/integration/icontracts/contracts/intelligent_oracle_factory.py b/tests/integration/icontracts/contracts/intelligent_oracle_factory.py index 4e38cc778..3318c32e5 100644 --- a/tests/integration/icontracts/contracts/intelligent_oracle_factory.py +++ b/tests/integration/icontracts/contracts/intelligent_oracle_factory.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/integration/icontracts/contracts/multi_file_contract/other.py b/tests/integration/icontracts/contracts/multi_file_contract/other.py index d867274a5..0da3cc5c1 100644 --- a/tests/integration/icontracts/contracts/multi_file_contract/other.py +++ b/tests/integration/icontracts/contracts/multi_file_contract/other.py @@ -1,4 +1,4 @@ -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/integration/icontracts/contracts/multi_file_contract/runner.json b/tests/integration/icontracts/contracts/multi_file_contract/runner.json index 132e5a1e1..a9f972e74 100644 --- a/tests/integration/icontracts/contracts/multi_file_contract/runner.json +++ b/tests/integration/icontracts/contracts/multi_file_contract/runner.json @@ -1,3 +1,3 @@ { - "Depends": "py-genlayer-multi:mgqted4jvgzwawnd7sp7kjhfwwpr83a7wxhhdxcs4yk07jzt9c9g" + "Depends": "py-genlayer-multi:faykzar6hr5ehfm07jatm69erx6nv96wmz1ab1dszjthbcgre3m0" } diff --git a/tests/integration/icontracts/contracts/multi_read_erc20.py b/tests/integration/icontracts/contracts/multi_read_erc20.py index 727d432de..b33c9c616 100644 --- a/tests/integration/icontracts/contracts/multi_read_erc20.py +++ b/tests/integration/icontracts/contracts/multi_read_erc20.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/multi_tenant_storage.py b/tests/integration/icontracts/contracts/multi_tenant_storage.py index fa083fc2b..ee210066a 100644 --- a/tests/integration/icontracts/contracts/multi_tenant_storage.py +++ b/tests/integration/icontracts/contracts/multi_tenant_storage.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/payable_escrow.py b/tests/integration/icontracts/contracts/payable_escrow.py index 10d4c3acb..a0dafd188 100644 --- a/tests/integration/icontracts/contracts/payable_escrow.py +++ b/tests/integration/icontracts/contracts/payable_escrow.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/read_erc20.py b/tests/integration/icontracts/contracts/read_erc20.py index 9da658db4..3433c8507 100644 --- a/tests/integration/icontracts/contracts/read_erc20.py +++ b/tests/integration/icontracts/contracts/read_erc20.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/integration/icontracts/contracts/utf8_roundtrip_contract.py b/tests/integration/icontracts/contracts/utf8_roundtrip_contract.py index a8f4b60b7..fbfc860fe 100644 --- a/tests/integration/icontracts/contracts/utf8_roundtrip_contract.py +++ b/tests/integration/icontracts/contracts/utf8_roundtrip_contract.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl diff --git a/tests/integration/test_deploy_reroute_to.py b/tests/integration/test_deploy_reroute_to.py index 72457e55b..bf0268cfb 100644 --- a/tests/integration/test_deploy_reroute_to.py +++ b/tests/integration/test_deploy_reroute_to.py @@ -67,7 +67,7 @@ # Current SDK, current executor. CONTRACT_A = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * @@ -106,7 +106,7 @@ def read_peer_storage(self) -> str: # Synchronous cross-contract call from the current executor into a # contract pinned to the legacy one. The manager runs the callee in a # nested executor of its own, which it only knows to do because the - # host answers `resolve_callcontract_executor` for a callee that + # host answers `resolve_call_contract_executor` for a callee that # carries a `reroute_to`. return gl.contract.get_at(Address(self.peer)).view().get_storage() """ @@ -340,7 +340,7 @@ def test_call_from_current_to_legacy_executor(peers): "v0.2 -> v0.3 synchronous call to an *unpinned* callee. The limit is " "ours, not the legacy executor's: v0.2.17 does issue " "RESOLVE_CALLCONTRACT_EXECUTOR (confirmed in the host call counts), but " - "`Host.resolve_callcontract_executor` answers only for a callee that " + "`Host.resolve_call_contract_executor` answers only for a callee that " "carries a `reroute_to` and returns None for anything else. So the " "answer here is 'stay in-process', the v0.2.17 executor loads A's v0.3 " "code itself, and rejects it with `invalid_contract " @@ -365,7 +365,7 @@ def pinned_peers() -> tuple[str, str]: """The same pair, except A is pinned to the current executor explicitly. The pin changes nothing about how A runs — it is already on that line. It - changes what the *host* can say about A: `resolve_callcontract_executor` + changes what the *host* can say about A: `resolve_call_contract_executor` answers from the callee's stored `reroute_to`, so an unpinned A leaves it with nothing to answer and the caller keeps the callee in-process. """ diff --git a/tests/integration/test_upgrade_contract.py b/tests/integration/test_upgrade_contract.py index 29c1080f1..0ee0423b1 100644 --- a/tests/integration/test_upgrade_contract.py +++ b/tests/integration/test_upgrade_contract.py @@ -162,7 +162,7 @@ def write_contract_method( # ============================================================================= CONTRACT_V1 = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * @@ -197,7 +197,7 @@ def set_name(self, new_name: str) -> None: """ CONTRACT_V2 = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * @@ -236,7 +236,7 @@ def new_method(self) -> str: """ CONTRACT_V3_WITH_NEW_STATE = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * @@ -277,7 +277,7 @@ def set_name(self, new_name: str) -> None: """ INVALID_CONTRACT = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * @@ -288,7 +288,7 @@ def __init__(self): """ SIMPLE_CONTRACT = """# v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/load/contracts/counter.py b/tests/load/contracts/counter.py index 8d9bdd151..1447d8f9b 100644 --- a/tests/load/contracts/counter.py +++ b/tests/load/contracts/counter.py @@ -1,5 +1,5 @@ # v0.3.0 -# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/test_linter_endpoint.py b/tests/test_linter_endpoint.py index d6af15cb5..4b0b42936 100644 --- a/tests/test_linter_endpoint.py +++ b/tests/test_linter_endpoint.py @@ -22,7 +22,7 @@ def get_balance(self) -> u256: # Should return int """ # Valid contract -VALID_CONTRACT = """# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" } +VALID_CONTRACT = """# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } import genlayer as gl from genlayer.types import * diff --git a/tests/unit/consensus/test_validator_exec_timeout.py b/tests/unit/consensus/test_validator_exec_timeout.py index 799c4ddc1..614e0d4ae 100644 --- a/tests/unit/consensus/test_validator_exec_timeout.py +++ b/tests/unit/consensus/test_validator_exec_timeout.py @@ -7,7 +7,7 @@ from backend.consensus.base import CommittingState from backend.database_handler.types import ConsensusData from backend.node.genvm.error_codes import GenVMInternalError, GenVMErrorCode -from backend.node.genvm.origin.public_abi import ResultCode +from backend.node.genvm.origin.host_fns import ResultCode from backend.node.types import ExecutionMode, ExecutionResultStatus, Receipt, Vote diff --git a/tests/unit/test_consumed_result_decode.py b/tests/unit/test_consumed_result_decode.py index 5a5690029..215fdf541 100644 --- a/tests/unit/test_consumed_result_decode.py +++ b/tests/unit/test_consumed_result_decode.py @@ -14,47 +14,47 @@ import pytest import backend.node.genvm.origin.calldata as gvm_calldata -from backend.node.genvm.origin import public_abi +from backend.node.genvm.origin import host_fns from backend.node.genvm.origin.base_host import ( ConsumedResult, ConsumedResultDecodeError, ) -def _encode_valid(result_kind: public_abi.ResultCode, data: dict) -> bytes: +def _encode_valid(result_kind: host_fns.ResultCode, data: dict) -> bytes: return bytes([int(result_kind)]) + gvm_calldata.encode(data) def test_decode_none_is_an_internal_error_result(): result = ConsumedResult.decode(None) - assert result.result_kind == public_abi.ResultCode.INTERNAL_ERROR + assert result.result_kind == host_fns.ResultCode.INTERNAL_ERROR assert result.result_data == "no_result" def test_decode_empty_bytes_is_an_internal_error_result(): result = ConsumedResult.decode(b"") - assert result.result_kind == public_abi.ResultCode.INTERNAL_ERROR + assert result.result_kind == host_fns.ResultCode.INTERNAL_ERROR assert result.result_data == "empty_result" def test_decode_valid_bytes_round_trips(): - raw = _encode_valid(public_abi.ResultCode.RETURN, {"execution_hash": b"\x01\x02"}) + raw = _encode_valid(host_fns.ResultCode.RETURN, {"execution_hash": b"\x01\x02"}) result = ConsumedResult.decode(raw) - assert result.result_kind == public_abi.ResultCode.RETURN + assert result.result_kind == host_fns.ResultCode.RETURN assert result.execution_hash == b"\x01\x02" def test_decode_not_a_mapping_is_an_internal_error_result(): - raw = bytes([int(public_abi.ResultCode.RETURN)]) + gvm_calldata.encode([1, 2, 3]) + raw = bytes([int(host_fns.ResultCode.RETURN)]) + gvm_calldata.encode([1, 2, 3]) result = ConsumedResult.decode(raw) - assert result.result_kind == public_abi.ResultCode.INTERNAL_ERROR + assert result.result_kind == host_fns.ResultCode.INTERNAL_ERROR assert result.result_data == "result is not a mapping" def test_decode_truncated_calldata_raises_a_protocol_error(): # A ResultCode byte with no calldata payload behind it at all. with pytest.raises(ConsumedResultDecodeError): - ConsumedResult.decode(bytes([int(public_abi.ResultCode.RETURN)])) + ConsumedResult.decode(bytes([int(host_fns.ResultCode.RETURN)])) def test_decode_invalid_result_code_raises_a_protocol_error(): @@ -65,9 +65,9 @@ def test_decode_invalid_result_code_raises_a_protocol_error(): def test_decode_base64_string_round_trips(): # The socket reports bytes, the deprecated http shim reports base64. - raw = _encode_valid(public_abi.ResultCode.RETURN, {"execution_hash": b"\x01\x02"}) + raw = _encode_valid(host_fns.ResultCode.RETURN, {"execution_hash": b"\x01\x02"}) result = ConsumedResult.decode(base64.b64encode(raw).decode()) - assert result.result_kind == public_abi.ResultCode.RETURN + assert result.result_kind == host_fns.ResultCode.RETURN assert result.execution_hash == b"\x01\x02" @@ -85,5 +85,5 @@ def test_decode_protocol_error_never_returns_an_internal_error_result(): # execution outcome -- the caller has to be able to tell "the genvm ran # and failed" apart from "we can't tell what the genvm reported". with pytest.raises(ConsumedResultDecodeError) as exc_info: - ConsumedResult.decode(bytes([int(public_abi.ResultCode.RETURN)])) + ConsumedResult.decode(bytes([int(host_fns.ResultCode.RETURN)])) assert not isinstance(exc_info.value, ConsumedResult) diff --git a/tests/unit/test_genvm_debug_mode_gate.py b/tests/unit/test_genvm_debug_mode_gate.py index 3b80c23e8..e5c9d14d8 100644 --- a/tests/unit/test_genvm_debug_mode_gate.py +++ b/tests/unit/test_genvm_debug_mode_gate.py @@ -42,7 +42,7 @@ def test_debug_mode_enabled_when_true(monkeypatch, base_module): def test_debug_mode_disabled_when_false(monkeypatch, base_module): """Prd setting: GENVM_DEBUG_MODE=false → 'safe' so the executor rejects - `py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0` / `:test` runner aliases. + `py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng` / `:test` runner aliases. """ for value in ("false", "FALSE", "0", "no", "off", "anything-else"): monkeypatch.setenv("GENVM_DEBUG_MODE", value) diff --git a/tests/unit/test_host_loop_fuel_widths.py b/tests/unit/test_host_loop_fuel_widths.py index 6dfd60c3a..2735d799c 100644 --- a/tests/unit/test_host_loop_fuel_widths.py +++ b/tests/unit/test_host_loop_fuel_widths.py @@ -16,20 +16,20 @@ def __init__(self, sock, remaining_fuel): async def loop_enter(self, _cancellation): return self.sock - async def consume_gas(self, gas): - self.consumed_gas.append(gas) + async def consume_time_fee_gen_wei(self, time_fee_gen_wei): + self.consumed_gas.append(time_fee_gen_wei) - async def remaining_fuel_as_gen(self): + async def get_remaining_time_fee_gen_wei(self): return self.remaining_fuel async def storage_read(self, *_args): raise AssertionError("storage_read should not be called") - async def eth_call(self, *_args): - raise AssertionError("eth_call should not be called") + async def external_call(self, *_args): + raise AssertionError("external_call should not be called") - async def get_balance(self, *_args): - raise AssertionError("get_balance should not be called") + async def get_balance_gen_wei(self, *_args): + raise AssertionError("get_balance_gen_wei should not be called") async def notify_nondet_disagreement(self, *_args): raise AssertionError("notify_nondet_disagreement should not be called") @@ -47,16 +47,17 @@ async def _recv_exact(sock, size): @pytest.mark.asyncio -async def test_consume_fuel_reads_32_byte_little_endian_u256(): +async def test_consume_time_fee_gen_wei_reads_32_byte_little_endian_u256(): server, client = socket.socketpair() server.setblocking(False) client.setblocking(False) try: gas = (1 << 100) + 7 client.sendall( - bytes([host_fns.Methods.CONSUME_FUEL]) + gas.to_bytes(32, "little") + bytes([host_fns.Methods.CONSUME_TIME_FEE_GEN_WEI]) + + gas.to_bytes(32, "little") ) - # CONSUME_FUEL sends no reply; EOF ends the loop after it is consumed. + # CONSUME_TIME_FEE_GEN_WEI sends no reply; EOF ends the loop after it is consumed. client.shutdown(socket.SHUT_WR) handler = FuelHandler(server, remaining_fuel=12345) @@ -69,13 +70,13 @@ async def test_consume_fuel_reads_32_byte_little_endian_u256(): @pytest.mark.asyncio -async def test_remaining_fuel_as_gen_replies_with_32_byte_little_endian_u256(): +async def test_get_remaining_time_fee_gen_wei_replies_with_32_byte_little_endian_u256(): server, client = socket.socketpair() server.setblocking(False) client.setblocking(False) try: remaining_fuel = 12345 - client.sendall(bytes([host_fns.Methods.REMAINING_FUEL_AS_GEN])) + client.sendall(bytes([host_fns.Methods.GET_REMAINING_TIME_FEE_GEN_WEI])) client.shutdown(socket.SHUT_WR) handler = FuelHandler(server, remaining_fuel=remaining_fuel) diff --git a/tests/unit/test_leader_llm_recovery.py b/tests/unit/test_leader_llm_recovery.py index 62e08e6e7..8320c4c0f 100644 --- a/tests/unit/test_leader_llm_recovery.py +++ b/tests/unit/test_leader_llm_recovery.py @@ -5,7 +5,7 @@ from backend.node.base import Node from backend.node.types import Receipt, ExecutionMode, ExecutionResultStatus, Vote -from backend.node.genvm.origin.public_abi import ResultCode +from backend.node.genvm.origin.host_fns import ResultCode from backend.node.genvm.error_codes import GenVMErrorCode, GenVMInternalError from backend.node.genvm.base import ExecutionError, ExecutionReturn, ExecutionResult from backend.database_handler.contract_snapshot import ContractSnapshot diff --git a/tests/unit/test_node_state_proxy_metrics.py b/tests/unit/test_node_state_proxy_metrics.py index 3429f2c87..7dca358db 100644 --- a/tests/unit/test_node_state_proxy_metrics.py +++ b/tests/unit/test_node_state_proxy_metrics.py @@ -12,7 +12,7 @@ from backend.node.genvm.base import Host as GenVMHost import backend.node.genvm.origin.calldata as gvm_calldata from backend.node.genvm.origin.base_host import RunHostAndProgramRes -from backend.node.genvm.origin.public_abi import ResultCode +from backend.node.genvm.origin.host_fns import ResultCode from backend.node.types import Address, ExecutionMode, ExecutionResultStatus from backend.protocol_rpc.fees import ( GENVM_UNMETERED_DATA_FEE_BUCKET, @@ -138,14 +138,14 @@ def test_host_provide_result_preserves_fee_metadata_from_genvm_emissions(): result_kind=ResultCode.RETURN, result_data=b"ok", result_fingerprint=None, - result_storage_changes=[], + result_storage_deltas=[], result_emissions=[ { - "type": "PostMessage", + "type": "InternalMessage", "address": Address("0x" + "22" * 20), "calldata": ["post", 1], "value": 7, - "on": "accepted", + "on": "decided", "fee_params": { "leader_timeunits_allocation": 6, "validator_timeunits_allocation": 10, @@ -160,7 +160,7 @@ def test_host_provide_result_preserves_fee_metadata_from_genvm_emissions(): "subtree": genvm_subtree, }, { - "type": "DeployContract", + "type": "InternalDeployMessage", "calldata": {"init": True}, "code": b"class Child: pass", "salt_nonce": 9, @@ -172,7 +172,7 @@ def test_host_provide_result_preserves_fee_metadata_from_genvm_emissions(): "allocation_subtree": allocation_subtree, }, { - "type": "EthSend", + "type": "ExternalMessage", "address": Address("0x" + "44" * 20), "calldata": b"\xab\xcd", "value": 5, diff --git a/tests/unit/test_provide_result_readonly.py b/tests/unit/test_provide_result_readonly.py index acac0738d..7c9a565e6 100644 --- a/tests/unit/test_provide_result_readonly.py +++ b/tests/unit/test_provide_result_readonly.py @@ -2,7 +2,7 @@ from backend.node.genvm.base import Context, Host from backend.node.genvm.origin.base_host import RunHostAndProgramRes -from backend.node.genvm.origin.public_abi import ResultCode +from backend.node.genvm.origin.host_fns import ResultCode def _result_with_storage_change(): @@ -15,7 +15,7 @@ def _result_with_storage_change(): result_kind=ResultCode.RETURN, result_data=b"ok", result_fingerprint=None, - result_storage_changes=[(b"\x11" * 32 + (0).to_bytes(4, "big"), b"\xaa")], + result_storage_deltas=[(b"\x11" * 32 + (0).to_bytes(4, "big"), b"\xaa")], result_emissions=[], result_nondet_results=[], data_fees_remaining=[], diff --git a/tests/unit/test_resolve_callcontract_executor.py b/tests/unit/test_resolve_callcontract_executor.py index f7954df78..d0d05c74e 100644 --- a/tests/unit/test_resolve_callcontract_executor.py +++ b/tests/unit/test_resolve_callcontract_executor.py @@ -1,4 +1,4 @@ -"""Nested cross-major call routing: `Host.resolve_callcontract_executor`. +"""Nested cross-major call routing: `Host.resolve_call_contract_executor`. A call target that is pinned to an executor version (its snapshot carries a `reroute_to`) answers the genvm's resolve query with that version, so the callee @@ -10,7 +10,7 @@ import backend.node.genvm.origin.calldata as gvm_calldata from backend.node.genvm.base import Host from backend.node.genvm.origin import base_host, host_fns -from backend.node.genvm.origin.public_abi import StorageType +from backend.node.genvm.origin.public_abi import StorageView from backend.node.types import Address @@ -43,7 +43,7 @@ async def test_resolve_names_the_pinned_line_itself(): # Not a major: every line released so far is semver major 0, so a major # would resolve to the newest line whichever one the pin meant. host = _host({ADDR_PINNED.as_hex.lower(): "v0.2.17"}) - res = await host.resolve_callcontract_executor(ADDR_PINNED, StorageType.DEFAULT, 6) + res = await host.resolve_call_contract_executor(ADDR_PINNED, StorageView.DEFAULT, 6) assert res is not None assert gvm_calldata.decode(res) == {"kind": "version", "version": "v0.2.17"} @@ -51,8 +51,8 @@ async def test_resolve_names_the_pinned_line_itself(): @pytest.mark.asyncio async def test_resolve_returns_none_for_unpinned_target(): host = _host({ADDR_PINNED.as_hex.lower(): "v0.2.17"}) - res = await host.resolve_callcontract_executor( - ADDR_UNPINNED, StorageType.DEFAULT, 6 + res = await host.resolve_call_contract_executor( + ADDR_UNPINNED, StorageView.DEFAULT, 6 ) assert res is None @@ -66,14 +66,14 @@ async def test_resolve_reports_an_unusable_stored_pin_as_a_host_error(): # transaction's time budget is gone. host = _host({ADDR_PINNED.as_hex.lower(): "banana"}) with pytest.raises(base_host.HostException) as exc: - await host.resolve_callcontract_executor(ADDR_PINNED, StorageType.DEFAULT, 6) + await host.resolve_call_contract_executor(ADDR_PINNED, StorageView.DEFAULT, 6) assert exc.value.error_code != host_fns.Errors.OK @pytest.mark.asyncio async def test_resolve_treats_empty_pin_as_unpinned(): host = _host({ADDR_PINNED.as_hex.lower(): ""}) - res = await host.resolve_callcontract_executor(ADDR_PINNED, StorageType.DEFAULT, 6) + res = await host.resolve_call_contract_executor(ADDR_PINNED, StorageView.DEFAULT, 6) assert res is None @@ -82,7 +82,7 @@ async def test_resolve_accepts_a_regex_selector(): # Same grammar the migration backfill writes for pre-existing v0.2 # contracts: a `re:`-prefixed pattern, not an exact version. host = _host({ADDR_PINNED.as_hex.lower(): r"re:^v0\.2\."}) - res = await host.resolve_callcontract_executor(ADDR_PINNED, StorageType.DEFAULT, 6) + res = await host.resolve_call_contract_executor(ADDR_PINNED, StorageView.DEFAULT, 6) assert res is not None assert gvm_calldata.decode(res) == {"kind": "version", "version": r"re:^v0\.2\."} @@ -91,5 +91,5 @@ async def test_resolve_accepts_a_regex_selector(): async def test_resolve_rejects_a_stored_pin_with_an_invalid_regex(): host = _host({ADDR_PINNED.as_hex.lower(): "re:("}) with pytest.raises(base_host.HostException) as exc: - await host.resolve_callcontract_executor(ADDR_PINNED, StorageType.DEFAULT, 6) + await host.resolve_call_contract_executor(ADDR_PINNED, StorageView.DEFAULT, 6) assert exc.value.error_code != host_fns.Errors.OK diff --git a/tests/unit/test_run_request_major.py b/tests/unit/test_run_request_major.py index 8384a301a..22efa8c2d 100644 --- a/tests/unit/test_run_request_major.py +++ b/tests/unit/test_run_request_major.py @@ -151,7 +151,7 @@ async def __aexit__(self, *_args): async def test_run_genvm_host_opts_into_cross_contract_resolution(monkeypatch): """Opting out is a silent downgrade, not a missing feature. - Without this flag the manager answers `resolve_callcontract_executor` + Without this flag the manager answers `resolve_call_contract_executor` itself with "stay in-process", so a pinned callee's code runs on the caller's executor and the pin quietly stops meaning anything. """ diff --git a/tests/unit/test_set_vote.py b/tests/unit/test_set_vote.py index 0ec954d6a..f26bf5875 100644 --- a/tests/unit/test_set_vote.py +++ b/tests/unit/test_set_vote.py @@ -4,7 +4,7 @@ from backend.node.base import Node from backend.node.types import Receipt, ExecutionMode, ExecutionResultStatus, Vote -from backend.node.genvm.origin.public_abi import ResultCode +from backend.node.genvm.origin.host_fns import ResultCode from backend.node.genvm.error_codes import GenVMErrorCode from backend.domain.types import Validator, LLMProvider diff --git a/tests/unit/test_studio_fees.py b/tests/unit/test_studio_fees.py index b4ef964d4..837621f19 100644 --- a/tests/unit/test_studio_fees.py +++ b/tests/unit/test_studio_fees.py @@ -1647,7 +1647,7 @@ def test_genvm_message_fee_allocation_maps_studio_nodes(): "recipient": "0x2222222222222222222222222222222222222222", "call_key": bytes.fromhex("12" * 32), "budget": 60, - "on": "accepted", + "on": "decided", "fee_params": { "Internal": { "leader_timeunits_allocation": 6, @@ -1711,7 +1711,7 @@ def test_genvm_message_fee_allocation_nests_descendants_under_roots(): "recipient": descendant["recipient"], "call_key": bytes.fromhex("34" * 32), "budget": 60, - "on": "accepted", + "on": "decided", "fee_params": { "Internal": { "leader_timeunits_allocation": 7, @@ -1779,7 +1779,7 @@ def test_genvm_message_fee_allocation_keeps_legacy_gasless_messages_unmetered(): assert [node["on"] for node in allocations] == [ "finalized", "finalized", - "accepted", + "decided", ] assert all(node["recipient"] is None for node in allocations) assert all(node["call_key"] is None for node in allocations) diff --git a/tests/unit/test_usage_metrics_service.py b/tests/unit/test_usage_metrics_service.py index f670b12d5..2af180b07 100644 --- a/tests/unit/test_usage_metrics_service.py +++ b/tests/unit/test_usage_metrics_service.py @@ -4,7 +4,7 @@ import pytest from backend.node.genvm import base as genvm_base -from backend.node.genvm.origin import public_abi +from backend.node.genvm.origin import host_fns from backend.services.usage_metrics_service import UsageMetricsService @@ -165,9 +165,9 @@ def test_provide_result_preserves_llm_token_metrics(): host._nondet_disagreement = None res = SimpleNamespace( - result_kind=public_abi.ResultCode.RETURN, + result_kind=host_fns.ResultCode.RETURN, result_data={"ok": True}, - result_storage_changes=[], + result_storage_deltas=[], result_emissions=[], result_nondet_results=[], stdout="", diff --git a/third_party/genvm/version b/third_party/genvm/version index 2918b1e43..07f057afb 100644 --- a/third_party/genvm/version +++ b/third_party/genvm/version @@ -1 +1 @@ -feat/rework-manager-api:7b7f2c4947ced7f968a16e02c80a4e321344d4f0 +fix/vm-fatal-errors:8a4816ebea2d2c1f210c6167778e94330fb3d46a