@rootzero/contracts 1.9.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 +40 -2
- package/Core.sol +3 -1
- package/Endpoints.sol +3 -6
- package/Events.sol +3 -6
- package/README.md +35 -33
- package/Utils.sol +2 -1
- package/blocks/Cursors.sol +39 -88
- 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 +16 -10
- package/commands/Relay.sol +21 -12
- 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/core/Payable.sol +7 -0
- package/core/Pipeline.sol +1 -1
- package/core/Portal.sol +19 -48
- package/docs/Schema.md +114 -82
- package/events/Endpoint.sol +19 -0
- package/events/Recovered.sol +17 -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 +7 -10
- 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/events/Resolved.sol +0 -17
- package/queries/Positions.sol +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,44 @@
|
|
|
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.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.
|
|
26
|
+
|
|
27
|
+
## 1.10.0
|
|
28
|
+
|
|
29
|
+
### Breaking Changes
|
|
30
|
+
|
|
31
|
+
- Moved `RecoverHook` from `Portal.sol` to `commands/Recover.sol`.
|
|
32
|
+
- Moved `RoutePayableHook` from `Portal.sol` to `commands/Relay.sol`.
|
|
33
|
+
- Renamed Portal helpers to `forward` / `retry`; hosts now explicitly bridge
|
|
34
|
+
the `RecoverPayable` hook to Portal retry behavior.
|
|
35
|
+
- Renamed the generic `Resolved` event to `Recovered`.
|
|
36
|
+
- Portal no longer emits `Recovered` when retrying an undelivered witness.
|
|
37
|
+
- Renamed `Cursors.exit` to `ensureAt` and renamed its position argument to
|
|
38
|
+
`pos`.
|
|
39
|
+
- Removed the redundant `next` return value from both `Cursors.init` overloads;
|
|
40
|
+
callers should use the returned cursor's `len` as the run boundary.
|
|
41
|
+
- Changed `Cursors.list` to require the expected current cursor position as
|
|
42
|
+
`pos` before entering the LIST block.
|
|
43
|
+
|
|
6
44
|
## 1.9.0
|
|
7
45
|
|
|
8
46
|
### Breaking Changes
|
|
@@ -18,10 +56,10 @@ breaking API changes. Breaking changes are called out explicitly.
|
|
|
18
56
|
so routes can be removed by emitting zero status.
|
|
19
57
|
- Added `Undelivered(uint indexed host, bytes32 key, bytes32 digest)` for portal
|
|
20
58
|
messages that could not be delivered to their handler port.
|
|
21
|
-
- Added `
|
|
59
|
+
- Added `Recovered(uint indexed host, bytes32 key)` as the
|
|
22
60
|
matching event for resolved undelivered digests.
|
|
23
61
|
- Removed the generic `Commitments` core mixin and `Commitment` event in favor
|
|
24
|
-
of domain-specific events such as `Undelivered` and `
|
|
62
|
+
of domain-specific events such as `Undelivered` and `Recovered`.
|
|
25
63
|
|
|
26
64
|
## 1.8.0
|
|
27
65
|
|
package/Core.sol
CHANGED
|
@@ -10,9 +10,11 @@ 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
|
-
import { Portal
|
|
16
|
+
import { Portal } from "./core/Portal.sol";
|
|
17
|
+
import { RecoverHook } from "./commands/Recover.sol";
|
|
16
18
|
import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
|
|
17
19
|
import { Validator } from "./core/Validator.sol";
|
|
18
20
|
|
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
|
|
@@ -16,9 +17,8 @@ import { DebitAccount, DebitAccountHook } from "./commands/Debit.sol";
|
|
|
16
17
|
import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
|
|
17
18
|
import { Payout, PayoutHook } from "./commands/Payout.sol";
|
|
18
19
|
import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
|
|
19
|
-
import { RecoverPayable } from "./commands/Recover.sol";
|
|
20
|
-
import { RelayPayable } from "./commands/Relay.sol";
|
|
21
|
-
import { RecoverHook, RoutePayableHook } from "./core/Portal.sol";
|
|
20
|
+
import { RecoverHook, RecoverPayable } from "./commands/Recover.sol";
|
|
21
|
+
import { RelayPayable, RoutePayableHook } from "./commands/Relay.sol";
|
|
22
22
|
import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
|
|
23
23
|
|
|
24
24
|
// Admin commands
|
|
@@ -27,11 +27,9 @@ import { AllowAssets, AllowAssetsHook } from "./commands/admin/AllowAssets.sol";
|
|
|
27
27
|
import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
|
|
28
28
|
import { Appoint } from "./commands/admin/Appoint.sol";
|
|
29
29
|
import { Authorize } from "./commands/admin/Authorize.sol";
|
|
30
|
-
import { Destroy, DestroyHook } from "./commands/admin/Destroy.sol";
|
|
31
30
|
import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
|
|
32
31
|
import { Dismiss } from "./commands/admin/Dismiss.sol";
|
|
33
32
|
import { ExecutePayable } from "./commands/admin/Execute.sol";
|
|
34
|
-
import { Init, InitHook } from "./commands/admin/Init.sol";
|
|
35
33
|
import { Label } from "./commands/admin/Label.sol";
|
|
36
34
|
import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
37
35
|
|
|
@@ -55,4 +53,3 @@ import { Revoke } from "./guards/Revoke.sol";
|
|
|
55
53
|
import { QueryBase } from "./queries/Base.sol";
|
|
56
54
|
import { AssetStatus, AssetStatusHook } from "./queries/Assets.sol";
|
|
57
55
|
import { GetBalances, GetBalancesHook } from "./queries/Balances.sol";
|
|
58
|
-
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
|
-
import {
|
|
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,36 +72,20 @@ 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
|
-
|
|
80
|
-
function init(
|
|
81
|
-
bytes calldata source,
|
|
82
|
-
uint group
|
|
83
|
-
) internal pure returns (Cur memory cur, uint groups, uint next) {
|
|
80
|
+
function init(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint groups) {
|
|
84
81
|
cur = open(source);
|
|
85
|
-
if (
|
|
82
|
+
if (group == 0) {
|
|
83
|
+
if (cur.len != 0) revert IncompleteCursor();
|
|
84
|
+
return (cur, 0);
|
|
85
|
+
}
|
|
86
|
+
if (cur.len == 0) revert ZeroCursor();
|
|
86
87
|
(bytes4 key, ) = cur.peek(cur.i);
|
|
87
88
|
groups = cur.run(key, group);
|
|
88
|
-
next = cur.len;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/// @notice Create a cursor over `source`, restrict it to its first grouped run, and require an exact group count.
|
|
92
|
-
/// @param source Calldata slice that forms the block stream.
|
|
93
|
-
/// @param group Expected block group size (e.g. 1 for single, 2 for paired).
|
|
94
|
-
/// @param expectedGroups Required number of groups in the run.
|
|
95
|
-
/// @return cur Cursor with `len` truncated to the end of the first run in `source`.
|
|
96
|
-
/// @return next Byte offset immediately after the run, relative to `source`.
|
|
97
|
-
function init(
|
|
98
|
-
bytes calldata source,
|
|
99
|
-
uint group,
|
|
100
|
-
uint expectedGroups
|
|
101
|
-
) internal pure returns (Cur memory cur, uint next) {
|
|
102
|
-
uint groups;
|
|
103
|
-
(cur, groups, next) = init(source, group);
|
|
104
|
-
if (groups != expectedGroups) revert BadRatio();
|
|
105
89
|
}
|
|
106
90
|
|
|
107
91
|
/// @notice Move the cursor to an absolute position within the source region.
|
|
@@ -333,14 +317,15 @@ library Cursors {
|
|
|
333
317
|
return find(cur, cur.i, key);
|
|
334
318
|
}
|
|
335
319
|
|
|
336
|
-
/// @notice
|
|
337
|
-
/// Advances `cur.i` past the list
|
|
338
|
-
///
|
|
339
|
-
///
|
|
340
|
-
/// @
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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,22 +363,13 @@ library Cursors {
|
|
|
378
363
|
return cur.isAt(key) ? take(cur, key) : cur.slice(cur.i, cur.i);
|
|
379
364
|
}
|
|
380
365
|
|
|
381
|
-
/// @notice
|
|
382
|
-
///
|
|
383
|
-
///
|
|
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
|
-
/// @notice Exit a nested region at an exact boundary.
|
|
391
|
-
/// Reverts with `IncompleteCursor` if `end` exceeds the cursor region length
|
|
392
|
-
/// or `cur.i != end`.
|
|
366
|
+
/// @notice Ensure the cursor is at an exact position.
|
|
367
|
+
/// Reverts with `IncompleteCursor` if `pos` exceeds the cursor region length
|
|
368
|
+
/// or `cur.i != pos`.
|
|
393
369
|
/// @param cur Cursor to check.
|
|
394
|
-
/// @param
|
|
395
|
-
function
|
|
396
|
-
if (
|
|
370
|
+
/// @param pos Relative byte offset the cursor must be positioned at.
|
|
371
|
+
function ensureAt(Cur memory cur, uint pos) internal pure {
|
|
372
|
+
if (pos > cur.len || cur.i != pos) revert IncompleteCursor();
|
|
397
373
|
}
|
|
398
374
|
|
|
399
375
|
/// @notice Assert that the cursor has consumed its entire source region.
|
|
@@ -747,7 +723,7 @@ library Cursors {
|
|
|
747
723
|
id = cur.readUint();
|
|
748
724
|
namespace = cur.read32();
|
|
749
725
|
name = cur.unpackString();
|
|
750
|
-
cur.
|
|
726
|
+
cur.ensureAt(end);
|
|
751
727
|
}
|
|
752
728
|
|
|
753
729
|
/// @notice Consume a dynamic block with a single bytes32 payload.
|
|
@@ -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
|
|
|
@@ -1107,7 +1072,7 @@ library Cursors {
|
|
|
1107
1072
|
target = uint(cur.read32());
|
|
1108
1073
|
resources = uint(cur.read32());
|
|
1109
1074
|
req = cur.unpackBytes();
|
|
1110
|
-
cur.
|
|
1075
|
+
cur.ensureAt(end);
|
|
1111
1076
|
}
|
|
1112
1077
|
|
|
1113
1078
|
/// @notice Consume a CALL block and return its target invocation fields.
|
|
@@ -1121,7 +1086,7 @@ library Cursors {
|
|
|
1121
1086
|
target = uint(cur.read32());
|
|
1122
1087
|
resources = uint(cur.read32());
|
|
1123
1088
|
data = cur.unpackBytes();
|
|
1124
|
-
cur.
|
|
1089
|
+
cur.ensureAt(end);
|
|
1125
1090
|
}
|
|
1126
1091
|
|
|
1127
1092
|
/// @notice Consume a CONTEXT block and return its command context fields.
|
|
@@ -1137,7 +1102,7 @@ library Cursors {
|
|
|
1137
1102
|
account = cur.read32();
|
|
1138
1103
|
state = cur.unpackBytes();
|
|
1139
1104
|
request = cur.unpackBytes();
|
|
1140
|
-
cur.
|
|
1105
|
+
cur.ensureAt(end);
|
|
1141
1106
|
}
|
|
1142
1107
|
|
|
1143
1108
|
/// @notice Consume a RELAY block and return its destination portal, resources, and request stream.
|
|
@@ -1145,14 +1110,12 @@ 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();
|
|
1154
1117
|
request = cur.unpackBytes();
|
|
1155
|
-
cur.
|
|
1118
|
+
cur.ensureAt(end);
|
|
1156
1119
|
}
|
|
1157
1120
|
|
|
1158
1121
|
/// @notice Consume a DISPATCH block and return its destination portal, resources, and payload.
|
|
@@ -1167,7 +1130,7 @@ library Cursors {
|
|
|
1167
1130
|
portal = cur.readUint();
|
|
1168
1131
|
resources = cur.readUint();
|
|
1169
1132
|
payload = cur.unpackBytes();
|
|
1170
|
-
cur.
|
|
1133
|
+
cur.ensureAt(end);
|
|
1171
1134
|
}
|
|
1172
1135
|
|
|
1173
1136
|
/// @notice Consume a RECOVER block and return its handler, resources, key, and witness bytes.
|
|
@@ -1184,7 +1147,7 @@ library Cursors {
|
|
|
1184
1147
|
resources = cur.readUint();
|
|
1185
1148
|
key = cur.read32();
|
|
1186
1149
|
witness = cur.unpackBytes();
|
|
1187
|
-
cur.
|
|
1150
|
+
cur.ensureAt(end);
|
|
1188
1151
|
}
|
|
1189
1152
|
|
|
1190
1153
|
// Type-specific validators
|
|
@@ -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.
|