Skip to main content
Status: Landed (regenesis-only)
Type: Standards Track
Category: Core
Created: 2026-08-26
Requires: CIP-3
Related: CIP-30, CIP-31, CIP-39

Status / landed state

This CIP now describes the access meter as it landed, not a provisional design.
  • Landed at node devnet 65a6518f24e2734c3289f0230f92f70d072ee6b0 (node PR #1457, a merge-commit integration bundle of the metering-audit line; devnet sync-3 CBQS-v2 folded in as #1533). Reflected here on 2026-09-07.
  • Amendment (2026-09-08, COW-3896, decision 2d). Access payer lanes were rebalanced at node devnet 22e325b90ab03cd1d4dc4e7e3b1cccb036211242 (node #1549): LANE_USER_ACCESS 2,294 → 2,051 and LANE_RUNNER_ACCESS 901 → 1,144 (with RUNNER_TX_ACCESS_LIMIT), and two runner-callback reservation constants were added — RUNNER_CALLBACK_HANDLER_ACCESS_ALLOWANCE = 152 and RUNNER_CALLBACK_ACCESSES_LIMIT = 314. Timer (901) and System (4,096) are unchanged and the three payer lanes still sum to 4,096. The rows and prose amended for this decision cite node devnet 22e325b9; the rest of the capacity table remains pinned to the 65a6518f landed state above. See “Two-results / two-users lane derivation and the runner-callback tier” below for the derivation and the aggregate-policy qualification.
  • ACCESS_WEIGHT_TABLE_VERSION = 4 (execution/src/access_weights.rs:14). The weight schedule is frozen protocol data, identified by the two fingerprints in §5.
  • Activation is regenesis-only. The meter is genesis-active from block 0 of the launch regenesis; there is no live-chain activation height and no observe-only ramp on the landed chain (§11).
  • Live scalar access_weight_scalar_ppm = 1_350_000 at genesis (COW3659_ACTIVATION_ACCESS_WEIGHT_SCALAR_PPM, types/src/constants.rs:273); the identity scalar 1_000_000 is the compatibility default (DEFAULT_ACCESS_WEIGHT_SCALAR_PPM, types/src/constants.rs:142).
The values below are committed protocol constants, not benchmark-gated placeholders. Where a constant is still marked benchmark-gated in the source (⟨benchmark-gated⟩), it is called out; the number is nevertheless the one that ships and is consensus-critical at version 4. The normative source of truth is the node code at 65a6518f and docs/access/CLIENT-LIMITS.md.

Changelog since the 2026-08-27 draft

Abstract

Cowboy prices execution in three consensus dimensions: compute in cycles, store I/O in accesses, and persisted bytes in cells. One access is one cold StateGet; every other store operation has a deterministic weight relative to that unit. memory_cells remains an unpriced liveness bound, while recurring storage rent remains a separate ledger. This CIP adds the access meter, its transaction and receipt fields, an independent EIP-1559 fee track, the enforced store gate, and the versioned weight schedule. On the landed chain the meter is not intrinsic-zero for native work: user-signed System instructions charge structural reservations before dispatch, the per-transaction envelope charges a fixed row, and each guest host-call crossing charges one unit. (Sources: CIP-3 §§2.1, 2.4; Technical Whitepaper §§2.3, 17.1; docs/access/CLIENT-LIMITS.md; execution/src/access_weights.rs; types/src/constants.rs.)

1. Why this is not the third meter §8.5 forbids

The Storage Whitepaper §8.5 states:
RAS MUST NOT introduce a third on‑chain meter. On‑chain operations related to storage MUST consume Cycles and Cells per Technical Whitepaper §4; storage rent MUST be maintained in the Storage Manager’s separate billing ledger.
That prohibition scopes the off-chain RAS/CBFS rent mechanism. Recurring rent for retained bytes MUST remain in the Storage Manager billing ledger described by CIP-31; it MUST NOT be folded into transaction gas. (Sources: Storage Whitepaper §8.5; CIP-31 §§1–5.) The accesses dimension is categorically different. It prices consensus compute-domain work: transient QMDB reads that can block proposal execution and writes that are durably flushed at apply_finalized. It is charged and settled inside the state-transition function in the same EIP-1559 family as cycles and cells; it is not a rent ledger and does not price bytes held over time. (Sources: Technical Whitepaper §§State Rent, 17.1, 17.5.) CIP-30 §3.3 already requires CIP-3 or a revision to fix a deterministic, bounded cost for trie-update work on top of per-byte Cell cost. This CIP supplies the missing store-I/O dimension and gate; it does not alter CIP-31’s off-chain billing ledger. (Source: CIP-30 §3.3.)

2. Motivation

  1. A transaction pays for compute (cycles) and stored bytes (cells), but the cost that dominates validator wall-time — cold state reads during block proposal and the writes flushed at finalization — was charged only as a flat cycle token, so a read-heavy actor could consume seconds of I/O for almost nothing and push the block past the proposal deadline.
  2. The access meter prices that store I/O in its own unit (one cold read = 1, other store operations weighted), with its own EIP-1559 basefee, per-transaction and per-lane limits, and a per-block aggregate ceiling, charged before each store operation through a single enforced gate that every host-visible read and write must pass.
  3. With compute, store I/O and persisted bytes each metered in the resource they consume, every transaction’s validator time is bounded by protocol constants calibrated on the slowest supported validator, instead of by a wall-clock reap that different machines trigger at different points.
(Sources: docs/access/README.md; docs/access/CLIENT-LIMITS.md; Technical Whitepaper §§4.1–4.3, 17.) The existing schedule also mis-denominated store work across two markets. Technical Whitepaper §17.3 assigned 100 cycles to a read and 200 cycles to a write, while §17.4 assigned reads 0.01 cells per byte as “bandwidth metering.” CIP-3 is normative over that architecture reference, so this CIP removes the flat store-I/O charges from cycles and moves store I/O into accesses. Read-side bytes are priced separately in cycles by measured bandwidth (STORAGE_READ_CYCLES_PER_KIB = 7, execution/src/gas.rs:413); they do not enter cells and do not enter the access set (§6). (Sources: CIP-3 §§2.1, 2.2.1; Technical Whitepaper §§17.3–17.4; docs/access/design/DESIGN-cow3633-read-bytes.md.) The resulting resource model is orthogonal: (Sources: Technical Whitepaper §§3.2, State Rent, 17.1, 17.5; Storage Whitepaper §8.5; execution/src/gas.rs:413.)

How host operations are priced

The meter MUST NOT charge by operation name. Each host call MUST be decomposed into the store operations it performs, and the transaction pays for the committed state it touches:
  • The landed primitives are R = 1 per cold point read (W_READ, types/src/constants.rs:150), W = 1 per cold staged write (W_WRITE, :151), Scan(n) = 5 + ceil(n/8) per bounded scan (W_SCAN_BASE = 5 :153, items-per-unit 8 :154), Copy(n) = ceil(n/8) per storage copy (access_weights.rs:22–23), Fanout(k) = ceil(k/4) per derived-key fan-out (access_weights.rs:27–28), NestedFrame = 75 per nested actor frame (W_NESTED_FRAME, constants.rs:156) with a one-time 40-unit first-nested setup per frame chain (W_FIRST_NESTED_SETUP, :157), and a top execution frame W_TOP_FRAME = 99 (:155). Cold/warm accounting is transaction-scoped: the first read of a key and the first write of a key charge, while repeats in the same direction are free.
  • Every guest host-call crossing charges W_HOST_CALL = 1 (constants.rs:152) through an invocation-scoped credit: a successful positive row already includes that unit, so it is not an additional surcharge; warm, no-op, and caught early-reject calls still retain exactly one crossing unit, and unused credit is never refunded across invocations (COW-3647; docs/access/CLIENT-LIMITS.md “COW-3647 host-crossing consequences”).
  • Every host call MUST have a weight-table row in §5.1 or §5.1.1; execution-envelope and deferred-work rows are in §5.4. CI SHOULD reject any host method without a row. Representative decompositions are unchanged from the draft except that a nested frame now costs 40 + 75 on first use and 75 thereafter, a scan uses the ceil(n/8) slope, and each crossing carries its 1-unit credit.
  • Scan, Copy, and Fanout MUST charge from a committed or input-visible bound before dispatch, never from what the operation happens to find. A charge MUST NOT depend on cache state, hardware, latency, or timeouts. The fixed MAX_LIBS_PER_ACTOR scan bound applies because no O(1) pin count provides a smaller preflight bound.
  • Only trusted runtime sidecars are zero-access: gas_left, charge_gas, and the instruction-gas flush/refresh (RuntimeSidecar → 0, execution/src/access_weights.rs:521). Every other guest crossing still charges its one-unit W_HOST_CALL (§2, A2) — including context and the cryptographic host calls (context/keccak256/ed25519_verify each call charge_host_call_base first, pvm_host.rs:5271,6510; GuestZero → W_HOST_CALL, access_weights.rs:522). What stays outside the access meter is the underlying work of those calls: bytecode and crypto compute (charged in cycles), persisted byte volume (cells), and transient RAM (the memory bound). Native STF reads are charged as specified in §3.
In short, a host call costs the number of distinct state keys it cold-reads and cold-writes, plus a fixed structural charge for each scan, copy, fan-out, nested frame, top frame, and guest crossing, all bounded before the call runs. (Sources: docs/access/CLIENT-LIMITS.md; execution/src/access_weights.rs; types/src/constants.rs.)

3. The access dimension

The unit is one cold StateGet = one access. Other operations consume the relative weights in §5. Cold/warm accounting MUST use one transaction-scoped access set with EIP-2929 semantics. The first logical in-access and the first logical out-access for a key are charged; repeats of the same direction are warm and free. Read-then-write of one key charges once in each direction. The access set MUST be shared by nested frames and MUST NOT roll back when a nested frame reverts. The transaction warm set and the committed-value cache MUST survive a nested revert. Only cache mutations caused by staged writes roll back, in reverse order, to their pre-write values. A committed read learned inside a reverted frame therefore remains cached and cannot cause a later uncharged physical read. An AccessKey is the tuple (committed store namespace/prefix, key bytes as stored). Within the in-access or out-access direction, two operations are warm-equivalent if and only if they resolve to the same physical committed key. Code(hash) and Library(hash) use different prefixes and are distinct keys; same-address account reads from sender, transfer-target, or balance-flush paths resolve to one key; system-record wrappers alias the underlying record key they read.

Native work is no longer intrinsic-zero

On the landed chain, native state-transition work is metered wherever a signer or the envelope owns it. The intrinsic access cost is no longer zero.
  • Per-transaction envelope (A3, COW-3648). Every signed transaction charges a fixed envelope row E = 5 structural accesses = 4R + 1W (SIGNED_TX_ENVELOPE_STRUCTURAL_ACCESSES = 5, types/src/constants.rs:282; BASE_ENVELOPE_ACCESSES_SIGNED_TX, execution/src/access_weights.rs:120–121). The four reads are Account[from], the lane-fee multipliers, the sender vesting-admission read, and the sender-tail vesting-floor backstop read; the write is the sender-account tail. This row is deliberately outside the transaction-scoped cold/warm set. Protocol-minted deferred work does not traverse the signed envelope, so its E = 0 (SAFETY_MARGIN_ACCESS = 0, constants.rs:2939).
  • User-signed System instructions (A1, COW-3631). Native System-instruction handlers charge in two phases (execution/src/execution/system_instruction.rs:108-117). A fixed, input-visible structural row is reserved before any admission gate, actor-pause/bank check, or handler dispatch. A handler that then decodes a committed live-set index reads that cardinality and reserves the exact cardinality-dependent row before the first per-item walk or mutation (gov_snapshot.rs:114-120; the write side reserves the full committed N even when fewer rows are ultimately selected). Each reservation, once actually made, is charged in full and never refunded — on success or rejection — so committed state cannot create a refund oracle while exhaustion stays state-preserving; a rejection before the second phase simply never incurs the phase-two reservation. Both phases stand outside the transaction-scoped cold/warm access set. (Sources: docs/access/CLIENT-LIMITS.md “D41 deferred-child failures”, “Production signers”; docs/access/design/DESIGN-cow3631-native-system-access.md.)
  • Host-call crossings (A2, COW-3647). Each physical non-sidecar guest crossing charges W_HOST_CALL = 1 (constants.rs:152), credited into the row it belongs to as described in §2. Instruction-gas flush/refresh are the only trusted zero-access runtime sidecars.
  • CBQS native rows (COW-3527 note / CBQS-v2). CBQS dispatch is priced in both cycles and access. Its eight decoded v2 rows reserve the complete structural row before the first admission check and never refund it (§5, §12).
Every reserved read charges one structural R and each reserved write one structural W, accumulated in the same structural meter as every other §5 primitive and converted to charged units once at the boundary (§5.0) — not scaled per reservation. Remaining native STF reads that no signer or envelope owns — pure admission, nonce, and basefee bookkeeping charged elsewhere — stay out of accesses; only pure in-memory work is free. Read-side value bytes are priced in cycles (§2, §6), not access. (Sources: CIP-39 §15.1; execution/src/gas.rs:413.) Reads MUST charge before the latency-bearing store operation on the proposal path. Writes MUST charge at stage time, before their later durable flush at apply_finalized. Read weights protect the proposal deadline; write weights protect fairness and finalization cadence.

4. The enforced gate

Every actor-reachable store operation MUST route through one before-dispatch charge point. A conforming client MUST make the raw state reader/writer inaccessible to host handlers, nested frames, system-route helpers, and post-effect code except through that gate. (Sources: CIP-3 §2.2.1.1 deterministic-ordering rule.) The reference sealed-capability design has these portable requirements:
  • A MeteredHostStore-equivalent capability owns the only raw store handle. Raw state construction and the typed access trait MUST be sealed outside the gate module.
  • Sync and async entry points MUST share one charging core, and the blocking adapter MUST exist only behind the gate.
  • Every scan, copy, or fanout MUST precharge from a committed or input-visible bound before dispatch. Scan(n) MUST charge from the validated bound and insert its returned keys, at most n, into the in-access warm set; a later point read of a returned key is warm, while a key outside the returned set MUST charge R. Copy(n) MUST insert the n copied child keys into the out-access warm set, so those keys are not charged W separately. Fanout(k) MUST insert the k derived per-item record keys into the in-access warm set; every per-item nested dispatch MUST still charge NestedFrame. Each aggregate MUST charge from its validated or derived bound irrespective of prior warmth of any individual key; implementations MUST NOT reduce the aggregate to n - 1 or k - 1. A key outside the exact precharged or returned set that is read anyway MUST pass through the gate and charge cold, so a bound mismatch cannot create free I/O.
  • CI SHOULD reject raw-store imports outside the gate and any HostApi method without a weight-table row. Equivalent static enforcement is conforming if it provides the same non-bypass property.

5. Weight table

ACCESS_WEIGHT_TABLE_VERSION = 4 (execution/src/access_weights.rs:14). The table version is a protocol constant. Changing relative weights requires a coordinated release under CIP-3 §4; governance may scale the whole table through access_weight_scalar_ppm within the bound in §8 but may not rewrite individual rows. The table’s identity is bound by two FNV-128 fingerprints computed over the closed row set (execution/src/access_weights.rs):
  • Executable-access fingerprint 145510929673014535888950164850774767257 (access_weights.rs:3339).
  • Composed tri-cost fingerprint 202721689210127662310138855754496845765 (access_weights.rs:3474).
Any row, opcode, or meter change moves a fingerprint and requires explicit attribution. §5 is the readable summary; the complete landed row tables are reproduced verbatim from node 65a6518f in Appendix A, with a generator recipe so drift can be re-checked mechanically.

5.0 Primitives (landed)

The access meter accumulates structural units. The gate charges each distinct cold R, distinct cold W, NestedFrame, and each aggregate Scan/Copy/Fanout preflight (its base and per-item units summed) in raw structural weight, with no scalar applied at the gate (execution/src/host_read_gate.rs:406). The access_weight_scalar_ppm scalar is applied once, at a meter boundary — never per host operation or per allowance (types/src/constants.rs:286; execution/src/execution/transaction.rs:641):
On entry, a signed transaction’s charged access_limit is floor-converted to a structural meter capacity (transaction.rs:653-659); at settlement the structural total is ceiling-converted once into the charged accesses_used written to the receipt (transaction.rs:1178-1183). The per-block aggregate ceiling, the System-selection sub-lane, and each deferred child are separately owned meters that convert at their own boundaries. All arithmetic is unsigned 128-bit; charged_accesses maps structural zero to charged zero and any non-zero structural total to at least one charged unit. Implementations MUST NOT apply the scalar per primitive charge: a per-charge ceiling (e.g. ceil(1·ppm) + ceil(1·ppm)) disagrees with the boundary conversion ceil(2·ppm) and is a consensus divergence, which the implementation asserts against by name (incorrectly_scaled_per_charge, execution/src/access_weights.rs:4870). access_weight_scalar_ppm = 1_000_000 is the identity (structural ≡ charged); the genesis live scalar is 1_350_000, so a structural total s settles to ceil(s · 1.35) charged.

5.1 PVM HostApi

The host-call rows are unchanged in shape from the draft — each host call decomposes into R/W/Scan/Copy/Fanout/NestedFrame primitives plus its W_HOST_CALL crossing credit — but every numeric weight now uses the §5.0 landed primitives. The complete per-method decomposition is the executable HOST_API_ACCESS_ROWS table (execution/src/access_weights.rs), reproduced verbatim in Appendix A.1 and bound by the executable-access fingerprint above. Representative rows: Timer host-call declarations carry a per-fire access authority. The persisted Timer record carries access_limit_per_fire: u64 adjacent to gas_limit_per_fire; this is a regenesis-only codec change — decoders MUST accept the current version exactly and MUST NOT infer a default for an older record. schedule_timer stores the classic default DEFAULT_TIMER_ACCESS_PER_FIRE = 160 (constants.rs:1841); schedule_timer_ex may store a larger opt-in request (§5.3). The launch fork_actor row prices the physical copy_actor_storage implementation: Copy(n) is one aggregate out-access charge, n being the parent’s committed kv_count read through the gate at preflight; CIP-30’s O(1) storage-root fork is not consensus-live, and its activation MUST amend the fork row to R(child collision) + W(root).

5.1.1 call_actor system subroutes

These rows replace the ordinary call_actor actor/code/pin route when intercepted (EventSub get_rank/get_orderbook/get_min_bid/update_bid/topup, SKM list_account_keys/initialize_paid_stream_config/register_content_keys/revoke_account_key). SKM register_content_keys is Rk(4 + 2N) + W(out-keys) with N <= CIP7_MAX_CONTENT_KEYS_PER_CALL = 256. The complete user-signed System row set (one entry per wire variant) is reproduced verbatim in Appendix A.2 (USER_SYSTEM_ACCESS_ROWS).

5.2 Scalar-safe allowances

Structural quantities — gate charges, the deferred-child and Timer tiers of §5.3, and the native reservation rows of §3/Appendix A — stay in structural units inside their meter; the charged_accesses / structural_access_capacity conversion of §5.0 is applied once per meter boundary (transaction settlement, the aggregate ceiling, each selection/child boundary), never per charge. Already-charged authorities — USER_TX_ACCESS_LIMIT, ORDINARY_SYSTEM_TX_ACCESS_LIMIT, DEFAULT_ACCESS_LIMIT, the lane budgets, and the aggregate ceiling — are stated in charged units and are floor-converted to a structural meter capacity on entry, not re-scaled. At the identity scalar structural and charged coincide; at the 1_350_000 genesis scalar a structural total s settles to ceil(s · 1.35) charged.

5.3 Natural-Timer and actor-minted deferred envelopes

The deferred-child tiers are the calibrated C0 constants: A minimal delivering deferred actor needs 10 + 99 + 1 = 110 structural accesses; a one-subscriber async event fire needs 4 + 40 + 75 + 1 = 120 (constants.rs:2915–2917). The 160-unit child execution allowance therefore delivers work but cannot itself admit a nested actor call (109 + 115 = 224 > 162; D36, constants.rs:1860–1875). At the genesis scalar, charged(215) = 291 and charged(375) = 507, so a 901-unit Timer phase admits exactly one actor-Intent opt-in fire per block. The actor-minted deferred System class (an outgoing message to the Job Dispatcher → JobSubmit; intent_deposit/intent_withdraw/intent_broadcast[_ex] → IntentDeposit/IntentWithdraw/IntentBroadcast) is classified from the child’s provenance/sender read (host_read_gate.rs:634-637,677); it then reserves its fixed native row from the child’s meter before the handler’s dependent store work, in full, never refunded; if the reservation does not fit, execution returns OutOfGas (code 1005) before any raw-store dispatch and commits no partial state. Conditional nested hook frames are charged live, only when they materialize. The hooked IntentDeposit/IntentWithdraw native structural models are 202/200 structural (constants.rs:1857–1858); the fixed reserved rows are Rk(8)+Wk(4) / Rk(7)+Wk(3) with the two frames live-only (ACTOR_SYSTEM_ACCESS_ROWS). JobSubmit’s metering is not a runner-registry scan: its actor-minted child row is Rk(1) sender ingress (ACTOR_SYSTEM_ACCESS_ROWS, access_weights.rs:877-891), while its selection cost is a fixed+variable 37 + 5N + 6M row (constants.rs:955-958) reserved from the System-selection sub-lane — a second consensus meter disjoint from the transaction/child access meter, drained even if the transaction later fails. N is the bounded frontier-plus-tail candidate pool min(eligible, MAX_SELECTION_CANDIDATES = 69) and M the committee (dispatcher.rs:1388); the MAX_REGISTERED_RUNNERS = 10_000 registration cap bounds the set but is not the metering row.

5.4 Execution envelope and deferred work

The ExecuteActor, DeployActor, and deferred event-fire rows keep their draft shapes with the §5.0 landed weights (top frame 99, nested 40 + 75, scan 5 + ceil(n/8)). Every library-pin scan in call_actor, fork_actor, ExecuteActor, and DeployActor MUST charge the fixed worst-case Scan(MAX_LIBS_PER_ACTOR) bound because there is no O(1) pin-count primitive. The engine-minted deferred event-fire batch, unsubscribe_event/force_unsubscribe_event bookkeeping-topic re-fire, and Copy(n) fork rule keep their draft semantics with the landed slopes (COW-3527 items 4–7 row fixes).

5.5 Signed RAS / CBFS rows

Signed RAS is GasLane::System, so its per-transaction ceiling is the ordinary-System 1_500. The exhaustive RAS sub-variant schedule is in docs/access/CLIENT-LIMITS.md “CBFS relay: all RAS sub-variants” and Appendix A.5; two RAS rows bind that ceiling and the CommitManifest staging cap (types/src/constants.rs:846–867):
  • SettlePorChallenge third-miss initial row = 1_106 native / 1_111 signed structural (+2N recipient-distribution phase), where 1_106 = 52R + 20W + 2·Scan(4_096) and Scan(4_096) = 5 + ceil(4_096/8) = 517. This is the largest mandatory signed ordinary-System initial row (MAX_SIGNED_ORDINARY_SYSTEM_INITIAL_ROW_STRUCTURAL_ACCESSES = 1_111, constants.rs:866), and it is exactly what fixes MAX_ACCESS_WEIGHT_SCALAR_PPM (§8): at 1.35× it charges exactly 1_500. At launch cip31.cbfs.por_slashing_enabled = 0, so settlement is alarm-only; the +2N distribution phase is COW-3672 staged/cursor work. SubmitDrainAudit (1,067+2N native) and ReportUnreachableRelay (557+2N) are the sibling redistribution rows.
  • CommitManifestFinalize funding envelope (COW-3855, #1532): the finalize predicate 60 + 3H + 2D + 2A + 2R + 2·ceil(A/512) + 2·ceil(R/512) <= 1_111 (structural, evaluated at the max governed scalar) sets the hard public staged-chunk cap MAX_COMMIT_STAGE_CHUNKS = 307: with the maximum D = 64 relay deltas and no shards, 307 chunks require 1_109 structural accesses (fit) and 308 require 1_112 (fail). CommitManifestStage moved to 12R/5W; finalize fails closed with the existing QuotaExceeded receipt. The former 948/1_024 caps were derived against the wrong 4_096 envelope and are superseded.

6. De-conflation

The Storage KV Read and Storage KV Write rows leave CIP-3 §2.2.1’s cycle schedule. The legacy 100-cycle read and 200-cycle write terms are removed from cycles. Read-side bytes are priced in cycles by measured bandwidth — read_cycles(bytes) = 7 * ceil(bytes / 1024) (STORAGE_READ_CYCLES_PER_KIB = 7, execution/src/gas.rs:413,417) — tracked as storage_read_bytes in GasCategory::Storage. state_get charges the returned value bytes (pvm_host.rs:5024-5034); state_scan_prefix charges the sum of key and value bytes across the returned rows (pvm_host.rs:5143-5155). A read persists nothing, so it consumes no cells, and the byte charge does not enter the cold/warm access set (COW-3633). Persisted write bytes continue to consume cells. (Sources: CIP-3 §2.2.1–2.2.2; Technical Whitepaper §§17.3–17.4; execution/src/gas.rs.) After the change, cycles measures pure compute plus read-side byte bandwidth, accesses measures store I/O structure, and cells measures persistent bytes. memory_cells MUST remain a hard per-execution liveness bound and MUST NOT enter fee settlement.

7. Transaction fields and wire encoding

Transaction version 2 adds four fields in these exact canonical wire positions: The leading CURRENT_TX_VERSION is 2, and the four fields participate in Write, Read, EncodeSize, signing preimages, signatures, and strict decode in the frozen order above. CURRENT_TX_VERSION and DEFAULT_ACCESS_LIMIT are defined in the pinned cowboy-protocol-codec dependency (rev 0d5e8b69, types/Cargo.toml:55) and re-exported at types/src/lib.rs:82,89; origin_remaining_access is carried on the receipt/response types (rpc/src/responses.rs:156). (Sources: Technical Whitepaper §§2.1, 2.5.) Deferred transactions MUST carry access parity: origin_remaining_access is optional exactly like origin_remaining_cycles/origin_remaining_cells, and a child MUST satisfy access_limit <= origin_remaining_access. Like its siblings, a deferred transaction MUST carry max_fee_per_access == 0 and max_priority_fee_per_access == 0. The existing Transaction.access_list (EIP-2930 scheduling/prefetch) and dashboard/CBFS accessMode (a storage read-cap) are unrelated and are not reused by this meter.

8. Basefee, fees, access lanes, and governance

access_basefee is denominated in attoCBY and uses the same independent EIP-1559 update family as the other meters:
U_a = successful_bucket + min(failed_bucket, T_a) over the ordinary transactions in the block transaction list (storage/src/speculative.rs:208-219). The successful (uncapped) bucket holds both Success and OutOfGas transactions; only an ExecutionError transaction goes to the failed (capped) bucket — an OutOfGas-heavy block is therefore demand-uncapped. Each bucket adds the transaction’s own accesses_used and the System-selection access it caused, credited to the same bucket as its parent status (speculative.rs:213-215,4408-4413). Natural-Timer transactions execute in a fresh end-of-block phase and are excluded from U_a (and U_c); the dedicated timer basefee remains cycle-priced, and timer accesses pay the live access price without contributing to a later block’s U_a. The payerless per-block allowances (block-start and epoch sweep) add charged usage to System-lane capacity reporting but are excluded from payer-congestion demand U_a (D19). The inherited constants are alpha = 96, a ±1/96 per-block clamp, and MIN_BASEFEE = 10_000. The landed capacity and genesis values: Per-class ceilings are block validity (D40 / D37 / Decision 21). Signed User transactions are capped at USER_TX_ACCESS_LIMIT = 1_024 charged accesses (two maximal User transactions fit the 2_051 User lane with 3 charged left; at the genesis scalar 1_024 charged buys 758 structural). DEFAULT_ACCESS_LIMIT remains 512 (the SDK/CLI/RPC stamp; docs/access/CLIENT-LIMITS.md:6,111), so an estimate-then-sign client may fill up to 1_024 while a static stamp above 512 is deliberate. Ordinary native-System transactions are capped at ORDINARY_SYSTEM_TX_ACCESS_LIMIT = 1_500 — which is also the entire ordinary-System block reserve — and the independent selection side-channel owns the other 2_596 of the 4_096 System lane. JobSubmit/JobResultSubmit/JobResultCommit/JobCancel use the 1_144-unit Runner lane; zero-origin deferred work uses the 901-unit Timer lane. No client-signed instruction is assessed against PROTOCOL_TX_ACCESS_LIMIT = 4_096; that is the outer envelope and full System-lane total. Aggregate ceiling (C1, COW-3632). Block validation MUST enforce BLOCK_AGGREGATE_ACCESS_CEILING = 4_096 charged accesses across the whole block, derived as LEADER_ACCESS_CAPACITY_UNITS(5_826) − EPOCH_SWEEP_UNITS_PER_BLOCK(1_000) − OUTSIDE_SHARED_POOL_ACCESS_UNITS(730) (constants.rs:647). It preserves either full System saturation or full User+Runner+Timer co-saturation while rejecting their unsafe 8_192 co-saturation. Two-results / two-users lane derivation and the runner-callback tier (COW-3896, decision 2d). The User and Runner payer-lane budgets are each sized to hold two reference transactions, and the three payer lanes still sum to BLOCK_AGGREGATE_ACCESS_CEILING (const-asserted in constants.rs: 2_051 + 1_144 + 901 = 4_096). The User lane holds two maximal 1_024-charged signed transactions with a three-unit tail (1_024 is USER_TX_ACCESS_LIMIT; 2 × 1_024 = 2_048 ≤ 2_051; LANE_USER_ACCESS, :408). The Runner lane is sized to hold two single-runner, no-delegation JobResultSubmit reference suggestions (2 × 572 = 1_144; LANE_RUNNER_ACCESS, :585); the 572 is that COW-3875 estimate fixture’s suggestion, not a per-transaction Runner cap — RUNNER_TX_ACCESS_LIMIT = LANE_RUNNER_ACCESS = 1_144 (:920), so one Runner transaction may claim the whole lane, and multi-runner, delegation, and offender work exceeds 572. A JobResultSubmit that produces a runner callback reserves the structural callback tier RUNNER_CALLBACK_ACCESSES_LIMIT = RUNNER_CALLBACK_HANDLER_ACCESS_ALLOWANCE (152) + REQUIRED_DEFERRED_CHILD_ACCESSES (162) = 314 (:1753): the 152-unit resume own-work allowance is the measured warm-resume ceiling ceil(173 / 1.35) = 129 structural plus 23 policy headroom (~17.8%) (:1744), and the 162-unit child reserve is D36’s exact one-child threshold — the callback tier funds exactly one nested submit_job child per callback, never a second. At the live 1.35× scalar the 314-unit tier charges 424, and with the result’s own 71-structural work (charged 96) the estimate-then-sign suggestion is ceil((96 + 424) × 1.1) = 572 live and 424 at the identity scalar. Lane fit is not aggregate-block admission (qualification). These budgets size each lane in isolation; they do not promise that two JobResultSubmit suggestions and two maximal User stamps are all selected into one block. Under the current proposer reservations — a charged block-start seed (BLOCK_START_ACCESS_BUDGET = 1_000, 1_350 at the live scalar) reserved ahead of payer rows, plus the complete Timer-lane hold — the payer remainder does not fit that full set; the exact selected/deferred counts at both scalars are pinned in node truth-table tests. A planned follow-up (COW-3898) is expected to reduce the Timer reservation to the parent-state due-timer bound, changing that aggregate picture but not these lane budgets. Lane admission and the tail guard. For each transaction in canonical order, block validation runs a class-specific pipeline (storage/src/speculative.rs), all of whose rejections are block validity, not proposer preference:
  1. Class ceiling (speculative.rs:3662-3699; branch on is_engine_deferred = non_system_deferred_origin(tx).is_some()). An engine-minted (nonzero-origin) deferred envelope is funded by authenticated origin authority, not by the child instruction’s lane: its access_limit is converted once — charged_accesses(access_limit, ppm).unwrap_or(u64::MAX) — and compared against PROTOCOL_TX_ACCESS_LIMIT = 4_096, regardless of lane. Every other transaction compares its raw access_limit against tx_access_limit_for_lane(lane) — signed requests are charged (User 1_024, Runner 1_144, Timer 901, ordinary System 1_500); a zero-origin Timer request retains its raw structural Timer ceiling. Either excess yields the structured 1900 (E1900) no-execution result and continues the block loop. The split is by origin, not by signing: a transaction carrying an origin_tx_hash is deferred and unsigned — is_deferred() = origin_tx_hash.is_some(), and verify() accepts it only with signature == EthSignature::ZERO (cowboy-protocol-codec transaction.rs:201-202,451-464 @ rev 0d5e8b69; both signed constructors set origin_tx_hash: None) — so a validly signed envelope never carries an origin. Thus a nonzero origin is always engine-minted deferred (the 4_096 protocol branch), while a zero-origin System/Timer deferred envelope (native/Timer-minted) takes the tx_access_limit_for_lane structural-class arm.
  2. Live cap. execution_access_cap is the charged conversion of access_limit for deferred transactions and the raw access_limit for signed ones. live_cap = min(lane_remaining, sub_lane_remaining_or_u64::MAX, aggregate_remaining.saturating_sub(held_finalization_charged)) (speculative.rs:784-797): the per-block aggregate ceiling and the reserved finalization tail constrain execution, not only the lane.
  3. Ordered tail guard. A signed transaction with 0 < live_cap < charged(E) (charged E = 5 → 7 at the genesis scalar) makes the block invalid (speculative.rs:3753-3760). Every deferred transaction must fit its complete effective reservation min(execution_access_cap, full_lane_budget, 1_500 if System else u64::MAX) — if it exceeds live_cap the block is invalid, including when live_cap == 0 (:3768-3781); it is not admitted merely because a few charged units remain, and is not partially run against a short remainder. Only after both checks pass does a live_cap == 0 take the E1900 no-execution path (:3783+).
  4. Execution cap. Otherwise the transaction runs capped at min(execution_access_cap, live_cap) (:4190), the later authorization checks are preserved, and the accumulator increases by actual accesses_used.
A transaction that fails after committed-store reads is charged to its lane for the reads it made; only a transaction that consumed nothing anywhere is a pre-execution failure outside lane accounting. Nothing is credited back, and unused capacity MUST NOT move between lanes. The access contribution to the transaction fee:
Unused access limit MUST be refunded at max_fee_per_access, and 100% of the access-basefee portion MUST be burned. Governance. UpdateBasefeeConfig and SubmitProposal MUST each carry block_access_target and access_weight_scalar_ppm, recorded under system:basefee_config as Tier-0 parameters. Protocol validation MUST bound the governed scalar to [MIN_ACCESS_WEIGHT_SCALAR_PPM = 1_000_000, MAX_ACCESS_WEIGHT_SCALAR_PPM = 1_350_135]. The maximum is derived from the mandatory 1_111-unit ordinary-System initial row inside its 1_500 reserve — floor(1_500 * 1_000_000 / 1_111) = 1_350_135 — at which the row charges exactly 1_500; at 1_350_136 it charges 1_501. The 1_350_000 live scalar leaves 135 ppm of integer-rounding headroom. Governance MAY change the target and scalar within this bound but MUST NOT change an individual table row without the coordinated CIP-3 §4 release. The operational commitment (COW-3774) is that no post-genesis retune is performed until the deferred-child scalar-straddle fix lands. (Sources: constants.rs:2989, :2973, :273; docs/access/CLIENT-LIMITS.md.) Fee-policy hardening (D21 / D24). The scalar and tri-basefee are loaded from BasefeeConfig/system:basefee_config at startup, submission, estimation, finalization, and proposal selection. The sole governance persistence path validates before writing; node-local consumers use try_load_from_storage and refuse an invalid committed config without panicking — engine startup returns that refusal to main, which logs and exits nonzero. RPC surfaces that depend on committed fee state (/basefee, /governance/params, /estimate_gas, submission, node-authored runner transactions) fail closed with HTTP 500 when the committed tri-basefee or BasefeeConfig is malformed; clients MUST treat this as fee policy unavailable / do not sign and MUST NOT fabricate a default fee policy. (Sources: docs/access/design/DESIGN-cow3648-signed-envelope.md; docs/access/CLIENT-LIMITS.md “Fee-policy failure contract”.) The governance minimum-basefee ceiling MAX_GOVERNED_MIN_BASEFEE = 1_346_204_307_369 per Cycle/Cell/charged-Access unit is derived so the complete four-transaction Tier-0 recovery path stays payable by the largest schedule-free genesis account: floor((GENESIS_MAX_LIQUID_ACCOUNT_WEI − MIN_STAKE_CBY_WEI) / GOVERNANCE_RECOVERY_TOTAL_AUTHORITY), with total recovery authority 99_086 (constants.rs:3144,3180). A split-Treasury genesis weakens the account-level guarantee, so clients MUST NOT infer that every valid genesis funds one account to the ceiling.

9. Exhaustion and receipts

ExecutionStatus::OutOfGas carries accesses_used. Access exhaustion uses that existing status and does not introduce a new structured-error code, because structured-error bytes participate in receipt_root.
  • A transaction whose max_fee_per_access is below the block’s access_basefee fails with the existing BASEFEE_TOO_LOW structured error code 1008 (execution/src/structured_error_map.rs:181). Its context appends max_fee_per_access and access_basefee after cell_basefee, in that order.
  • A deferred child that cannot reserve its complete structural tier fails with DeferredChildOutOfAccesses code 1005 (E1005, structured_error_map.rs:161), context used, limit, child_kind, child_access_tier_structural, rolling back the requesting transaction atomically (D41).
Transaction receipts add accesses_used, remaining_access, and cumulative_accesses_used adjacent to their cycle/cell siblings in canonical Write, Read, EncodeSize, and RLP encodings, and receipt-root computation commits the resulting bytes (storage/src/types.rs:1085,1093,1106 with Write :1223,1234,1246, Read :1268,1290,1352, EncodeSize :1423,1432,1445; RPC DTOs rpc/src/responses.rs:199,211,214). The D37 receipt-leaf semantic property digest binds tx_hash and remaining_access so a divergent remaining_access cannot pass unnoticed. Nested exhaustion (B7, COW-3557). An omitted nested-call access allowance follows the EIP-150 caller-reserve shape: reserve = ceil(remaining / 64), and the nested call receives remaining − reserve (the all-but-1/64 rule), so a caller always retains 1/64 of its remaining access authority (nested_call_access_allowance, execution/src/pvm_host.rs:1823). Exhaustion of a smaller nested cap is an isolated subscriber/callee failure and cannot suppress later work while the live lane still has headroom; provenance is carried on the receipt. Estimate-then-sign (D22 / D24). POST /estimate_gas runs the same child-affordability formulas, ordering, and partial-prefix policy as execution and returns access_limit_suggested (rpc/src/handlers/chain.rs:992) so a client can estimate → read access_limit_suggested → sign. Static daemons that cannot round-trip an estimate MUST sign explicit, bounds-validated limits; a fee-policy-unavailable response (§8) means do not sign.

10. Determinism and safety

Every charge MUST derive only from committed or input-visible quantities and MUST NOT depend on observed latency, hardware cache warmth, validator model, wall-clock time, or timeouts. Charging MUST occur before dispatch; exhaustion MUST fail closed into the committed OutOfGas outcome, while a wall-clock reap remains a non-committing infrastructure backstop. In-process and zygote execution MUST produce byte-identical committed accesses_used, receipt, and state-root surfaces for the same transaction (§12). Access accounting MUST use the same transaction snapshot boundary as state and messages. The settlement inequality holds by denomination (§5.0): structural meter usage never exceeds the structural meter capacity, and the charged accesses_used written to the receipt never exceeds the corresponding charged effective authority. The direct wire-field inequality accesses_used <= access_limit applies to signed transactions, whose access_limit is charged. An engine-minted deferred transaction instead carries a structural access_limit (constants.rs:920-922; transaction.rs:2664-2674), so a fully consumed 162-structural child reports 219 charged accesses at the genesis scalar — greater than its wire 162, yet within its structural capacity. The unused limit is refunded per §8.

11. Activation

The meter is genesis-active from block 0 of the launch regenesis; there is no live-chain activation height, and ACCESS_WEIGHT_TABLE_VERSION = 4 documents the frozen schedule. The whole line is coordinated-regenesis deployment; a hot-merge is out of policy (node #1457, #1533). The D50 dormant continuation and deferred gates are flipped to height 0 (regenesis-active) at this landing (types/src/constants.rs):
  • DEFERRED_RECEIPT_AUTH_REMOVAL_ACTIVATION_HEIGHT = 0 (:4299)
  • JOB_CALLBACK_ACTOR_BINDING_ACTIVATION_HEIGHT = 0 (:4667)
  • RAS_GC_IS_TERMINAL_ACTIVATION_HEIGHT = 0 (:2706)
  • SDK_CONTINUATION_PERMISSION_ACTIVATION_HEIGHT = 0 (:4481)
  • SDK_CONTINUATION_DECODE_REJECTION_ACTIVATION_HEIGHT = 0 (:4595)
  • SDK_CONTINUATION_RESUME_PERMISSION_ACTIVATION_HEIGHT = 0 (:4642)
The cross-engine PVM zygote gate remains dormant (PVM_ZYGOTE_ACTIVATION_HEIGHT = u64::MAX, :3881): the in-process engine is the live consensus engine, while the zygote path is the cross-engine conformance oracle (§12). Other dormant activation constants are unchanged by this line.

12. Conformance vectors: gas-vectors.v2

cip-39-gas-vectors-v2.json (execution/tests/fixtures/cip39/cip-39-gas-vectors-v2.json) has schema cowboy.cbqs.gas-vectors.v2. It removes state_read_cycles/state_write_cycles, adds one R per charged read and one W per charged write at the identity scalar, and records accesses = charged_reads + charged_writes. The normative SHA-256 of the v2 artifact is:
(docs/access/integration/MERGE-LOG-integ.md:188.) The CBQS-v2 family (wire tag 218 v2) is an executing, state-writing, fee-charging surface priced in both cycles and access. Its eight decoded rows carry structural (engine-minted, native) [9, 5, 19, 3, 13, 12, 9, 11] and, as a complete signed transaction (native + E=5 envelope) at the 1.35× genesis scalar, charged [19, 14, 33, 11, 25, 23, 19, 22] (RegisterProvider 6R/3W, UpdateProvider 4R/1W, CreateStream 11R/8W, UpdateStream 2R/1W, CloseStream 7R/6W, TopUpAccount 7R/5W, SettleAccount 5R/4W, WithdrawAccount 6R/5W; execution/src/access_weights.rs:1936, tabulated in Appendix A.4); the top-level CBQS classifier stays (0,0), and the reservation is charged in full before the first admission check and never refunded. Devnet sync-3 also removed six CIP-16 variants (SubmitRegistrySettlementConfigProposal, RegisterTldLabel, RenewTldLabel, TransferTldLabel, SetTldActor, UpdateRegistrySettlementConfig), shrinking the wire corpus 204 → 198. Cross-engine conformance runs the in-process engine against the zygote engine and MUST produce identical accesses_used, receipt, and state root, including alias-key, nested-revert, and non-identity-scalar cases. The consensus golden corpus is 55 re-blessed rows (21 added, 34 modified; docs/access/integration/MERGE-LOG-integ.md:190); devnet sync-3 moved zero .golden files and only re-derived the two access_weights.rs fingerprint constants.

13. Security considerations

The access table and its version are consensus-critical. Any individual weight change requires a coordinated release under CIP-3 §4, and all clients MUST implement the exact same table, identified by the two fingerprints in §5. Only block_access_target and access_weight_scalar_ppm are live-governable, within [1_000_000, 1_350_135] ppm for the scalar. Missing a store path permits unmetered I/O denial of service; charging after dispatch makes the halt depend on wall-clock work already consumed; non-canonical key aliasing makes cold/warm results diverge; an unbounded native reservation or refundable native charge creates a content-dependent refund oracle. Implementations MUST therefore enforce a non-bypassable gate, charge before dispatch, reserve native and deferred rows in full without refund, and conformance-test alias keys, nested-tree sharing, speed invariance, cross-engine parity, and write-flush cadence. (Sources: CIP-3 §2.2.1.1; Technical Whitepaper §§Determinism, 12.1, 13.1.)

Implementation map

The full implementation and integration record lives at docs/access/ on node (README.md, the normative CLIENT-LIMITS.md, the 33 design/DESIGN-*.md records, and integration/MERGE-LOG-integ.md). Landing bundle: node #1457 (47 constituent PRs; history tag access-line-history); devnet sync-3: #1533. (Source: docs/access/integration/MERGE-LOG-integ.md.)

Appendix A: Verbatim landed access-row tables (node 65a6518f)

This appendix reproduces the executable row schedule verbatim from the landed node so the readable §5 summary can be re-checked mechanically. The two fingerprints in §5 (access 145510929673014535888950164850774767257; tri-cost 202721689210127662310138855754496845765) identify the exact table version this appendix mirrors; any row change moves a fingerprint. Generator recipe (reproduces every block below; diff against this appendix to detect drift):

A.1 PVM HostApi rows

HOST_API_ACCESS_ROWS (execution/src/access_weights.rs:641-850):
HOST_ENVELOPE_ACCESS_ROWS (execution/src/access_weights.rs:865-868):

A.2 User-signed System instruction rows

USER_SYSTEM_ACCESS_ROWS (execution/src/access_weights.rs:1933-2333), one row per wire variant with its Rk/Wk/Scan/NestedFrame shape. This table contains the CBQS-v2 row (Cbqs, :1935) and the RAS sub-variant pointer (Ras, :2317); the CBQS-v2 numeric rows are in A.4 and the exhaustive RAS sub-variant rows are in A.5.

A.3 Actor-minted deferred System rows

ACTOR_SYSTEM_ACCESS_ROWS (execution/src/access_weights.rs:877-891), reserved in full before the first raw-store dispatch and never refunded:

A.4 CBQS-v2 decoded rows

Structural (engine-minted, native) and, as a complete signed transaction (native + E=5 envelope) at the 1.35× genesis scalar, charged (execution/src/access_weights.rs:1936; docs/access/integration/MERGE-LOG-integ.md:188):

A.5 Signed RAS / CBFS sub-variant rows

The Ras entry in A.2 dispatches to the exhaustive RAS sub-table; the complete per-sub-op schedule is the normative docs/access/CLIENT-LIMITS.md “CBFS relay: all RAS sub-variants” table. The rows that bind the ordinary-System 1_500 ceiling and the staging cap: