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
7 changes: 7 additions & 0 deletions docs/contributing/howto/building/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ shows up as ninja's `missing and no known rule to make it`
| `all/data` | runner data via nix |
| `all/runners` | the runners themselves via nix, x86_64 Linux only |
| `codegen` | generated sources ([genvm-tool.md](../genvm-tool.md)) |
| `cargo/clippy` | clippy over every registered crate, warnings fatal |
| `cargo/clippy/fix` | the same lints, applying every machine-applicable fix |
| `cargo/fmt` | `cargo fmt` over every crate |

Outputs land in `build/out`: `bin/genvm-modules`, `bin/genvm-manager`,
`bin/genvm-post-install`, and `executor/<version>/bin/genvm` per built line,
where the version comes from `executors/<line>.x/manifest.json` and is recorded
in `build/info.json`

The full CI matrix has a `clippy` cell running `cargo/clippy`; when it fails it
reruns `cargo/clippy/fix` and prints the resulting patch in the job log and
summary, so run the target locally before pushing

Release packages: [release-build.md](../releasing/release-build.md). Runners:
[runners.md](runners.md)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ executor installs no signal handlers and has no graceful-shutdown path, so it
**can be killed at any moment**, between any two operations, without notice.

Implications
-----------
------------

- The executor keeps no durable state of its own. All persistent state lives in
the host and is written only as part of delivering a result. A killed executor
Expand All @@ -41,9 +41,10 @@ Output **capture** is a derived property with three states:
- ``disabled`` — nothing is captured into the result: the executor's
stdout/stderr go to ``/dev/null`` and its logs are *forwarded to the manager's
own log* (so they are not lost, just not returned in the response).
- ``bounded`` — captured into the result, but bounded: at most the 128 most
recent log entries are kept (oldest dropped), and stdout/stderr are truncated
to a 4 MiB tail each.
- ``bounded`` — captured into the result, but bounded: at most 128 log entries
are kept, evicted by audience first and by age second (see
:doc:`../appendix/log-record`), and stdout/stderr are truncated to a 4 MiB
tail each.
- ``unbounded`` — captured into the result in full.

When capture is ``disabled`` the result's log/stdout/stderr fields are empty
Expand Down
7 changes: 0 additions & 7 deletions docs/website/src/impl-spec/03-greyboxing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,3 @@ of the LLM module config and MUST contain the documented ``#{...}`` placeholders

Missing placeholders are a config error and surface as :ref:`gvm-def-internal-error`
at module startup.

Generated Reference
-------------------

The auto-generated signature reference for the Lua tables exposed to scripts
(``lib.rs.*``, ``llm.rs.*``, ``web.rs.*``, the ``Prompt`` shape, etc.) lives in
:doc:`01-lua-api`.
1 change: 1 addition & 0 deletions docs/website/src/impl-spec/appendix/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Appendix
host-loop
manager-api
manager-socket
log-record
93 changes: 93 additions & 0 deletions docs/website/src/impl-spec/appendix/log-record.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Log Record Format
=================

Every log line the executor and the manager emit is one JSON object. The same
shape is used on stderr, in the manager's own log and in the ``genvm_log``
artifact served over the manager socket (see :doc:`manager-socket`).

Fields
------

.. list-table::
:header-rows: 1
:widths: 16 14 70

* - Key
- Type
- Meaning
* - ``level``
- string
- One of ``trace``, ``debug``, ``info``, ``warn``, ``error``. Always the first key
* - ``audience``
- string
- Who the record is meant for, see below. Always the second key
* - ``target``
- string
- Rust module path of the call site
* - ``message``
- string
- Human-readable text
* - ``ts``
- string
- Wall-clock timestamp
* - *anything else*
- any
- Structured captures of the call site. Long strings and byte buffers are truncated
to a per-record byte limit, currently 128 bytes

``audience`` is a reserved key: a capture named that way fails to compile and a
hand-built record carrying one has it dropped.

Audience
--------

The audience is a tag only, it never takes part in filtering. It answers *who
should act on this line*:

.. list-table::
:header-rows: 1
:widths: 18 82

* - Value
- Reader
* - ``user``
- The contract developer or caller: the record is a consequence of the contract's
own input, code or budget
* - ``operator``
- The node runner: configuration, environment, providers, broken internal
invariants, panics
* - ``introspector``
- Someone debugging GenVM itself: lifecycle tracing, state dumps, timings. The
default when a call site names none

Rules that follow from the tag:

1. A ``user`` record may only carry scalar captures (display, error, debug,
id); bulk dumps such as calldata, byte buffers and serialised values are
rejected at compile time, and the byte limit is clamped to 128 regardless of
configuration
2. Panics are ``operator``
3. Records from a v0.2.x executor, which knows nothing about audiences, are
tagged ``introspector`` by the manager
4. A line the executor emitted that is not valid JSON is wrapped by the manager
into ``{"level": "error", "audience": "operator", "message": "genvm log",
"line": <base64>}``

Lua scripts log through ``lib.log { level = ..., audience = ..., message = ... }``;
an absent or unknown ``audience`` means ``introspector``.

Manager Sink
------------

Under ``bounded`` capture (see :doc:`../01-core-architecture/04-executor`) the
manager keeps at most 128 records per execution and evicts by audience rather
than by age:

1. While the cap has never been hit, everything is queued
2. On the first overflow every ``introspector`` record is dropped, a marker
``{"level": "warn", "audience": "operator", "message": "introspector logs dropped"}``
is appended, and from then on ``introspector`` records are discarded on arrival
3. On the next overflow the sink degrades to a plain queue that drops the oldest
``user`` or ``operator`` record; ``introspector`` records stay discarded

The marker counts toward the cap. ``unbounded`` capture keeps every record.
7 changes: 7 additions & 0 deletions docs/website/src/spec/02-execution-environment/02-wasip1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ Path Resolution
``path_*`` functions take a directory :term:`FD` and a path. Resolution is
purely lexical and identical in both modes:

#. The supplied path must be at most
:ref:`gvm-def-consts-value-top-limits-vfs-path-len` octets long and contain
at most :ref:`gvm-def-consts-value-top-limits-vfs-path-components` ``/``
separators, otherwise the call fails with ``Inval``. Both bound the argument
as written, before any normalization, so a separator that yields no component
still counts. Neither bound applies to the directory descriptor's own path,
which is bounded by how the :ref:`gvm-def-vfs` was populated.
#. The directory descriptor's own path and the supplied path are concatenated
and split on ``/``. Empty and ``.`` components are dropped, a ``..``
component pops the preceding one, and a ``..`` that would escape the root is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,12 @@ Semantics:

Metering additionally enforces node-configured bounds, surfaced as ``VMError``\ s:

- :ref:`gvm-def-str-trie-value-vm-error-fee-phase-timeout-out-of-bounds` —
unless both time-unit allocations are
zero, the leader allocation is outside ``node.minProposeTimeout`` through
``node.maxProposeTimeout``, or the validator allocation is outside
- :ref:`gvm-def-str-trie-value-vm-error-fee-below-minimum` — either a non-zero
``execution_budget_per_round`` below ``node.messageBudgetFloor`` (the chain's
``BudgetTooLow``), or — unless both time-unit allocations are zero — a leader
allocation outside ``node.minProposeTimeout`` through
``node.maxProposeTimeout`` or a validator allocation outside
``node.minCommitTimeout`` through ``node.maxCommitTimeout``.
- :ref:`gvm-def-str-trie-value-vm-error-fee-below-minimum` — a non-zero
``execution_budget_per_round`` below
``node.messageBudgetFloor`` (the chain's ``BudgetTooLow``).
- :ref:`gvm-def-str-trie-value-vm-error-fee-too-many-rounds` — ``rotations``
implies more consensus rounds than the
node's validator table supports (on-chain ``MAX_ROUNDS``).
Expand Down Expand Up @@ -370,15 +368,19 @@ Semantics
that returns the same runner id. Otherwise
:ref:`gvm-def-consts-value-memory-limiter-consts-runner-load-cost` plus ``code`` length is charged against
the caller's RAM budget before the archive is parsed; on success, the runner
enters the caller's loaded set.
also incurs its :ref:`metadata charge <gvm-def-runner-load-charge>` and enters
the caller's loaded set

The outcomes, in check order, are:
The outcomes are:

#. Missing :ref:`gvm-def-det-mode`: the call fails with ``Forbidden``. Nothing
is charged and no state changes.
#. Insufficient memory for the charge: the :term:`sub-VM` exits with an
out-of-memory :ref:`gvm-def-vm-error`. Nothing is charged and the runner is
not registered.
#. Insufficient memory for the base cost and ``code`` length: the :term:`sub-VM`
exits with :ref:`gvm-def-str-trie-value-vm-error-out-of-memory`. Nothing is
charged and the runner is not registered
#. Insufficient memory for metadata while loading: the :term:`sub-VM` exits
with :ref:`gvm-def-str-trie-value-vm-error-out-of-memory` and the runner is
not registered
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#. Malformed archive: the call fails with a deterministic invalid-contract
:ref:`gvm-def-vm-error`. The charge is retained until the :term:`sub-VM`
finishes, and the runner is not in the loaded set.
Expand Down
23 changes: 19 additions & 4 deletions docs/website/src/spec/02-execution-environment/04-runners.rst
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,30 @@ Runner actions are executed left-recursively, until :ref:`gvm-def-start-wasm` is
If it was not reached, it will result in a :ref:`gvm-def-vm-error` with
``invalid_contract runner malformed`` code.

.. _gvm-def-runner-load-charge:

Loading a :term:`runner` goes through a single **load action**, defined per
:term:`sub-VM`. Each :term:`sub-VM` owns a **loaded-runner set**: the runner
ids it has already loaded. The load action for an id is:

- if the id is already in the :term:`sub-VM`'s loaded set, nothing is charged;
- otherwise :ref:`gvm-def-consts-value-memory-limiter-consts-runner-load-cost` plus the runner's size in octets is charged as
- otherwise, when the loaded set already holds
:ref:`gvm-def-consts-value-top-limits-max-runners` ids, the load fails with
:ref:`gvm-def-str-trie-value-vm-error-out-of-memory` and nothing is charged —
a count cap is refused exactly like an exhausted RAM budget;
- otherwise :ref:`gvm-def-consts-value-memory-limiter-consts-runner-load-cost`
plus the runner's raw size and metadata cost in octets is charged as
Comment thread
coderabbitai[bot] marked this conversation as resolved.
:ref:`gvm-def-ram-consumption` against the :term:`sub-VM`'s RAM budget, and
the id is then added to the loaded set.

The raw size is the length of the runner's code or archive bytes. For a ZIP
runner, the metadata cost is the sum of
:ref:`gvm-def-consts-value-memory-limiter-consts-zip-file-cost` plus the UTF-8
filename length in octets for each distinct non-directory entry. Repeated
filenames count once, using the last entry. Non-ZIP runners have no additional
metadata charge. Insufficient RAM for either charge exits the :term:`sub-VM`
with :ref:`gvm-def-str-trie-value-vm-error-out-of-memory`

Whether the executor has the archive cached internally is not observable: the
charge depends only on the :term:`sub-VM`'s own load history, never on cache
state. The same runner loaded by different :term:`sub-VM` instances is charged
Expand All @@ -388,9 +403,9 @@ A load action occurs when:
- receiving a custom-runner grant at :term:`sub-VM` creation
(see :ref:`gvm-meta-property-custom-runners`).

For a ``chain:`` runner the size is the length of the code blob read from
storage. A ``chain:`` load costs the same as any other load of that size —
there is no doubled charge and no separate fee component.
For a ``chain:`` runner the raw size is the length of the code blob read from
storage. The same content has the same load charge regardless of its source;
there is no doubled charge and no separate fee component

.. _gvm-def-custom-runner-visibility:

Expand Down
6 changes: 3 additions & 3 deletions docs/website/src/spec/03-vm/01-startup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ A new :term:`sub-VM` is created for:
- :ref:`gvm-def-gl-call-run-nondet`

Creation is rejected with
:ref:`gvm-def-str-trie-value-vm-error-out-of-vm-recursion` if the new
:ref:`gvm-def-str-trie-value-vm-error-out-of-subvm-recursion` if the new
:ref:`gvm_vm_field_depth` is greater than or equal to
:ref:`gvm-def-consts-value-top-limits-vm-recursion`.

Expand Down Expand Up @@ -157,10 +157,10 @@ The child is read-only.
- :ref:`gvm-perm-use-balance-for-message-fees` is false

- :ref:`gvm_vm_field_state_mode` is the requested storage view (*param*
``state``); a request of :ref:`gvm-def-enum-value-storage-type-default`
``state``); a request of :ref:`gvm-def-enum-value-storage-view-default`
keeps the parent's value (the plain copy rule), so by default the callee
observes a view at least as recent as its caller's. Because the child
cannot write, its :ref:`gvm-def-enum-value-storage-type-default` view is
cannot write, its :ref:`gvm-def-enum-value-storage-view-default` view is
the decided state: it never includes the calling transaction's uncommitted
writes (see :ref:`contract-execution-flow`).
- :ref:`gvm_vm_field_topmost_runner_id` is the callee's contract runner.
Expand Down
21 changes: 10 additions & 11 deletions docs/website/src/spec/03-vm/03-ram-limiting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,30 @@ The following operations consume RAM:
- **File mapping**: :ref:`gvm-def-consts-value-memory-limiter-consts-file-mapping` octets base cost plus the length of the filename in bytes
- **File descriptor allocation**: :ref:`gvm-def-consts-value-memory-limiter-consts-fd-allocation` octets per descriptor
- **Runner loading**: the first load of a :term:`runner` in a :term:`sub-VM`
costs :ref:`gvm-def-consts-value-memory-limiter-consts-runner-load-cost` plus the runner's size in octets. A runner already
consumes its :ref:`load charge <gvm-def-runner-load-charge>`, including ZIP
metadata. A runner already
in that :term:`sub-VM`'s loaded set costs nothing, and the charge is released
when the :term:`sub-VM` finishes, like any other charge. Loading covers
spawning the entry-point runner, ``Depends``/``With`` actions, the ``MapFile``
and ``RegisterRunner`` ``gl_call``\ s, and receiving a custom-runner grant at
sub-VM creation (see :doc:`../02-execution-environment/04-runners` and
:ref:`gvm-meta-property-custom-runners`)
:ref:`gvm-meta-property-custom-runners`). A :term:`sub-VM` holds at most
:ref:`gvm-def-consts-value-top-limits-max-runners` runners: a load past that
fails the same way an exhausted budget does, and charges nothing
- **Storage writes**: writing to a 32-octet aligned region of a
:term:`Storage Slot` costs
:ref:`gvm-def-consts-value-memory-limiter-consts-new-storage-page` octets the
first time that region is written. Regions the :term:`sub-VM` inherited
already written from its caller, and repeated writes to a region, cost nothing
- **Emissions**: each emitted message or event costs
:ref:`gvm-def-consts-value-memory-limiter-consts-execution-emission-base-size`
octets,
plus its retained calldata, code, allocation subtree, topics, event data, and
:ref:`gvm-def-consts-value-memory-limiter-consts-calldata-arg-element-size`
octets per
retained positional argument,
:ref:`gvm-def-consts-value-memory-limiter-consts-calldata-kwarg-entry-size`
octets per
retained keyword argument, and
octets, plus the encoded length of each payload it retains — calldata, code,
allocation subtree, topics and event data — and
:ref:`gvm-def-consts-value-memory-limiter-consts-message-fee-rotation-element-size`
octets
per retained message-fee rotation
per retained message-fee rotation. Calldata is charged by its
:ref:`encoded <gvm-def-calldata-encoding>` length, so positional and keyword
arguments carry no charge of their own
- **Nondeterministic outputs**: each output costs
:ref:`gvm-def-consts-value-memory-limiter-consts-nondet-output-base-size`
octets plus its encoded length on every role
Expand Down
29 changes: 25 additions & 4 deletions docs/website/src/spec/appendix/internal-constants.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Value: ``96``
runner_load_cost
~~~~~~~~~~~~~~~~

Value: ``4096``
Value: ``1048576``

.. _gvm-def-consts-value-memory-limiter-consts-vm-spawn-cost:

Expand Down Expand Up @@ -62,21 +62,28 @@ Value: ``128``
execution_emission_base_size
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Value: ``256``
Value: ``1024``

.. _gvm-def-consts-value-memory-limiter-consts-message-fee-rotation-element-size:

message_fee_rotation_element_size
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Value: ``32``
Value: ``128``

.. _gvm-def-consts-value-memory-limiter-consts-nondet-output-base-size:

nondet_output_base_size
~~~~~~~~~~~~~~~~~~~~~~~

Value: ``32``
Value: ``128``

.. _gvm-def-consts-value-memory-limiter-consts-zip-file-cost:

zip_file_cost
~~~~~~~~~~~~~

Value: ``128``

.. _gvm-def-consts-top-limits:

Expand Down Expand Up @@ -134,6 +141,13 @@ max_fds

Value: ``1024``

.. _gvm-def-consts-value-top-limits-max-runners:

max_runners
~~~~~~~~~~~

Value: ``128``

.. _gvm-def-consts-value-top-limits-wasm-call-depth:

wasm_call_depth
Expand All @@ -155,6 +169,13 @@ vfs_path_components

Value: ``128``

.. _gvm-def-consts-value-top-limits-vfs-path-len:

vfs_path_len
~~~~~~~~~~~~

Value: ``16384``

.. _gvm-def-consts-runner-limits:

runner_limits
Expand Down
Loading
Loading