@rootzero/contracts 1.20.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/Codec.sol +1 -1
- package/Commands.sol +1 -0
- package/Endpoints.sol +1 -0
- package/Events.sol +2 -2
- package/README.md +25 -15
- package/annotations/Schema.sol +35 -2
- package/codec/Blocks.sol +64 -2
- package/codec/Decoders.sol +71 -9
- package/codec/Descriptors.sol +28 -28
- package/codec/Readers.sol +39 -0
- package/codec/Schema.sol +45 -36
- package/codec/Specs.sol +9 -27
- package/codec/Writers.sol +8 -0
- package/commands/Allocate.sol +1 -1
- package/commands/Base.sol +3 -8
- package/commands/Burn.sol +1 -1
- package/commands/Credit.sol +1 -1
- package/commands/Debit.sol +1 -1
- package/commands/Deposit.sol +3 -10
- package/commands/Payout.sol +1 -1
- package/commands/Provision.sol +3 -3
- package/commands/Recover.sol +3 -3
- package/commands/Relay.sol +5 -4
- package/commands/Repay.sol +3 -3
- package/commands/Settle.sol +3 -3
- package/commands/Withdraw.sol +1 -1
- package/commands/admin/AllowAssets.sol +2 -2
- package/commands/admin/Allowance.sol +2 -2
- package/commands/admin/Annotate.sol +2 -2
- package/commands/admin/Appoint.sol +2 -2
- package/commands/admin/Authorize.sol +2 -2
- package/commands/admin/Base.sol +1 -1
- package/commands/admin/DenyAssets.sol +2 -8
- package/commands/admin/Dismiss.sol +2 -2
- package/commands/admin/Execute.sol +2 -2
- package/commands/admin/Unauthorize.sol +2 -2
- package/core/Host.sol +7 -10
- package/core/Portal.sol +11 -8
- package/events/Dispatch.sol +1 -1
- package/events/Relay.sol +1 -1
- package/events/Resolved.sol +17 -0
- package/events/Route.sol +1 -1
- package/events/Unresolved.sol +18 -0
- package/execution/Execution.sol +92 -5
- package/package.json +2 -3
- package/ports/AllowAssets.sol +1 -1
- package/ports/Allowance.sol +1 -1
- package/ports/Base.sol +3 -3
- package/ports/Credit.sol +1 -1
- package/ports/Debit.sol +1 -1
- package/ports/DenyAssets.sol +1 -1
- package/ports/Dispatch.sol +4 -2
- package/ports/Pipe.sol +2 -1
- package/ports/Post.sol +1 -1
- package/ports/Redeem.sol +1 -1
- package/docs/Schema.md +0 -403
- package/events/Recovered.sol +0 -17
- package/events/Undelivered.sol +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,97 @@
|
|
|
3
3
|
Until the protocol reaches integration-stable status, minor versions may include
|
|
4
4
|
breaking API changes. Breaking changes are called out explicitly.
|
|
5
5
|
|
|
6
|
+
## 1.22.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Declared child blocks are now structurally present whenever their parent is
|
|
11
|
+
non-empty. A child without a value uses its zero-payload block form instead
|
|
12
|
+
of being omitted. The `maybe` modifier now hints that onchain code accepts
|
|
13
|
+
that empty form; it no longer describes an absent item. Lists likewise keep
|
|
14
|
+
their header and represent no items with an empty payload.
|
|
15
|
+
- Removed `Schemas.Unit`. Use the empty form of the block key that carries the
|
|
16
|
+
relevant semantic meaning instead of a generic unit marker.
|
|
17
|
+
- Renamed `Decoders.take(cur, key)` to `takeBlock(cur, key)`. The `take` name is
|
|
18
|
+
now used by raw cursor navigation and returns the absolute start of a
|
|
19
|
+
bounds-checked byte range while advancing the cursor.
|
|
20
|
+
- Normalized standard schema bodies by removing their presentation-only outer
|
|
21
|
+
braces, and renamed the standard node field from `id` to `node`. Published
|
|
22
|
+
schema annotation bytes therefore change even though their wire layouts do
|
|
23
|
+
not.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Added empty-block inspection, conditional consumption, encoding, writing,
|
|
28
|
+
and execution-output helpers across `Blocks`, `Decoders`, `Readers`,
|
|
29
|
+
`Writers`, and `Executions`.
|
|
30
|
+
- Added `absolute`, `advance`, and raw `take` cursor helpers, plus `enter`
|
|
31
|
+
overloads that advance over a validated fixed payload prefix in one cursor
|
|
32
|
+
update.
|
|
33
|
+
- Added unnamed `schema` overloads for context-local specifications and an
|
|
34
|
+
unnamed `Blocks.schema` factory overload.
|
|
35
|
+
- Added the offchain-only `at N` schema projection hint. Explicit positions are
|
|
36
|
+
reserved first, then unannotated siblings fill the remaining positions in
|
|
37
|
+
declaration order; wire encoding and onchain decoding remain unchanged.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Clarified that one optional pair of outer braces is presentation-only for all
|
|
42
|
+
non-empty schema bodies, including custom top-level `many` schemas.
|
|
43
|
+
- Updated custom input examples to group fixed-width fields for direct calldata
|
|
44
|
+
reads, demonstrate empty child blocks, and separate top-level and nested swap
|
|
45
|
+
decoding.
|
|
46
|
+
- Excluded repository documentation and examples from the prepared npm package;
|
|
47
|
+
the package continues to contain Solidity sources, the README, changelog, and
|
|
48
|
+
license.
|
|
49
|
+
|
|
50
|
+
### Upgrade Compatibility
|
|
51
|
+
|
|
52
|
+
- Emit every declared child header in schema order. When a `maybe` child has no
|
|
53
|
+
value, emit that child's key with a zero payload length and call
|
|
54
|
+
`tryConsumeEmpty` before its strict semantic unpacker.
|
|
55
|
+
- Replace `Schemas.Unit` markers with an empty block carrying the key expected
|
|
56
|
+
by the receiving schema.
|
|
57
|
+
- Replace `cur.take(key)` with `cur.takeBlock(key)`. Use `cur.take(amount)` only
|
|
58
|
+
for raw fixed-width ranges.
|
|
59
|
+
- Indexers should accept braced and unbraced schema bodies, treat `maybe` as an
|
|
60
|
+
empty-value hint, and apply `at N` only after decoding declaration-order wire
|
|
61
|
+
data.
|
|
62
|
+
|
|
63
|
+
## 1.21.0
|
|
64
|
+
|
|
65
|
+
### Breaking Changes
|
|
66
|
+
|
|
67
|
+
- Repacked endpoint descriptors as `[state key:4][stride:1]`,
|
|
68
|
+
`[input key:4][stride:1]`,
|
|
69
|
+
`[output key:4][min:4][max:4][hint:3][stride:1]`, four reserved bytes,
|
|
70
|
+
one transaction-count byte, and one flags byte. The input child-key field was
|
|
71
|
+
removed; indexers and request builders must decode the new layout.
|
|
72
|
+
- Removed container metadata and the `many` and `keys` helpers from `Specs`.
|
|
73
|
+
Top-level lists are now emitted custom schemas whose context-local key is the
|
|
74
|
+
endpoint input key; nested lists continue to use the generic `#list` key.
|
|
75
|
+
- Replaced the `funded` and `admin` boolean parameters on command helpers and
|
|
76
|
+
the `funded` boolean parameter on port helpers with a packed `uint8 flags`
|
|
77
|
+
argument. Endpoint flags now live in the dedicated `Flags` library.
|
|
78
|
+
- Renamed the portal lifecycle events from `Undelivered` and `Recovered` to
|
|
79
|
+
`Unresolved` and `Resolved`, and renamed `Portal.retry` to `Portal.resolve`.
|
|
80
|
+
`Portal` now composes the node-access, runtime, and lifecycle-event
|
|
81
|
+
capabilities; concrete implementations remain responsible for explicitly
|
|
82
|
+
emitting lifecycle events.
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- Added custom-spec overloads for `Decoders.list(cur, spec)` and
|
|
87
|
+
`Executions.list(exec, spec, lane)` so custom-keyed top-level lists can use
|
|
88
|
+
the same cursor model as generic nested lists.
|
|
89
|
+
- Added `Specs.lane` for canonical descriptor lane packing and exported
|
|
90
|
+
`Flags` from the codec, command, and endpoint package entry points.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- Updated schema and indexing documentation and the list example for the
|
|
95
|
+
custom-keyed top-level list convention.
|
|
96
|
+
|
|
6
97
|
## 1.20.0
|
|
7
98
|
|
|
8
99
|
### Breaking Changes
|
package/Codec.sol
CHANGED
|
@@ -7,7 +7,7 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
import { AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
|
|
8
8
|
import { Keys } from "./codec/Keys.sol";
|
|
9
9
|
import { Sizes, Specs } from "./codec/Specs.sol";
|
|
10
|
-
import { Descriptors } from "./codec/Descriptors.sol";
|
|
10
|
+
import { Descriptors, Flags } from "./codec/Descriptors.sol";
|
|
11
11
|
import { Schemas } from "./codec/Schema.sol";
|
|
12
12
|
import { Decoders } from "./codec/Decoders.sol";
|
|
13
13
|
import { Cursors, Cur } from "./utils/Cursors.sol";
|
package/Commands.sol
CHANGED
|
@@ -5,6 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
// Import this file for both standard Execution-based commands and custom decoders.
|
|
6
6
|
|
|
7
7
|
import {CommandBase} from "./commands/Base.sol";
|
|
8
|
+
import {Flags} from "./codec/Descriptors.sol";
|
|
8
9
|
import {Execution, Executions} from "./execution/Execution.sol";
|
|
9
10
|
import {Lanes} from "./utils/Lanes.sol";
|
|
10
11
|
import {Blocks} from "./codec/Blocks.sol";
|
package/Endpoints.sol
CHANGED
|
@@ -5,6 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
// Import this file to inherit from the full rootzero callable host surface without managing individual paths.
|
|
6
6
|
|
|
7
7
|
// Shared endpoint hooks
|
|
8
|
+
import {Flags} from "./codec/Descriptors.sol";
|
|
8
9
|
import {CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook} from "./core/Settlement.sol";
|
|
9
10
|
|
|
10
11
|
// Commands
|
package/Events.sol
CHANGED
|
@@ -12,7 +12,7 @@ import { CommanderEvent } from "./events/Commander.sol";
|
|
|
12
12
|
import { DispatchEvent } from "./events/Dispatch.sol";
|
|
13
13
|
import { EndpointEvent } from "./events/Endpoint.sol";
|
|
14
14
|
import { ReceivedEvent } from "./events/Received.sol";
|
|
15
|
-
import {
|
|
15
|
+
import { ResolvedEvent } from "./events/Resolved.sol";
|
|
16
16
|
import { RelayEvent } from "./events/Relay.sol";
|
|
17
17
|
import { EventEmitter } from "./events/Emitter.sol";
|
|
18
18
|
import { GuardianEvent } from "./events/Guardian.sol";
|
|
@@ -22,7 +22,7 @@ import { NodeEvent } from "./events/Node.sol";
|
|
|
22
22
|
import { RootedEvent } from "./events/Rooted.sol";
|
|
23
23
|
import { RouteEvent } from "./events/Route.sol";
|
|
24
24
|
import { SpentEvent } from "./events/Spent.sol";
|
|
25
|
-
import {
|
|
25
|
+
import { UnresolvedEvent } from "./events/Unresolved.sol";
|
|
26
26
|
import { UnlockedEvent } from "./events/Unlocked.sol";
|
|
27
27
|
|
|
28
28
|
|
package/README.md
CHANGED
|
@@ -114,24 +114,29 @@ identifiers inside, never how the bytes are laid out.
|
|
|
114
114
|
|
|
115
115
|
Schemas can express more than flat fields: a block may contain any number of
|
|
116
116
|
nested child blocks (`#bytes as payload` names raw dynamic bytes), items can be
|
|
117
|
-
marked `maybe`
|
|
118
|
-
paths give off-chain tooling presentation
|
|
119
|
-
on the wire.
|
|
120
|
-
|
|
117
|
+
marked `maybe` when their empty form is accepted or `many` when they form a
|
|
118
|
+
list, and aliases and dotted field paths give off-chain tooling presentation
|
|
119
|
+
names without changing a single byte on the wire. Declared child headers are
|
|
120
|
+
always present; a zero payload length represents an empty block. An `at N` hint
|
|
121
|
+
can reposition one field in off-chain presentation without changing its wire
|
|
122
|
+
position. The full schema language is specified in
|
|
123
|
+
[`docs/Schema.md`](https://github.com/lastqubit/rootzero-evm/blob/main/docs/Schema.md). The standard block schemas live in
|
|
121
124
|
`Schemas` and their runtime keys in `Keys` (both via
|
|
122
125
|
`@rootzero/contracts/Codec.sol`).
|
|
123
126
|
|
|
127
|
+
A rare top-level list is published as a custom schema consisting of one item,
|
|
128
|
+
such as `many #asset` or `{ many #asset }`. Its context-local schema key becomes
|
|
129
|
+
the outer block key accepted by the endpoint; a list alongside sibling items
|
|
130
|
+
continues to use the generic `#list` key.
|
|
131
|
+
|
|
124
132
|
## Batches
|
|
125
133
|
|
|
126
134
|
A input is not a single struct; it is a run of blocks. One `#amount` block
|
|
127
135
|
asks for one deposit, five blocks ask for five, and the code path is identical
|
|
128
136
|
— every endpoint parses with a cursor and loops until the stream is exhausted.
|
|
129
137
|
The descriptor lane key is the prime item: it is the block type that may repeat
|
|
130
|
-
for batching.
|
|
131
|
-
|
|
132
|
-
as `many #asset` are encoded as
|
|
133
|
-
`[Keys.List][Keys.Asset]`, so indexers can see both the top-level LIST container
|
|
134
|
-
and the item type inside it.
|
|
138
|
+
for batching. Readers interpret a zero group byte as group size 1 when the lane
|
|
139
|
+
is non-empty.
|
|
135
140
|
|
|
136
141
|
Off-chain, building a batch is concatenation. Using the reference encoders from
|
|
137
142
|
[`test/helpers/blocks.ts`](test/helpers/blocks.ts):
|
|
@@ -308,7 +313,7 @@ abstract contract MyCommand is CommandBase {
|
|
|
308
313
|
uint private immutable descriptor;
|
|
309
314
|
|
|
310
315
|
constructor() {
|
|
311
|
-
(, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0,
|
|
316
|
+
(, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0, 0);
|
|
312
317
|
}
|
|
313
318
|
|
|
314
319
|
function myCommand(
|
|
@@ -327,6 +332,10 @@ abstract contract MyCommand is CommandBase {
|
|
|
327
332
|
}
|
|
328
333
|
```
|
|
329
334
|
|
|
335
|
+
The final argument is a packed flags byte. Pass `0` for an ordinary endpoint,
|
|
336
|
+
or compose values such as `Flags.Funded`, `Flags.Admin`, and
|
|
337
|
+
`Flags.AdminFunded` from the command or endpoint package entry point.
|
|
338
|
+
|
|
330
339
|
The standard commands cover the common ledger movements: `deposit` and
|
|
331
340
|
`depositPayable` (external funds in), `settlePayable` (funded settlement),
|
|
332
341
|
`withdraw` and `burn` (funds out),
|
|
@@ -466,12 +475,13 @@ Import from the package entry points rather than deep paths:
|
|
|
466
475
|
|
|
467
476
|
- `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
|
|
468
477
|
`Settlement`, `Pipeline`, `Portal`, validator
|
|
469
|
-
- `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`,
|
|
470
|
-
helpers, and shared value types for authoring custom commands
|
|
478
|
+
- `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, `Flags`,
|
|
479
|
+
codec helpers, and shared value types for authoring custom commands
|
|
471
480
|
- `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
|
|
472
|
-
mixins
|
|
481
|
+
mixins, their hooks, and `Flags`
|
|
473
482
|
- `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
|
|
474
|
-
`Reader`/`Readers`, `Writers`, `Schemas`, `Keys`, and
|
|
483
|
+
`Reader`/`Readers`, `Writers`, `Schemas`, `Descriptors`, `Flags`, `Keys`, and
|
|
484
|
+
`Specs`
|
|
475
485
|
- `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
|
|
476
486
|
layout and value helpers
|
|
477
487
|
- `@rootzero/contracts/Events.sol` — protocol event contracts
|
|
@@ -486,7 +496,7 @@ Repo layout:
|
|
|
486
496
|
- `contracts/blocks` — block schema, cursor parsing, writers
|
|
487
497
|
- `contracts/utils` — ids, nodes, assets, accounts, layout, ECDSA
|
|
488
498
|
- `contracts/events` — event contracts and emitters
|
|
489
|
-
- `docs` — [`Schema.md`](docs/Schema.md) (wire format and schema DSL)
|
|
499
|
+
- `docs` — [`Schema.md`](https://github.com/lastqubit/rootzero-evm/blob/main/docs/Schema.md) (wire format and schema DSL)
|
|
490
500
|
|
|
491
501
|
Use this library to create a new rootzero host, implement a command, or reuse
|
|
492
502
|
the protocol's block format in tooling. It is the shared protocol foundation,
|
package/annotations/Schema.sol
CHANGED
|
@@ -11,6 +11,23 @@ import {Runtime} from "../core/Runtime.sol";
|
|
|
11
11
|
/// @dev Schema annotations accumulate for distinct block keys. For a trusted
|
|
12
12
|
/// emitter, the latest schema for the same block key replaces the earlier claim.
|
|
13
13
|
abstract contract Schema is Runtime, AnnotationEvent {
|
|
14
|
+
/// @notice Construct and publish an unnamed context-local block specification.
|
|
15
|
+
/// @param key Context-local key value.
|
|
16
|
+
/// @param min Minimum accepted payload length.
|
|
17
|
+
/// @param max Maximum accepted payload length; zero means unbounded.
|
|
18
|
+
/// @param hint Initial per-block payload capacity.
|
|
19
|
+
/// @param body Schema DSL string describing the block payload body.
|
|
20
|
+
/// @return spec The context-local block specification.
|
|
21
|
+
function schema(
|
|
22
|
+
uint32 key,
|
|
23
|
+
uint32 min,
|
|
24
|
+
uint32 max,
|
|
25
|
+
uint32 hint,
|
|
26
|
+
string memory body
|
|
27
|
+
) internal returns (uint spec) {
|
|
28
|
+
return schema(key, min, max, hint, body, bytes32(0));
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
/// @notice Construct and publish a context-local block specification.
|
|
15
32
|
/// @param key Context-local key value.
|
|
16
33
|
/// @param min Minimum accepted payload length.
|
|
@@ -27,8 +44,16 @@ abstract contract Schema is Runtime, AnnotationEvent {
|
|
|
27
44
|
string memory body,
|
|
28
45
|
bytes32 name
|
|
29
46
|
) internal returns (uint spec) {
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
return schema(Specs.create(key, min, max, hint), body, name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// @notice Construct and publish an unnamed exact-size context-local block specification.
|
|
51
|
+
/// @param key Context-local key value.
|
|
52
|
+
/// @param size Exact payload length and initial per-block payload capacity.
|
|
53
|
+
/// @param body Schema DSL string describing the block payload body.
|
|
54
|
+
/// @return spec The context-local block specification.
|
|
55
|
+
function schema(uint32 key, uint32 size, string memory body) internal returns (uint spec) {
|
|
56
|
+
return schema(key, size, body, bytes32(0));
|
|
32
57
|
}
|
|
33
58
|
|
|
34
59
|
/// @notice Construct and publish an exact-size context-local block specification.
|
|
@@ -41,6 +66,14 @@ abstract contract Schema is Runtime, AnnotationEvent {
|
|
|
41
66
|
return schema(Specs.create(key, size), body, name);
|
|
42
67
|
}
|
|
43
68
|
|
|
69
|
+
/// @notice Publish an unnamed, already constructed block specification for the current host.
|
|
70
|
+
/// @param spec Packed block specification.
|
|
71
|
+
/// @param body Schema DSL string describing the block payload body.
|
|
72
|
+
/// @return The published block specification.
|
|
73
|
+
function schema(uint spec, string memory body) internal returns (uint) {
|
|
74
|
+
return schema(spec, body, bytes32(0));
|
|
75
|
+
}
|
|
76
|
+
|
|
44
77
|
/// @notice Publish an already constructed block specification for the current host.
|
|
45
78
|
/// @param spec Packed block specification.
|
|
46
79
|
/// @param body Schema DSL string describing the block payload body.
|
package/codec/Blocks.sol
CHANGED
|
@@ -73,6 +73,17 @@ library Blocks {
|
|
|
73
73
|
len = uint32(head >> 192);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/// @notice Decode a complete block header within an absolute calldata region.
|
|
77
|
+
/// @param abs Absolute position of the header.
|
|
78
|
+
/// @param end Absolute region boundary.
|
|
79
|
+
/// @return key Decoded block key.
|
|
80
|
+
/// @return len Decoded payload length.
|
|
81
|
+
function peek(uint abs, uint end) internal pure returns (bytes4 key, uint len) {
|
|
82
|
+
if (abs > end || Sizes.Header > end - abs) revert MalformedBlocks();
|
|
83
|
+
(key, len) = header(abs);
|
|
84
|
+
if (len > end - abs - Sizes.Header) revert MalformedBlocks();
|
|
85
|
+
}
|
|
86
|
+
|
|
76
87
|
/// @notice Validate a block header at an absolute calldata position.
|
|
77
88
|
/// @dev DANGER: This performs an unchecked calldata read and does not ensure `end`
|
|
78
89
|
/// lies within the caller's logical calldata region. Only the key, minimum,
|
|
@@ -101,6 +112,17 @@ library Blocks {
|
|
|
101
112
|
end = body + size;
|
|
102
113
|
}
|
|
103
114
|
|
|
115
|
+
/// @notice Validate an empty block at an absolute calldata position.
|
|
116
|
+
/// @dev DANGER: This performs an unchecked calldata read. The caller must
|
|
117
|
+
/// validate the returned end against its logical calldata region.
|
|
118
|
+
/// @param abs Absolute position of the block header.
|
|
119
|
+
/// @param key Expected block key.
|
|
120
|
+
/// @return end Absolute position immediately after the empty block header.
|
|
121
|
+
function expectEmpty(uint abs, bytes4 key) internal pure returns (uint end) {
|
|
122
|
+
if (header(abs, key) != 0) revert InvalidBlock();
|
|
123
|
+
return abs + Sizes.Header;
|
|
124
|
+
}
|
|
125
|
+
|
|
104
126
|
/// @notice Return whether `abs` identifies a header with `key` before an absolute end.
|
|
105
127
|
/// @param abs Absolute calldata position to inspect.
|
|
106
128
|
/// @param end Absolute region boundary.
|
|
@@ -111,6 +133,16 @@ library Blocks {
|
|
|
111
133
|
return bytes4(read32(abs)) == key;
|
|
112
134
|
}
|
|
113
135
|
|
|
136
|
+
/// @notice Return whether `abs` identifies a complete empty block header.
|
|
137
|
+
/// @param abs Absolute position to inspect.
|
|
138
|
+
/// @param end Absolute region boundary.
|
|
139
|
+
/// @param key Expected block key.
|
|
140
|
+
/// @return Whether the expected key occurs with a zero-length payload.
|
|
141
|
+
function isEmpty(uint abs, uint end, bytes4 key) internal pure returns (bool) {
|
|
142
|
+
if (!hasAt(abs, end, key)) return false;
|
|
143
|
+
return uint32(uint(read32(abs)) >> 192) == 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
114
146
|
/// @notice Find the first block with `key` at or after absolute position `abs`.
|
|
115
147
|
/// @param abs Absolute search position.
|
|
116
148
|
/// @param end Absolute region boundary.
|
|
@@ -160,6 +192,18 @@ library Blocks {
|
|
|
160
192
|
|
|
161
193
|
// Generic block writes
|
|
162
194
|
|
|
195
|
+
/// @notice Write an empty block header at `i`.
|
|
196
|
+
/// @dev DANGER: Unchecked memory write. Reserve `Sizes.Header` bytes first.
|
|
197
|
+
/// @param dst Destination buffer.
|
|
198
|
+
/// @param i Relative write position.
|
|
199
|
+
/// @param key Block key.
|
|
200
|
+
function writeEmpty(bytes memory dst, uint i, bytes4 key) internal pure {
|
|
201
|
+
uint head = uint(uint32(key)) << 224;
|
|
202
|
+
assembly ("memory-safe") {
|
|
203
|
+
mstore(add(add(dst, 0x20), i), head)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
163
207
|
/// @notice Write a custom block with one payload word at `i`.
|
|
164
208
|
/// @dev DANGER: Unchecked memory write. Reserve `Sizes.B32` bytes first.
|
|
165
209
|
/// @param dst Destination buffer.
|
|
@@ -1729,6 +1773,14 @@ library Blocks {
|
|
|
1729
1773
|
|
|
1730
1774
|
// Generic factories
|
|
1731
1775
|
|
|
1776
|
+
/// @notice Encode an empty block.
|
|
1777
|
+
/// @param key Block type key.
|
|
1778
|
+
/// @return value Encoded empty block header.
|
|
1779
|
+
function empty(bytes4 key) internal pure returns (bytes memory value) {
|
|
1780
|
+
value = allocate(Sizes.Header);
|
|
1781
|
+
writeEmpty(value, 0, key);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1732
1784
|
/// @notice Encode a block with a raw payload.
|
|
1733
1785
|
/// @param key Block type key.
|
|
1734
1786
|
/// @param payload Raw payload bytes.
|
|
@@ -1831,6 +1883,14 @@ library Blocks {
|
|
|
1831
1883
|
/// @notice Encode a SCHEMA block.
|
|
1832
1884
|
/// @param spec Block specification.
|
|
1833
1885
|
/// @param body Schema body.
|
|
1886
|
+
/// @return value Encoded SCHEMA block bytes.
|
|
1887
|
+
function schema(uint spec, string memory body) internal pure returns (bytes memory value) {
|
|
1888
|
+
return schema(spec, body, bytes32(0));
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/// @notice Encode a named SCHEMA block.
|
|
1892
|
+
/// @param spec Block specification.
|
|
1893
|
+
/// @param body Schema body.
|
|
1834
1894
|
/// @param name Schema name.
|
|
1835
1895
|
/// @return value Encoded SCHEMA block bytes.
|
|
1836
1896
|
function schema(uint spec, string memory body, bytes32 name) internal pure returns (bytes memory value) {
|
|
@@ -1931,7 +1991,8 @@ library Blocks {
|
|
|
1931
1991
|
}
|
|
1932
1992
|
|
|
1933
1993
|
/// @notice Encode a RELAY block.
|
|
1934
|
-
/// @param portal Destination portal
|
|
1994
|
+
/// @param portal Destination portal implementation's host ID, passed through
|
|
1995
|
+
/// without semantic validation.
|
|
1935
1996
|
/// @param resources Chain-specific resources for the destination context.
|
|
1936
1997
|
/// @param input Nested input block stream.
|
|
1937
1998
|
/// @return value Encoded RELAY block bytes.
|
|
@@ -1949,7 +2010,8 @@ library Blocks {
|
|
|
1949
2010
|
}
|
|
1950
2011
|
|
|
1951
2012
|
/// @notice Encode a DISPATCH block.
|
|
1952
|
-
/// @param portal Destination portal
|
|
2013
|
+
/// @param portal Destination portal implementation's host ID, passed through
|
|
2014
|
+
/// without semantic validation.
|
|
1953
2015
|
/// @param resources Chain-specific resources for the destination dispatch.
|
|
1954
2016
|
/// @param payload Encoded payload.
|
|
1955
2017
|
/// @return value Encoded DISPATCH block bytes.
|
package/codec/Decoders.sol
CHANGED
|
@@ -52,6 +52,13 @@ library Decoders {
|
|
|
52
52
|
return cur.state.more();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/// @notice Return the cursor's current absolute calldata position.
|
|
56
|
+
/// @param cur Cursor to inspect.
|
|
57
|
+
/// @return Current absolute calldata position.
|
|
58
|
+
function absolute(Cur memory cur) internal pure returns (uint) {
|
|
59
|
+
return cur.state.absolute();
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
/// @notice Validate and consume the next block from a cursor.
|
|
56
63
|
/// @param cur Cursor advanced over the complete block.
|
|
57
64
|
/// @param spec Expected block specification.
|
|
@@ -62,6 +69,18 @@ library Decoders {
|
|
|
62
69
|
cur.state = cur.state.seekAbs(end);
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
/// @notice Consume a matching empty block from a cursor when present.
|
|
73
|
+
/// @param cur Cursor advanced only when the matching block is empty.
|
|
74
|
+
/// @param key Expected block key.
|
|
75
|
+
/// @return Whether an empty block was consumed.
|
|
76
|
+
function tryConsumeEmpty(Cur memory cur, bytes4 key) internal pure returns (bool) {
|
|
77
|
+
(uint i, uint offset, uint size) = cur.state.decode();
|
|
78
|
+
(bytes4 current, uint len) = Blocks.peek(offset + i, offset + size);
|
|
79
|
+
if (current != key || len != 0) return false;
|
|
80
|
+
cur.state = cur.state.seek(i + Sizes.Header);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
/// @notice Validate and enter the payload of the next block in a cursor.
|
|
66
85
|
/// @dev The cursor remains in its existing frame so callers can decode child
|
|
67
86
|
/// blocks in place. Callers should prove complete payload consumption with
|
|
@@ -71,8 +90,38 @@ library Decoders {
|
|
|
71
90
|
/// @return abs Absolute position of the first payload byte.
|
|
72
91
|
/// @return end Absolute position immediately after the payload.
|
|
73
92
|
function enter(Cur memory cur, uint spec) internal pure returns (uint abs, uint end) {
|
|
93
|
+
return enter(cur, spec, 0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// @notice Validate a parent block and advance over a fixed payload prefix.
|
|
97
|
+
/// @dev `amount` is relative to the payload start and cannot exceed the
|
|
98
|
+
/// current parent payload. The returned `abs` remains the payload start.
|
|
99
|
+
/// @param cur Cursor advanced over the block header and fixed prefix.
|
|
100
|
+
/// @param spec Expected parent block specification.
|
|
101
|
+
/// @param amount Number of initial payload bytes to advance over.
|
|
102
|
+
/// @return abs Absolute position of the first payload byte.
|
|
103
|
+
/// @return end Absolute position immediately after the payload.
|
|
104
|
+
function enter(Cur memory cur, uint spec, uint amount) internal pure returns (uint abs, uint end) {
|
|
74
105
|
(abs, end) = Blocks.expect(cur.state.absolute(), spec);
|
|
75
|
-
|
|
106
|
+
if (amount > end - abs) revert Blocks.InvalidBlock();
|
|
107
|
+
cur.state = cur.state.seekAbs(abs + amount);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// @notice Advance a cursor by a raw byte count.
|
|
111
|
+
/// @dev No block header or schema is validated.
|
|
112
|
+
/// @param cur Cursor advanced by `amount` bytes.
|
|
113
|
+
/// @param amount Number of bytes to advance.
|
|
114
|
+
function advance(Cur memory cur, uint amount) internal pure {
|
|
115
|
+
cur.state = cur.state.advance(amount);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// @notice Take a raw byte range from the cursor.
|
|
119
|
+
/// @dev No block header or schema is validated.
|
|
120
|
+
/// @param cur Cursor advanced by `amount` bytes.
|
|
121
|
+
/// @param amount Number of bytes to take.
|
|
122
|
+
/// @return abs Absolute position of the first taken byte.
|
|
123
|
+
function take(Cur memory cur, uint amount) internal pure returns (uint abs) {
|
|
124
|
+
(cur.state, abs) = cur.state.consume(amount);
|
|
76
125
|
}
|
|
77
126
|
|
|
78
127
|
/// @notice Require a decoder cursor to be at absolute position `abs`.
|
|
@@ -128,9 +177,7 @@ library Decoders {
|
|
|
128
177
|
/// @return len Payload length.
|
|
129
178
|
function peek(Cur memory cur, uint i) internal pure returns (bytes4 key, uint len) {
|
|
130
179
|
(, uint offset, uint size) = cur.state.decode();
|
|
131
|
-
|
|
132
|
-
(key, len) = Blocks.header(offset + i);
|
|
133
|
-
if (len > size - i - Sizes.Header) revert Blocks.MalformedBlocks();
|
|
180
|
+
return Blocks.peek(offset + i, offset + size);
|
|
134
181
|
}
|
|
135
182
|
|
|
136
183
|
/// @notice Return the relative position immediately after the current block.
|
|
@@ -161,6 +208,15 @@ library Decoders {
|
|
|
161
208
|
return Blocks.hasAt(offset + i, offset + len, key);
|
|
162
209
|
}
|
|
163
210
|
|
|
211
|
+
/// @notice Return whether the current block has `key` and an empty payload.
|
|
212
|
+
/// @param cur Cursor positioned at a block.
|
|
213
|
+
/// @param key Expected block key.
|
|
214
|
+
/// @return Whether a complete matching empty block header occurs at the current position.
|
|
215
|
+
function isEmpty(Cur memory cur, bytes4 key) internal pure returns (bool) {
|
|
216
|
+
(uint i, uint offset, uint len) = cur.state.decode();
|
|
217
|
+
return Blocks.isEmpty(offset + i, offset + len, key);
|
|
218
|
+
}
|
|
219
|
+
|
|
164
220
|
/// @notice Find `key` at or after relative position `i`.
|
|
165
221
|
/// @param cur Cursor to search.
|
|
166
222
|
/// @param i Relative search position.
|
|
@@ -194,7 +250,15 @@ library Decoders {
|
|
|
194
250
|
/// @param cur Cursor advanced past the list.
|
|
195
251
|
/// @return items Cursor spanning the list payload.
|
|
196
252
|
function list(Cur memory cur) internal pure returns (Cur memory items) {
|
|
197
|
-
|
|
253
|
+
return list(cur, Specs.List);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// @notice Consume a list block described by `spec` and return a cursor over its items.
|
|
257
|
+
/// @param cur Cursor advanced past the list.
|
|
258
|
+
/// @param spec Custom list block specification.
|
|
259
|
+
/// @return items Cursor spanning the list payload.
|
|
260
|
+
function list(Cur memory cur, uint spec) internal pure returns (Cur memory items) {
|
|
261
|
+
(uint abs, uint end) = consume(cur, spec);
|
|
198
262
|
items.state = Cursors.create(abs, end - abs, 0, 0, 0);
|
|
199
263
|
}
|
|
200
264
|
|
|
@@ -202,7 +266,7 @@ library Decoders {
|
|
|
202
266
|
/// @param cur Cursor advanced past the block.
|
|
203
267
|
/// @param key Expected block key.
|
|
204
268
|
/// @return out Cursor spanning the complete encoded block.
|
|
205
|
-
function
|
|
269
|
+
function takeBlock(Cur memory cur, bytes4 key) internal pure returns (Cur memory out) {
|
|
206
270
|
uint abs = cur.state.absolute();
|
|
207
271
|
(, uint end) = consume(cur, Specs.create(key, 0, 0, 0));
|
|
208
272
|
out.state = Cursors.create(abs, end - abs, 0, 0, 0);
|
|
@@ -248,9 +312,7 @@ library Decoders {
|
|
|
248
312
|
|
|
249
313
|
/// @dev Return the next raw calldata word and advance by `size` bytes.
|
|
250
314
|
function next(Cur memory cur, uint size) private pure returns (bytes32 value) {
|
|
251
|
-
|
|
252
|
-
(cur.state, abs) = cur.state.consume(size);
|
|
253
|
-
value = Blocks.read32(abs);
|
|
315
|
+
value = Blocks.read32(take(cur, size));
|
|
254
316
|
}
|
|
255
317
|
|
|
256
318
|
/// @notice Return the next raw byte and advance the cursor by one byte.
|
package/codec/Descriptors.sol
CHANGED
|
@@ -4,25 +4,32 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
import {Specs} from "./Specs.sol";
|
|
5
5
|
import {Lanes} from "../utils/Lanes.sol";
|
|
6
6
|
|
|
7
|
+
/// @title Flags
|
|
8
|
+
/// @notice Packed endpoint behavior flags.
|
|
9
|
+
library Flags {
|
|
10
|
+
/// @dev Endpoint accepts nonzero native value.
|
|
11
|
+
uint8 internal constant Funded = 1 << 0;
|
|
12
|
+
/// @dev Endpoint is restricted to the admin account.
|
|
13
|
+
uint8 internal constant Admin = 1 << 1;
|
|
14
|
+
/// @dev Endpoint accepts nonzero native value and is restricted to the admin account.
|
|
15
|
+
uint8 internal constant AdminFunded = Admin | Funded;
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
/// @title Descriptors
|
|
8
19
|
/// @notice Packing and lane metadata helpers for endpoint descriptors.
|
|
9
20
|
library Descriptors {
|
|
10
21
|
/// @dev The requested lane is not part of an endpoint descriptor.
|
|
11
22
|
error InvalidLane();
|
|
12
23
|
|
|
13
|
-
/// @dev Endpoint accepts nonzero native value.
|
|
14
|
-
uint8 internal constant Funded = 1 << 0;
|
|
15
|
-
/// @dev Endpoint is restricted to the admin account.
|
|
16
|
-
uint8 internal constant Admin = 1 << 1;
|
|
17
|
-
|
|
18
24
|
/// @notice Create a descriptor from endpoint lane specifications and flags.
|
|
19
25
|
/// @dev Layout: `[state key:4][stride:1]`
|
|
20
|
-
/// `[input key:4][
|
|
26
|
+
/// `[input key:4][stride:1]`
|
|
21
27
|
/// `[output key:4][min:4][max:4][hint:3][stride:1]`
|
|
28
|
+
/// `[reserved:4]`
|
|
22
29
|
/// `[transactions:1]`
|
|
23
30
|
/// `[flags:1]`. Flag bits: funded = 0, admin = 1.
|
|
24
31
|
/// @param state State lane specification.
|
|
25
|
-
/// @param input
|
|
32
|
+
/// @param input Direct input lane specification.
|
|
26
33
|
/// @param output Output writer specification.
|
|
27
34
|
/// @param transactions Transactions produced per batch.
|
|
28
35
|
/// @param flags Endpoint behavior flags.
|
|
@@ -34,15 +41,13 @@ library Descriptors {
|
|
|
34
41
|
uint8 transactions,
|
|
35
42
|
uint8 flags
|
|
36
43
|
) internal pure returns (uint descriptor) {
|
|
37
|
-
|
|
38
|
-
input = Specs.normalize(input, false);
|
|
39
|
-
output = Specs.normalize(output, true);
|
|
44
|
+
output = Specs.normalize(output);
|
|
40
45
|
descriptor = pack(state, input, output, transactions, flags);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
/// @dev Pack
|
|
44
|
-
/// @param state
|
|
45
|
-
/// @param input
|
|
48
|
+
/// @dev Pack endpoint specs and flags into a descriptor.
|
|
49
|
+
/// @param state State specification.
|
|
50
|
+
/// @param input Input specification.
|
|
46
51
|
/// @param output Normalized direct output specification.
|
|
47
52
|
/// @param transactions Transactions produced per batch.
|
|
48
53
|
/// @param flags Endpoint behavior flags.
|
|
@@ -54,14 +59,9 @@ library Descriptors {
|
|
|
54
59
|
uint8 transactions,
|
|
55
60
|
uint8 flags
|
|
56
61
|
) private pure returns (uint descriptor) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
descriptor |=
|
|
60
|
-
descriptor |= uint(Specs.stride(state)) << 216;
|
|
61
|
-
descriptor |= uint(uint32(outer)) << 184;
|
|
62
|
-
descriptor |= uint(uint32(child)) << 152;
|
|
63
|
-
descriptor |= uint(Specs.stride(input)) << 144;
|
|
64
|
-
descriptor |= (output >> 128) << 16;
|
|
62
|
+
descriptor |= uint(Specs.lane(state)) << 216;
|
|
63
|
+
descriptor |= uint(Specs.lane(input)) << 176;
|
|
64
|
+
descriptor |= (output >> 128) << 48;
|
|
65
65
|
descriptor |= uint(transactions) << 8;
|
|
66
66
|
descriptor |= flags;
|
|
67
67
|
}
|
|
@@ -82,22 +82,22 @@ library Descriptors {
|
|
|
82
82
|
/// @return Effective blocks per batch for the lane.
|
|
83
83
|
function stride(uint descriptor, uint8 lane) internal pure returns (uint8) {
|
|
84
84
|
if (lane == Lanes.State) return uint8(descriptor >> 216);
|
|
85
|
-
if (lane == Lanes.Input) return uint8(descriptor >>
|
|
86
|
-
if (lane == Lanes.Output) return uint8(descriptor >>
|
|
85
|
+
if (lane == Lanes.Input) return uint8(descriptor >> 176);
|
|
86
|
+
if (lane == Lanes.Output) return uint8(descriptor >> 48);
|
|
87
87
|
if (lane == Lanes.Transactions) return uint8(descriptor >> 8);
|
|
88
88
|
revert InvalidLane();
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/// @notice Return the effective block key for `lane`.
|
|
92
|
-
/// @dev
|
|
93
|
-
///
|
|
92
|
+
/// @dev Transaction blocks have a fixed protocol key that is not stored in
|
|
93
|
+
/// the descriptor.
|
|
94
94
|
/// @param descriptor Packed endpoint descriptor.
|
|
95
95
|
/// @param lane Lane identifier from `Lanes`.
|
|
96
|
-
/// @return Effective
|
|
96
|
+
/// @return Effective block key for the lane.
|
|
97
97
|
function key(uint descriptor, uint8 lane) internal pure returns (bytes4) {
|
|
98
98
|
if (lane == Lanes.State) return bytes4(uint32(descriptor >> 224));
|
|
99
99
|
if (lane == Lanes.Input) return bytes4(uint32(descriptor >> 184));
|
|
100
|
-
if (lane == Lanes.Output) return bytes4(uint32(descriptor >>
|
|
100
|
+
if (lane == Lanes.Output) return bytes4(uint32(descriptor >> 144));
|
|
101
101
|
if (lane == Lanes.Transactions) return Specs.key(Specs.Transaction);
|
|
102
102
|
revert InvalidLane();
|
|
103
103
|
}
|
|
@@ -110,7 +110,7 @@ library Descriptors {
|
|
|
110
110
|
/// @return growable Whether the writer may grow beyond that capacity.
|
|
111
111
|
function allocation(uint descriptor, uint8 lane, uint groups) internal pure returns (uint capacity, bool growable) {
|
|
112
112
|
if (lane == Lanes.Output) {
|
|
113
|
-
uint spec = uint(uint128(descriptor >>
|
|
113
|
+
uint spec = uint(uint128(descriptor >> 48)) << 128;
|
|
114
114
|
return Specs.allocation(spec, groups);
|
|
115
115
|
}
|
|
116
116
|
if (lane == Lanes.Transactions) {
|