Status: accepted implementation contract for the 0.30 branch.
This document fixes the production meaning of
input BoundedCellSet<Resource, N> consumed by consume_each. It closes the
bounded dynamic GroupInput part of issue #7. It does not introduce a generic
transaction iterator or an allocation-backed Cell collection.
The accepted shape is:
resource Token has store, consume {
amount: u64
}
action verify(input inputs: BoundedCellSet<Token, 16>) -> u64 {
verification
let mut total: u64 = 0
consume_each token in inputs {
require token.amount > 0
total += token.amount
}
require total <= 1600
return 0
}
The contract requires all of the following:
- the parameter source is explicitly
input; 1 <= N <= 1024;- the element is a
resourcewith an exact encoded width from 1 through 512 bytes; - selection uses the current Type Script's
GroupInputsource; - every selected input is decoded exactly once and the body runs exactly once;
- the body contains only pure
requirepredicates and+=updates to mutable numeric accumulators declared outside the body; - every selected linear resource is discharged by the operation.
Any other source, dynamic or recursive element layout, larger bound, or arbitrary loop side effect remains unsupported. Production compilation rejects those shapes with E2105. A permissive artifact retains runtime error 24 rather than returning success.
CKB defines CKB_SOURCE_GROUP_INPUT as the virtual input array containing
Cells that use the same complete Script as the currently executing Script. A
Type Script group is formed from inputs and outputs that share that complete
Type Script. CellScript therefore does not perform a transaction-wide scan and
does not infer membership from data, Lock hash, or a partial code-hash match.
The runtime starts at group-relative index zero and increments by one. This is
the canonical filtered transaction-input order supplied by CKB. Only
CKB_INDEX_OUT_OF_BOUND ends the scan. Any other syscall status rejects the
transaction.
Normative upstream sources:
For a declared bound N, the generated verifier performs this algorithm:
- Call
LOAD_CELL_DATA(index, GroupInput)into a bounded stack buffer. - On success, require
index < N. A successful load at indexNproves the group has at leastN + 1members and rejects it. - Require the returned byte length to equal the typed resource width.
- Load the current Script hash.
- Load the selected GroupInput's Type hash and compare all four 64-bit words with the current Script hash.
- Load its Lock hash and require that the current Type Script hash is not being reused in the Lock role.
- Decode the fixed-width value, execute every body predicate and accumulator update, and increment the index exactly once.
- On
CKB_INDEX_OUT_OF_BOUND, produce the absent-element result and exit the loop.
Zero members are accepted when the Type Script is invoked through its output
group. This gives BoundedCellSet<T, N> the runtime cardinality 0..=N; it
does not silently change the minimum to one.
Group membership uses the complete current Type Script. Transaction inputs are identified by distinct OutPoints at the CKB transaction layer. The collection operation does not treat an arbitrary resource data field as a second global identity namespace.
Two selected Cells may therefore contain the same application field value.
The shared fixture records this as
duplicate_application_identity_is_collection_neutral. A protocol that needs
field uniqueness must declare and enforce that resource identity through its
creation/replacement policy or a separate checked invariant. The collection
contract must not invent a uniqueness rule from a field name.
| Code | Meaning in this contract |
|---|---|
| 1 | CKB syscall failed where no more specific mapped status applies |
| 3 | GroupInput Cell data load failed |
| 4 | fixed-width data or hash result length is not exact |
| 5 | a per-element or post-loop require predicate is false |
| 17 | selected input Type hash differs from the current Script hash |
| 21 | actual group cardinality exceeds N |
| 24 | requested bounded collection shape has no supported runtime contract |
| 47 | current Script is used in the wrong Script role |
All failures terminate the verifier process. No partial discharge or clean success path is emitted after an error.
The compiler records a bounded-cell-load operation whose declared type is
BoundedCellSet<T, N>. ProofPlan records the runtime cardinality, exact
GroupInput source, fixed-width decoding, per-element predicate execution, and
linear discharge. The source map and lowering record bind those typed blocks
to the emitted machine blocks.
The standalone artifact checker additionally decodes the RISC-V sequence. It requires:
LOAD_CELL_DATAwithGroupInput, the typed loop ordinal, the exact stack buffer/length slots, and the success/end/error split;- the strict
index < Nbranch over that same ordinal and stable error 21; - the exact typed byte-width comparison over the syscall's length slot and stable error 4;
- current Script hash loading plus GroupInput Type-hash field 5, including successful syscall status, exact 32-byte lengths, exact corresponding 0/8/16/24 word offsets, and mismatch failure;
- GroupInput Lock-hash field 3, the same loop ordinal, all four corresponding hash words folded into the Lock/Type distinction result, and stable role failure;
- exact loaded/absent destination and presence-bit stack slots;
- the stable terminal errors used by the scan.
Deterministic checker mutations change the count immediate, decode width, GroupInput ordinal, Type-hash word offset, identity length, Lock fold, success pointer, absent result slot, and the predicate's typed-to-machine binding. Every mutation is rejected with V2420 after all artifact and sidecar hashes are rebound.
bounded_group_input_v1.json
is the versioned shared corpus. The simulator adapter and the ckb-testtool
full transaction adapter execute the same case identities and expected exit
codes:
- zero, one,
N, andN + 1members; - malformed fixed-width data;
- false predicate at the first, middle, and last position;
- wrong-Type/GroupInput-excluded Cells;
- repeated application identity values.
The maximum supported bound has a separate executable fixture at
bounded_group_input_max.cell.
The test executes 1024 GroupInput Cells and enforces a 4 KiB stripped ELF limit
and a 3,000,000-cycle CKB-VM limit. These are release guards for this focused
contract, not general transaction size or fee promises.
The stateful CKB acceptance run on 2026-09-08 executed all ten cases against one
deployed 1,752-byte ELF. The N = 3 success case measured 8,996 cycles; the
one-element, excluded-foreign, and duplicate-application-identity successes
measured 4,348, 4,885, and 6,672 cycles. Over-bound, malformed, and false
predicate cases returned exactly 21, 4, and 5 and left every seeded input live.
The production-evidence validator recomputes the current fixture SHA-256,
requires exactly one bounded-group-input-v1:verify build/deployment row, and
binds the acceptance artifact path and CKB data hash to that row. Stale,
spliced, missing, or duplicate evidence fails closed.