@rootzero/contracts 1.10.0 → 1.11.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 +20 -1
- package/Core.sol +1 -0
- package/Endpoints.sol +1 -3
- package/Events.sol +2 -5
- package/README.md +35 -33
- package/Utils.sol +2 -1
- package/blocks/Cursors.sol +26 -75
- package/blocks/Keys.sol +14 -4
- package/blocks/Schema.sol +49 -44
- package/commands/Base.sol +43 -13
- package/commands/Burn.sol +3 -6
- package/commands/Credit.sol +9 -6
- package/commands/Debit.sol +15 -11
- package/commands/Deposit.sol +19 -23
- package/commands/Payout.sol +6 -10
- package/commands/Provision.sol +19 -23
- package/commands/Recover.sol +7 -9
- package/commands/Relay.sol +10 -11
- package/commands/Withdraw.sol +3 -6
- package/commands/admin/AllowAssets.sol +7 -10
- package/commands/admin/Allowance.sol +7 -10
- package/commands/admin/Appoint.sol +7 -10
- package/commands/admin/Authorize.sol +7 -10
- package/commands/admin/Base.sol +1 -2
- package/commands/admin/DenyAssets.sol +7 -10
- package/commands/admin/Dismiss.sol +7 -10
- package/commands/admin/Execute.sol +7 -10
- package/commands/admin/Label.sol +8 -11
- package/commands/admin/Unauthorize.sol +7 -10
- package/core/Endpoint.sol +153 -0
- package/docs/Schema.md +114 -82
- package/events/Endpoint.sol +19 -0
- package/events/Schema.sol +23 -0
- package/guards/Base.sol +17 -8
- package/guards/Revoke.sol +4 -6
- package/package.json +1 -1
- package/ports/AllowAssets.sol +6 -9
- package/ports/Allowance.sol +6 -9
- package/ports/Base.sol +21 -8
- package/ports/Credit.sol +6 -9
- package/ports/Debit.sol +6 -9
- package/ports/DenyAssets.sol +6 -9
- package/ports/Dispatch.sol +6 -9
- package/ports/Pipe.sol +4 -7
- package/ports/Redeem.sol +4 -7
- package/ports/Settle.sol +6 -9
- package/queries/Assets.sol +9 -12
- package/queries/Balances.sol +7 -9
- package/queries/Base.sol +19 -11
- package/utils/Selectors.sol +49 -0
- package/commands/admin/Destroy.sol +0 -43
- package/commands/admin/Init.sol +0 -43
- package/events/Admin.sol +0 -32
- package/events/Command.sol +0 -32
- package/events/Guard.sol +0 -18
- package/events/Port.sol +0 -22
- package/events/Query.sol +0 -20
- package/queries/Positions.sol +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,26 @@
|
|
|
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
|
-
##
|
|
6
|
+
## 1.11.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Replaced the separate command, port, query, guard, and admin discovery events
|
|
11
|
+
with the shared `Endpoint` event and a packed endpoint descriptor. Admin
|
|
12
|
+
commands are now identified by the descriptor admin flag. Default lane groups
|
|
13
|
+
remain encoded as zero and are interpreted as group size one when read; the
|
|
14
|
+
final four descriptor bytes are reserved.
|
|
15
|
+
- Renamed the block discovery event to `Schema` and added a `name` field for
|
|
16
|
+
alias-style schema references.
|
|
17
|
+
- Removed the generic `DATA` block type. Custom input blocks should define
|
|
18
|
+
their own local keys and publish schemas explicitly when they need discovery.
|
|
19
|
+
- Removed the built-in admin `init` and `destroy` commands.
|
|
20
|
+
- Removed the built-in `Positions` query; custom position output should be
|
|
21
|
+
modeled as a custom query.
|
|
22
|
+
- Changed `Cursors.list` to consume the LIST block and return a cursor scoped
|
|
23
|
+
to its payload instead of returning the list's end offset.
|
|
24
|
+
- Renamed the `CommandContext.request` field to `input`. The tuple layout and
|
|
25
|
+
command selectors are unchanged.
|
|
7
26
|
|
|
8
27
|
## 1.10.0
|
|
9
28
|
|
package/Core.sol
CHANGED
|
@@ -10,6 +10,7 @@ import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
|
|
|
10
10
|
import { NativeAsset, Runtime } from "./core/Runtime.sol";
|
|
11
11
|
import { Host, IHostIntroduction } from "./core/Host.sol";
|
|
12
12
|
import { CommandCalls, FailedCall, NodeCalls, PortCalls } from "./core/Calls.sol";
|
|
13
|
+
import { EndpointBase, Lane } from "./core/Endpoint.sol";
|
|
13
14
|
import { Payable } from "./core/Payable.sol";
|
|
14
15
|
import { Pipeline } from "./core/Pipeline.sol";
|
|
15
16
|
import { Portal } from "./core/Portal.sol";
|
package/Endpoints.sol
CHANGED
|
@@ -7,6 +7,7 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
// Shared helpers
|
|
8
8
|
import { Keys } from "./blocks/Keys.sol";
|
|
9
9
|
import { CommandBase, CommandContext } from "./commands/Base.sol";
|
|
10
|
+
import { EndpointBase, Lane } from "./core/Endpoint.sol";
|
|
10
11
|
import { Payable } from "./core/Payable.sol";
|
|
11
12
|
|
|
12
13
|
// Commands
|
|
@@ -26,11 +27,9 @@ import { AllowAssets, AllowAssetsHook } from "./commands/admin/AllowAssets.sol";
|
|
|
26
27
|
import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
|
|
27
28
|
import { Appoint } from "./commands/admin/Appoint.sol";
|
|
28
29
|
import { Authorize } from "./commands/admin/Authorize.sol";
|
|
29
|
-
import { Destroy, DestroyHook } from "./commands/admin/Destroy.sol";
|
|
30
30
|
import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
|
|
31
31
|
import { Dismiss } from "./commands/admin/Dismiss.sol";
|
|
32
32
|
import { ExecutePayable } from "./commands/admin/Execute.sol";
|
|
33
|
-
import { Init, InitHook } from "./commands/admin/Init.sol";
|
|
34
33
|
import { Label } from "./commands/admin/Label.sol";
|
|
35
34
|
import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
36
35
|
|
|
@@ -54,4 +53,3 @@ import { Revoke } from "./guards/Revoke.sol";
|
|
|
54
53
|
import { QueryBase } from "./queries/Base.sol";
|
|
55
54
|
import { AssetStatus, AssetStatusHook } from "./queries/Assets.sol";
|
|
56
55
|
import { GetBalances, GetBalancesHook } from "./queries/Balances.sol";
|
|
57
|
-
import { GetPosition, GetPositionHook } from "./queries/Positions.sol";
|
package/Events.sol
CHANGED
|
@@ -4,27 +4,24 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
// Aggregator: re-exports all event contracts.
|
|
5
5
|
// Import this file to get access to every event emitter in one import.
|
|
6
6
|
|
|
7
|
-
import { AdminEvent } from "./events/Admin.sol";
|
|
8
7
|
import { AssetEvent, AssetStatusEvent } from "./events/Asset.sol";
|
|
9
8
|
import { Actions } from "./utils/Actions.sol";
|
|
10
9
|
import { BalanceEvent } from "./events/Balance.sol";
|
|
11
10
|
import { CommanderEvent } from "./events/Commander.sol";
|
|
12
|
-
import { CommandEvent } from "./events/Command.sol";
|
|
13
11
|
import { DispatchEvent } from "./events/Dispatch.sol";
|
|
12
|
+
import { EndpointEvent } from "./events/Endpoint.sol";
|
|
14
13
|
import { PositionEvent } from "./events/Position.sol";
|
|
15
14
|
import { ReceivedEvent } from "./events/Received.sol";
|
|
16
15
|
import { RecoveredEvent } from "./events/Recovered.sol";
|
|
17
16
|
import { EventEmitter } from "./events/Emitter.sol";
|
|
18
|
-
import { GuardEvent } from "./events/Guard.sol";
|
|
19
17
|
import { GuardianEvent } from "./events/Guardian.sol";
|
|
20
18
|
import { IntroductionEvent } from "./events/Introduction.sol";
|
|
21
19
|
import { LabeledEvent } from "./events/Labeled.sol";
|
|
22
20
|
import { LockedEvent } from "./events/Locked.sol";
|
|
23
21
|
import { NodeEvent } from "./events/Node.sol";
|
|
24
|
-
import { PortEvent } from "./events/Port.sol";
|
|
25
|
-
import { QueryEvent } from "./events/Query.sol";
|
|
26
22
|
import { RootedEvent } from "./events/Rooted.sol";
|
|
27
23
|
import { RouteEvent } from "./events/Route.sol";
|
|
24
|
+
import { SchemaEvent } from "./events/Schema.sol";
|
|
28
25
|
import { SpentEvent } from "./events/Spent.sol";
|
|
29
26
|
import { UndeliveredEvent } from "./events/Undelivered.sol";
|
|
30
27
|
import { UnlockedEvent } from "./events/Unlocked.sol";
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ const host = await ethers.deployContract("ExampleHost", [deployer.address]);
|
|
|
58
58
|
|
|
59
59
|
const account = encodeUserAccount(user.address); // receiving account
|
|
60
60
|
const request = encodeAmountBlock(asset, 100n); // what to deposit
|
|
61
|
-
await host.deposit({ account, state: "0x", request }); // emits Balance
|
|
61
|
+
await host.deposit({ account, state: "0x", input: request }); // emits Balance
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The rest of this guide explains the ideas this example leans on — blocks, IDs,
|
|
@@ -74,11 +74,12 @@ payload:
|
|
|
74
74
|
[bytes4 key][uint32 payloadLen][payload]
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
The key is `bytes4(keccak256("#name"))`, and the payload layout is
|
|
78
|
-
a schema
|
|
77
|
+
The key is usually `bytes4(keccak256("#name"))`, and the payload layout is
|
|
78
|
+
described by a schema body published under an alias. For example, the standard
|
|
79
|
+
`amount` block that requests a deposit:
|
|
79
80
|
|
|
80
81
|
```txt
|
|
81
|
-
|
|
82
|
+
amount { bytes32 asset, uint amount }
|
|
82
83
|
```
|
|
83
84
|
|
|
84
85
|
is 72 bytes on the wire: an 8-byte header followed by two big-endian 32-byte
|
|
@@ -102,8 +103,12 @@ on the wire. The full schema language is specified in
|
|
|
102
103
|
A request is not a single struct; it is a run of blocks. One `#amount` block
|
|
103
104
|
asks for one deposit, five blocks ask for five, and the code path is identical
|
|
104
105
|
— every endpoint parses with a cursor and loops until the stream is exhausted.
|
|
105
|
-
The
|
|
106
|
-
|
|
106
|
+
The descriptor lane key is the prime item: it is the block type that may repeat
|
|
107
|
+
for batching. Plain lanes are encoded as `[key][0]`; readers interpret the zero
|
|
108
|
+
group byte as group size 1 when the lane is non-empty. Generic list lanes such
|
|
109
|
+
as `many #asset` are encoded as
|
|
110
|
+
`[Keys.List][Keys.Asset]`, so indexers can see both the top-level LIST container
|
|
111
|
+
and the item type inside it.
|
|
107
112
|
|
|
108
113
|
Off-chain, building a batch is concatenation. Using the reference encoders from
|
|
109
114
|
[`test/helpers/blocks.ts`](test/helpers/blocks.ts):
|
|
@@ -207,49 +212,46 @@ Commands are the write endpoints. Every command receives the same context:
|
|
|
207
212
|
struct CommandContext {
|
|
208
213
|
bytes32 account; // acting account
|
|
209
214
|
bytes state; // block stream produced by the previous command
|
|
210
|
-
bytes
|
|
215
|
+
bytes input; // block stream for this invocation
|
|
211
216
|
}
|
|
212
217
|
```
|
|
213
218
|
|
|
214
|
-
The
|
|
219
|
+
The input carries instructions; the state carries live value. While a
|
|
215
220
|
sequence of commands executes, `#balance` and `#custody` blocks in the state
|
|
216
221
|
are the funds being moved — produced by one command, consumed by the next.
|
|
217
222
|
|
|
218
|
-
The standard `Deposit` mixin shows the canonical shape —
|
|
219
|
-
the batch, call the hook, write the output run:
|
|
223
|
+
The standard `Deposit` mixin shows the canonical shape — open the input,
|
|
224
|
+
loop the batch, call the hook, write the output run:
|
|
220
225
|
|
|
221
226
|
```solidity
|
|
222
227
|
function deposit(CommandContext calldata c) external onlyCommand returns (bytes memory) {
|
|
223
|
-
(Cur memory
|
|
224
|
-
Writer memory
|
|
228
|
+
(Cur memory input, uint outputs) = openInput(c.input, descriptor);
|
|
229
|
+
Writer memory output = Writers.allocBalances(outputs);
|
|
225
230
|
|
|
226
|
-
while (
|
|
227
|
-
(bytes32 asset, uint amount) =
|
|
231
|
+
while (input.i < input.len) {
|
|
232
|
+
(bytes32 asset, uint amount) = input.unpackAmount();
|
|
228
233
|
deposit(c.account, asset, amount); // host policy hook
|
|
229
|
-
|
|
234
|
+
output.appendBalance(asset, amount);
|
|
230
235
|
}
|
|
231
236
|
|
|
232
|
-
|
|
233
|
-
return writer.finish();
|
|
237
|
+
return output.finish();
|
|
234
238
|
}
|
|
235
239
|
```
|
|
236
240
|
|
|
237
241
|
A command announces itself when the host is deployed. Its constructor emits a
|
|
238
|
-
discovery event carrying
|
|
239
|
-
|
|
240
|
-
input state, one output block per operation), plus a human-readable label:
|
|
242
|
+
discovery event carrying a packed descriptor with the input, state, and output
|
|
243
|
+
lanes, derived group sizes, and flags, plus a human-readable label:
|
|
241
244
|
|
|
242
245
|
```solidity
|
|
243
246
|
abstract contract MyCommand is CommandBase {
|
|
244
|
-
|
|
247
|
+
bytes32 private immutable descriptor;
|
|
245
248
|
|
|
246
249
|
constructor() {
|
|
247
|
-
|
|
248
|
-
emit Labeled(myCommandId, bytes32(0), "myCommand");
|
|
250
|
+
(, descriptor) = command("myCommand", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
function myCommand(CommandContext calldata c) external onlyCommand returns (bytes memory) {
|
|
252
|
-
// parse c.
|
|
254
|
+
// parse c.input, loop, return the output state run
|
|
253
255
|
}
|
|
254
256
|
}
|
|
255
257
|
```
|
|
@@ -266,7 +268,7 @@ A single command is rarely the whole story. A pipeline is a run of `#step`
|
|
|
266
268
|
blocks executed in order within one transaction:
|
|
267
269
|
|
|
268
270
|
```txt
|
|
269
|
-
|
|
271
|
+
step { uint target, uint resources, #bytes as request }
|
|
270
272
|
```
|
|
271
273
|
|
|
272
274
|
Each step names a target command, the resources it may spend, and its request.
|
|
@@ -297,22 +299,22 @@ standard `getBalances` query takes a run of positions and answers each one in
|
|
|
297
299
|
order:
|
|
298
300
|
|
|
299
301
|
```txt
|
|
300
|
-
request:
|
|
301
|
-
response:
|
|
302
|
+
request: accountAsset { bytes32 account, bytes32 asset }
|
|
303
|
+
response: accountAmount { bytes32 account, bytes32 asset, uint amount }
|
|
302
304
|
```
|
|
303
305
|
|
|
304
|
-
Like commands, every query announces
|
|
305
|
-
|
|
306
|
+
Like commands, every query announces a descriptor at deployment; tooling resolves
|
|
307
|
+
the descriptor's lanes through the published block schemas.
|
|
306
308
|
|
|
307
309
|
## Ports
|
|
308
310
|
|
|
309
311
|
Ports are the host-to-host surfaces, callable only by trusted peer hosts. The two
|
|
310
312
|
central ones are batches all the way down:
|
|
311
313
|
|
|
312
|
-
- `portSettle` consumes
|
|
314
|
+
- `portSettle` consumes `transaction { bytes32 from, bytes32 to, bytes32 asset,
|
|
313
315
|
uint amount }` blocks, debiting `from` and crediting `to` per
|
|
314
316
|
block — how two hosts record settlement between their ledgers.
|
|
315
|
-
- `portPipePayable` consumes
|
|
317
|
+
- `portPipePayable` consumes `context` blocks, each carrying an account, an
|
|
316
318
|
initial state, and a run of steps — a complete pipeline delivered by another
|
|
317
319
|
host, executed locally against the port call's shared value budget.
|
|
318
320
|
|
|
@@ -339,8 +341,8 @@ drop a trusted node immediately.
|
|
|
339
341
|
## Events and Discovery
|
|
340
342
|
|
|
341
343
|
Hosts are self-describing. At deployment a host emits the ABI of every event it
|
|
342
|
-
uses (`EventAbi`),
|
|
343
|
-
|
|
344
|
+
uses (`EventAbi`), block schema events, endpoint descriptors, and labels for
|
|
345
|
+
human-readable names. State changes then follow evented
|
|
344
346
|
conventions: `Balance` for every ledger change and flow events (`Received`,
|
|
345
347
|
`Spent`, `Locked`, `Unlocked`) for value movement, each tagged with the endpoint that
|
|
346
348
|
caused it. An indexer can reconstruct the entire repository — endpoints,
|
package/Utils.sol
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
// Aggregator: re-exports all utility libraries (Keys, Accounts, Actions, Assets, ECDSA, Ids, Nodes, Layout, Utils, Value).
|
|
4
|
+
// Aggregator: re-exports all utility libraries (Keys, Accounts, Actions, Assets, ECDSA, Ids, Nodes, Selectors, Layout, Utils, Value).
|
|
5
5
|
// Import this file to access the full utility surface without managing individual paths.
|
|
6
6
|
|
|
7
7
|
import { Keys } from "./blocks/Keys.sol";
|
|
@@ -11,6 +11,7 @@ import { Amounts, Assets } from "./utils/Assets.sol";
|
|
|
11
11
|
import { ECDSA } from "./utils/ECDSA.sol";
|
|
12
12
|
import { Ids } from "./utils/Ids.sol";
|
|
13
13
|
import { Nodes } from "./utils/Nodes.sol";
|
|
14
|
+
import { Selectors } from "./utils/Selectors.sol";
|
|
14
15
|
import { Layout } from "./utils/Layout.sol";
|
|
15
16
|
import { Schemas } from "./blocks/Schema.sol";
|
|
16
17
|
import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, retryTicket, toLocalBase, toUnspecifiedBase, ValueOverflow } from "./utils/Utils.sol";
|
package/blocks/Cursors.sol
CHANGED
|
@@ -72,35 +72,22 @@ library Cursors {
|
|
|
72
72
|
|
|
73
73
|
/// @notice Create a cursor over `source` and restrict it to its first grouped run.
|
|
74
74
|
/// Equivalent to `open(source)`, reading the current key, then `run(key, group)`.
|
|
75
|
+
/// When `group` is zero, `source` must be empty and the function returns an empty cursor.
|
|
75
76
|
/// @param source Calldata slice that forms the block stream.
|
|
76
|
-
/// @param group Expected block group size (e.g. 1 for single, 2 for paired).
|
|
77
|
+
/// @param group Expected block group size (e.g. 1 for single, 2 for paired); 0 means empty.
|
|
77
78
|
/// @return cur Cursor with `len` truncated to the end of the first run in `source`.
|
|
78
79
|
/// @return groups Number of block groups in the run (`block count / group`).
|
|
79
|
-
function init(
|
|
80
|
-
bytes calldata source,
|
|
81
|
-
uint group
|
|
82
|
-
) internal pure returns (Cur memory cur, uint groups) {
|
|
80
|
+
function init(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint groups) {
|
|
83
81
|
cur = open(source);
|
|
84
|
-
if (
|
|
82
|
+
if (group == 0) {
|
|
83
|
+
if (cur.len != 0) revert IncompleteCursor();
|
|
84
|
+
return (cur, 0);
|
|
85
|
+
}
|
|
86
|
+
if (cur.len == 0) revert ZeroCursor();
|
|
85
87
|
(bytes4 key, ) = cur.peek(cur.i);
|
|
86
88
|
groups = cur.run(key, group);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
/// @notice Create a cursor over `source`, restrict it to its first grouped run, and require an exact group count.
|
|
90
|
-
/// @param source Calldata slice that forms the block stream.
|
|
91
|
-
/// @param group Expected block group size (e.g. 1 for single, 2 for paired).
|
|
92
|
-
/// @param expectedGroups Required number of groups in the run.
|
|
93
|
-
/// @return cur Cursor with `len` truncated to the end of the first run in `source`.
|
|
94
|
-
function init(
|
|
95
|
-
bytes calldata source,
|
|
96
|
-
uint group,
|
|
97
|
-
uint expectedGroups
|
|
98
|
-
) internal pure returns (Cur memory cur) {
|
|
99
|
-
uint groups;
|
|
100
|
-
(cur, groups) = init(source, group);
|
|
101
|
-
if (groups != expectedGroups) revert BadRatio();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
91
|
/// @notice Move the cursor to an absolute position within the source region.
|
|
105
92
|
/// @param cur Cursor to update.
|
|
106
93
|
/// @param i New read position (byte offset relative to source start).
|
|
@@ -330,17 +317,15 @@ library Cursors {
|
|
|
330
317
|
return find(cur, cur.i, key);
|
|
331
318
|
}
|
|
332
319
|
|
|
333
|
-
/// @notice
|
|
334
|
-
///
|
|
335
|
-
///
|
|
336
|
-
///
|
|
337
|
-
///
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
cur.ensureAt(pos);
|
|
343
|
-
next = enter(cur, Keys.List, 0, 0);
|
|
320
|
+
/// @notice Consume a LIST block and return a cursor over its payload.
|
|
321
|
+
/// Advances `cur.i` past the full list while the returned cursor is scoped to
|
|
322
|
+
/// the list members as a fresh zero-based region.
|
|
323
|
+
/// @param cur Cursor positioned at a list block; advanced past the full list.
|
|
324
|
+
/// @return items Cursor scoped to the list payload.
|
|
325
|
+
function list(Cur memory cur) internal pure returns (Cur memory items) {
|
|
326
|
+
uint next = enter(cur, Keys.List, 0, 0);
|
|
327
|
+
items = cur.slice(cur.i, next);
|
|
328
|
+
cur.i = next;
|
|
344
329
|
}
|
|
345
330
|
|
|
346
331
|
/// @notice Consume a block with the given key at the current position and return a cursor over the full block slice.
|
|
@@ -378,15 +363,6 @@ library Cursors {
|
|
|
378
363
|
return cur.isAt(key) ? take(cur, key) : cur.slice(cur.i, cur.i);
|
|
379
364
|
}
|
|
380
365
|
|
|
381
|
-
/// @notice Consume an optional DATA block at the current position and return a cursor over the full block slice.
|
|
382
|
-
/// If the current block is not DATA, returns an empty cursor and leaves `cur.i` unchanged.
|
|
383
|
-
/// Otherwise behaves like `take(cur, Keys.Data)`.
|
|
384
|
-
/// @param cur Cursor positioned at an optional DATA block.
|
|
385
|
-
/// @return out Cursor scoped to the full DATA block, or empty when no DATA block is present.
|
|
386
|
-
function maybeData(Cur memory cur) internal pure returns (Cur memory out) {
|
|
387
|
-
return maybeTake(cur, Keys.Data);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
366
|
/// @notice Ensure the cursor is at an exact position.
|
|
391
367
|
/// Reverts with `IncompleteCursor` if `pos` exceeds the cursor region length
|
|
392
368
|
/// or `cur.i != pos`.
|
|
@@ -825,10 +801,7 @@ library Cursors {
|
|
|
825
801
|
/// @param key Expected block key.
|
|
826
802
|
/// @return asset Asset identifier.
|
|
827
803
|
/// @return amount Scalar amount value.
|
|
828
|
-
function unpackAssetAmount(
|
|
829
|
-
Cur memory cur,
|
|
830
|
-
bytes4 key
|
|
831
|
-
) internal pure returns (bytes32 asset, uint amount) {
|
|
804
|
+
function unpackAssetAmount(Cur memory cur, bytes4 key) internal pure returns (bytes32 asset, uint amount) {
|
|
832
805
|
uint abs = consume(cur, 0, key, 64, 64);
|
|
833
806
|
asset = bytes32(msg.data[abs:abs + 32]);
|
|
834
807
|
amount = uint(bytes32(msg.data[abs + 32:abs + 64]));
|
|
@@ -992,9 +965,7 @@ library Cursors {
|
|
|
992
965
|
/// @return host Host node ID.
|
|
993
966
|
/// @return account Account identifier.
|
|
994
967
|
/// @return asset Asset identifier.
|
|
995
|
-
function unpackHostAccountAsset(
|
|
996
|
-
Cur memory cur
|
|
997
|
-
) internal pure returns (uint host, bytes32 account, bytes32 asset) {
|
|
968
|
+
function unpackHostAccountAsset(Cur memory cur) internal pure returns (uint host, bytes32 account, bytes32 asset) {
|
|
998
969
|
return unpackHostAccountAsset(cur, Keys.HostAccountAsset);
|
|
999
970
|
}
|
|
1000
971
|
|
|
@@ -1010,9 +981,7 @@ library Cursors {
|
|
|
1010
981
|
/// @return account Account identifier.
|
|
1011
982
|
/// @return asset Asset identifier.
|
|
1012
983
|
/// @return amount Token amount.
|
|
1013
|
-
function unpackAccountAmount(
|
|
1014
|
-
Cur memory cur
|
|
1015
|
-
) internal pure returns (bytes32 account, bytes32 asset, uint amount) {
|
|
984
|
+
function unpackAccountAmount(Cur memory cur) internal pure returns (bytes32 account, bytes32 asset, uint amount) {
|
|
1016
985
|
return unpackAccountAmount(cur, Keys.AccountAmount);
|
|
1017
986
|
}
|
|
1018
987
|
|
|
@@ -1028,9 +997,7 @@ library Cursors {
|
|
|
1028
997
|
/// @return host Host node ID.
|
|
1029
998
|
/// @return asset Asset identifier.
|
|
1030
999
|
/// @return amount Token amount.
|
|
1031
|
-
function unpackAllocation(
|
|
1032
|
-
Cur memory cur
|
|
1033
|
-
) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1000
|
+
function unpackAllocation(Cur memory cur) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1034
1001
|
return unpackHostAmount(cur, Keys.Allocation);
|
|
1035
1002
|
}
|
|
1036
1003
|
|
|
@@ -1046,9 +1013,7 @@ library Cursors {
|
|
|
1046
1013
|
/// @return host Host node ID.
|
|
1047
1014
|
/// @return asset Asset identifier.
|
|
1048
1015
|
/// @return amount Token amount.
|
|
1049
|
-
function unpackAllowance(
|
|
1050
|
-
Cur memory cur
|
|
1051
|
-
) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1016
|
+
function unpackAllowance(Cur memory cur) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1052
1017
|
return unpackHostAmount(cur, Keys.Allowance);
|
|
1053
1018
|
}
|
|
1054
1019
|
|
|
@@ -1145,9 +1110,7 @@ library Cursors {
|
|
|
1145
1110
|
/// @return portal Destination portal identifier, often the destination host ID.
|
|
1146
1111
|
/// @return resources Chain-specific resources for the destination context.
|
|
1147
1112
|
/// @return request Embedded request block stream.
|
|
1148
|
-
function unpackRelay(
|
|
1149
|
-
Cur memory cur
|
|
1150
|
-
) internal pure returns (uint portal, uint resources, bytes calldata request) {
|
|
1113
|
+
function unpackRelay(Cur memory cur) internal pure returns (uint portal, uint resources, bytes calldata request) {
|
|
1151
1114
|
uint end = cur.enter(Keys.Relay, 64 + Sizes.Header, 0);
|
|
1152
1115
|
portal = cur.readUint();
|
|
1153
1116
|
resources = cur.readUint();
|
|
@@ -1214,11 +1177,7 @@ library Cursors {
|
|
|
1214
1177
|
/// @param key Expected block type key.
|
|
1215
1178
|
/// @param asset Expected asset identifier.
|
|
1216
1179
|
/// @return amount Amount from the block.
|
|
1217
|
-
function requireAssetAmount(
|
|
1218
|
-
Cur memory cur,
|
|
1219
|
-
bytes4 key,
|
|
1220
|
-
bytes32 asset
|
|
1221
|
-
) internal pure returns (uint amount) {
|
|
1180
|
+
function requireAssetAmount(Cur memory cur, bytes4 key, bytes32 asset) internal pure returns (uint amount) {
|
|
1222
1181
|
uint abs = consume(cur, 0, key, 64, 64);
|
|
1223
1182
|
if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
|
|
1224
1183
|
amount = uint(bytes32(msg.data[abs + 32:abs + 64]));
|
|
@@ -1274,12 +1233,7 @@ library Cursors {
|
|
|
1274
1233
|
/// @param key Expected block type key.
|
|
1275
1234
|
/// @param host Expected host node ID.
|
|
1276
1235
|
/// @param asset Expected asset identifier.
|
|
1277
|
-
function requireUnitHostAmount(
|
|
1278
|
-
Cur memory cur,
|
|
1279
|
-
bytes4 key,
|
|
1280
|
-
uint host,
|
|
1281
|
-
bytes32 asset
|
|
1282
|
-
) internal pure {
|
|
1236
|
+
function requireUnitHostAmount(Cur memory cur, bytes4 key, uint host, bytes32 asset) internal pure {
|
|
1283
1237
|
uint abs = consume(cur, 0, key, 96, 96);
|
|
1284
1238
|
if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
|
|
1285
1239
|
if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
|
|
@@ -1326,10 +1280,7 @@ library Cursors {
|
|
|
1326
1280
|
/// @param host Expected host node ID.
|
|
1327
1281
|
/// @return account Account identifier from the block.
|
|
1328
1282
|
/// @return asset Asset identifier from the block.
|
|
1329
|
-
function requireHostAccountAsset(
|
|
1330
|
-
Cur memory cur,
|
|
1331
|
-
uint host
|
|
1332
|
-
) internal pure returns (bytes32 account, bytes32 asset) {
|
|
1283
|
+
function requireHostAccountAsset(Cur memory cur, uint host) internal pure returns (bytes32 account, bytes32 asset) {
|
|
1333
1284
|
return requireHostAccountAsset(cur, Keys.HostAccountAsset, host);
|
|
1334
1285
|
}
|
|
1335
1286
|
|
package/blocks/Keys.sol
CHANGED
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
/// @title Keys
|
|
5
|
-
/// @notice
|
|
6
|
-
///
|
|
5
|
+
/// @notice Standard block type selectors for the rootzero block stream protocol.
|
|
6
|
+
/// Standard keys use the first 4 bytes of `keccak256("#name")` by convention.
|
|
7
|
+
/// Custom block keys only need to be unique in the context where they are used;
|
|
8
|
+
/// hosts may publish custom key meanings with the `Schema` event.
|
|
7
9
|
library Keys {
|
|
10
|
+
/// @notice Create a context-local block key.
|
|
11
|
+
/// @dev Local keys are opaque tags for host- or endpoint-specific schemas.
|
|
12
|
+
/// The caller is responsible for choosing values that are unique in the
|
|
13
|
+
/// context where they are used and publishing their meaning with `Schema`.
|
|
14
|
+
/// @param value Opaque local key value.
|
|
15
|
+
/// @return Context-local block key.
|
|
16
|
+
function local(uint32 value) internal pure returns (bytes4) {
|
|
17
|
+
return bytes4(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
/// @dev Empty / unset key.
|
|
9
21
|
bytes4 constant Empty = bytes4(0);
|
|
10
22
|
/// @dev Wildcard key used in discovery when any block stream is accepted.
|
|
@@ -27,8 +39,6 @@ library Keys {
|
|
|
27
39
|
bytes4 constant Fee = bytes4(keccak256("#fee"));
|
|
28
40
|
/// @dev List wrapper; payload is an embedded repeated block stream
|
|
29
41
|
bytes4 constant List = bytes4(keccak256("#list"));
|
|
30
|
-
/// @dev Extensible data field; layout is schema-defined
|
|
31
|
-
bytes4 constant Data = bytes4(keccak256("#data"));
|
|
32
42
|
/// @dev EVM-encoded payload field; layout follows standard ABI tuple encoding
|
|
33
43
|
bytes4 constant Evm = bytes4(keccak256("#evm"));
|
|
34
44
|
/// @dev Reserved raw bytes child block.
|
package/blocks/Schema.sol
CHANGED
|
@@ -7,31 +7,36 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
// - payload layout is block-specific
|
|
8
8
|
//
|
|
9
9
|
// Schema:
|
|
10
|
-
// -
|
|
11
|
-
// -
|
|
10
|
+
// - block aliases are published separately from payload schemas
|
|
11
|
+
// - payload schemas are written as `{ fields }`
|
|
12
|
+
// - an empty schema string means the block has no structured payload
|
|
12
13
|
// - commas separate siblings at every level
|
|
13
|
-
// - braces define
|
|
14
|
+
// - braces define the current block payload body
|
|
14
15
|
// - command requests are a single run when the request schema is non-empty
|
|
15
16
|
// - command state is a single active state run without trailing globals
|
|
16
17
|
// - run items may repeat at top level for batching
|
|
17
|
-
// - `maybe #x
|
|
18
|
-
// - `many #x
|
|
18
|
+
// - `maybe #x` marks an optional block item
|
|
19
|
+
// - `many #x` emits one generic list block containing repeated `#x` items
|
|
20
|
+
// - endpoint descriptor lanes are `[key bytes4][item bytes4]`; normal keys widen to `[key][0]`
|
|
21
|
+
// - descriptor lanes for `many #x` use `[Keys.List][keyOfX]`; bare `[Keys.List][0]`
|
|
22
|
+
// is incomplete discovery metadata and should be rejected by tooling
|
|
19
23
|
// - `portal` fields are routing identifiers, often destination host IDs
|
|
20
24
|
// - `resources` fields are chain-specific resource words. A portal adapter
|
|
21
25
|
// interprets them for the destination runtime. EVM resources use the low
|
|
22
26
|
// 128 bits as native value.
|
|
23
27
|
// - dotted field names and aliases, e.g. `dst.portal` or `#bytes as dst.payload`,
|
|
24
28
|
// are offchain projection metadata only and do not change runtime encoding
|
|
25
|
-
// -
|
|
26
|
-
//
|
|
29
|
+
// - child blocks resolve by alias in the active schema context; unresolved aliases are invalid
|
|
30
|
+
// - schema strings describe the payload body only; the `Block` event carries the alias
|
|
27
31
|
// - fixed fields are packed in declaration order
|
|
28
32
|
// - blocks have fixed fields followed by a dynamic child-block tail
|
|
29
33
|
// - child block tails are embedded directly, without an extra stream wrapper
|
|
30
34
|
// - `#bytes` is a reserved child block that stores raw bytes and has no body
|
|
31
35
|
// - `#string` is a reserved child block that stores UTF-8 string bytes and has no body
|
|
32
|
-
// - generic `#data` uses the stable key derived from `#data`
|
|
33
36
|
// - generic lists use the stable key derived from `#list`
|
|
34
|
-
// - keys are derived from block
|
|
37
|
+
// - standard keys are derived from block aliases, e.g. bytes4(keccak256("#amount"))
|
|
38
|
+
// - custom keys are opaque bytes4 tags and only need to be unique in their
|
|
39
|
+
// active context; use `Schema(host, key, schema, name)` to publish their meaning
|
|
35
40
|
// - see `docs/Schema.md` for the full working spec
|
|
36
41
|
//
|
|
37
42
|
// Pipeline state:
|
|
@@ -57,48 +62,48 @@ pragma solidity ^0.8.33;
|
|
|
57
62
|
/// @title Schemas
|
|
58
63
|
/// @notice Human-readable schema string constants for each block type.
|
|
59
64
|
/// These strings describe payload layout for discovery events and docs; block
|
|
60
|
-
/// keys
|
|
65
|
+
/// aliases map to standard keys by convention. Custom blocks may use any unique
|
|
66
|
+
/// bytes4 key in their active context.
|
|
61
67
|
library Schemas {
|
|
62
|
-
string constant Unit = "
|
|
63
|
-
string constant Node = "
|
|
64
|
-
string constant Account = "
|
|
65
|
-
string constant Asset = "
|
|
66
|
-
string constant Amount = "
|
|
67
|
-
string constant Balance = "
|
|
68
|
-
string constant BalanceLimit = "
|
|
69
|
-
string constant Custody = "
|
|
70
|
-
string constant CustodyLimit = "
|
|
71
|
-
string constant Allocation = "
|
|
72
|
-
string constant Allowance = "
|
|
73
|
-
string constant Transaction = "
|
|
74
|
-
string constant Context = "
|
|
75
|
-
string constant Recover = "
|
|
76
|
-
string constant Call = "
|
|
77
|
-
string constant Step = "
|
|
78
|
-
string constant Relay = "
|
|
79
|
-
string constant Dispatch = "
|
|
80
|
-
string constant Bounty = "
|
|
81
|
-
string constant Fee = "
|
|
82
|
-
string constant Auth = "
|
|
83
|
-
string constant Label = "
|
|
84
|
-
string constant Bytes = "
|
|
85
|
-
string constant String = "
|
|
86
|
-
string constant
|
|
87
|
-
string constant
|
|
88
|
-
string constant Evm = "#evm";
|
|
68
|
+
string constant Unit = "";
|
|
69
|
+
string constant Node = "{ uint id }";
|
|
70
|
+
string constant Account = "{ bytes32 account }";
|
|
71
|
+
string constant Asset = "{ bytes32 asset }";
|
|
72
|
+
string constant Amount = "{ bytes32 asset, uint amount }";
|
|
73
|
+
string constant Balance = "{ bytes32 asset, uint amount }";
|
|
74
|
+
string constant BalanceLimit = "{ bytes32 asset, uint min, uint max }";
|
|
75
|
+
string constant Custody = "{ uint host, bytes32 asset, uint amount }";
|
|
76
|
+
string constant CustodyLimit = "{ uint host, bytes32 asset, uint min, uint max }";
|
|
77
|
+
string constant Allocation = "{ uint host, bytes32 asset, uint amount }";
|
|
78
|
+
string constant Allowance = "{ uint host, bytes32 asset, uint amount }";
|
|
79
|
+
string constant Transaction = "{ bytes32 from, bytes32 to, bytes32 asset, uint amount }";
|
|
80
|
+
string constant Context = "{ bytes32 account, #bytes as state, #bytes as request }";
|
|
81
|
+
string constant Recover = "{ uint handler, uint resources, bytes32 key, #bytes as witness }";
|
|
82
|
+
string constant Call = "{ uint target, uint resources, #bytes as payload }";
|
|
83
|
+
string constant Step = "{ uint target, uint resources, #bytes as request }";
|
|
84
|
+
string constant Relay = "{ uint portal, uint resources, #bytes as request }";
|
|
85
|
+
string constant Dispatch = "{ uint portal, uint resources, #bytes as payload }";
|
|
86
|
+
string constant Bounty = "{ uint amount, bytes32 relayer }";
|
|
87
|
+
string constant Fee = "{ uint amount }";
|
|
88
|
+
string constant Auth = "{ uint cid, uint deadline, #bytes as proof }";
|
|
89
|
+
string constant Label = "{ uint id, bytes32 namespace, #string as name }";
|
|
90
|
+
string constant Bytes = "";
|
|
91
|
+
string constant String = "";
|
|
92
|
+
string constant List = "";
|
|
93
|
+
string constant Evm = "";
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/// @title Forms
|
|
92
97
|
/// @notice Reusable structural block schemas for core tuple shapes.
|
|
93
98
|
/// These describe payload form without assigning command or query semantics.
|
|
94
99
|
library Forms {
|
|
95
|
-
string constant Status = "
|
|
96
|
-
string constant AssetAmount = "
|
|
97
|
-
string constant AccountAsset = "
|
|
98
|
-
string constant AccountAmount = "
|
|
99
|
-
string constant HostAmount = "
|
|
100
|
-
string constant HostAccountAsset = "
|
|
101
|
-
string constant HostAccountAmount = "
|
|
100
|
+
string constant Status = "{ uint code }";
|
|
101
|
+
string constant AssetAmount = "{ bytes32 asset, uint amount }";
|
|
102
|
+
string constant AccountAsset = "{ bytes32 account, bytes32 asset }";
|
|
103
|
+
string constant AccountAmount = "{ bytes32 account, bytes32 asset, uint amount }";
|
|
104
|
+
string constant HostAmount = "{ uint host, bytes32 asset, uint amount }";
|
|
105
|
+
string constant HostAccountAsset = "{ uint host, bytes32 account, bytes32 asset }";
|
|
106
|
+
string constant HostAccountAmount = "{ uint host, bytes32 account, bytes32 asset, uint amount }";
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/// @title Sizes
|