@rootzero/contracts 1.10.0 → 1.12.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 +46 -1
- package/Core.sol +1 -0
- package/Endpoints.sol +2 -3
- package/Events.sol +2 -5
- package/README.md +38 -36
- package/Utils.sol +2 -1
- package/blocks/Cursors.sol +39 -75
- package/blocks/Keys.sol +18 -4
- package/blocks/Schema.sol +53 -47
- package/commands/Base.sol +43 -13
- package/commands/Burn.sol +3 -6
- package/commands/Credit.sol +4 -6
- package/commands/Debit.sol +10 -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 +10 -19
- 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/Schemas.sol +31 -0
- package/commands/admin/Unauthorize.sol +10 -19
- package/core/Endpoint.sol +160 -0
- package/core/Host.sol +1 -1
- package/docs/Schema.md +124 -86
- 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,52 @@
|
|
|
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.12.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Removed the generic `EndpointBase.schema(...)` helper. Use
|
|
11
|
+
`localSchema(...)` for context-local endpoint schemas or emit `Schema`
|
|
12
|
+
directly for custom/named keys.
|
|
13
|
+
- Replaced `isDebitAccount`, `isCreditAccount`, `isAuthorize`, and
|
|
14
|
+
`isUnauthorize` helper predicates with internal command ID fields:
|
|
15
|
+
`debitAccountId`, `creditAccountId`, `authorizeId`, and `unauthorizeId`.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Added `Keys.Local` as the default context-local block key and added
|
|
20
|
+
`EndpointBase.localSchema(...)` helpers for publishing local endpoint
|
|
21
|
+
schemas.
|
|
22
|
+
- Added the standard `#schema` block, `unpackSchema`, and an opt-in
|
|
23
|
+
`publishSchema` admin command for emitting schema claims from `#schema`
|
|
24
|
+
blocks.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated the schema DSL documentation to allow any number of child blocks in
|
|
29
|
+
declaration order, including fixed fields before, after, or between child
|
|
30
|
+
blocks.
|
|
31
|
+
|
|
32
|
+
## 1.11.0
|
|
33
|
+
|
|
34
|
+
### Breaking Changes
|
|
35
|
+
|
|
36
|
+
- Replaced the separate command, port, query, guard, and admin discovery events
|
|
37
|
+
with the shared `Endpoint` event and a packed endpoint descriptor. Admin
|
|
38
|
+
commands are now identified by the descriptor admin flag. Default lane groups
|
|
39
|
+
remain encoded as zero and are interpreted as group size one when read; the
|
|
40
|
+
final four descriptor bytes are reserved.
|
|
41
|
+
- Renamed the block discovery event to `Schema` and added a `name` field for
|
|
42
|
+
alias-style schema references.
|
|
43
|
+
- Removed the generic `DATA` block type. Custom input blocks should define
|
|
44
|
+
their own local keys and publish schemas explicitly when they need discovery.
|
|
45
|
+
- Removed the built-in admin `init` and `destroy` commands.
|
|
46
|
+
- Removed the built-in `Positions` query; custom position output should be
|
|
47
|
+
modeled as a custom query.
|
|
48
|
+
- Changed `Cursors.list` to consume the LIST block and return a cursor scoped
|
|
49
|
+
to its payload instead of returning the list's end offset.
|
|
50
|
+
- Renamed the `CommandContext.request` field to `input`. The tuple layout and
|
|
51
|
+
command selectors are unchanged.
|
|
7
52
|
|
|
8
53
|
## 1.10.0
|
|
9
54
|
|
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,12 +27,11 @@ 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";
|
|
34
|
+
import { PublishSchema } from "./commands/admin/Schemas.sol";
|
|
35
35
|
import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
36
36
|
|
|
37
37
|
// Port endpoints
|
|
@@ -54,4 +54,3 @@ import { Revoke } from "./guards/Revoke.sol";
|
|
|
54
54
|
import { QueryBase } from "./queries/Base.sol";
|
|
55
55
|
import { AssetStatus, AssetStatusHook } from "./queries/Assets.sol";
|
|
56
56
|
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
|
|
@@ -88,8 +89,8 @@ request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
|
|
|
88
89
|
port would parse; what differs per chain is how a host *resolves* the
|
|
89
90
|
identifiers inside, never how the bytes are laid out.
|
|
90
91
|
|
|
91
|
-
Schemas can express more than flat fields: a block may
|
|
92
|
-
blocks (`#bytes as payload` names
|
|
92
|
+
Schemas can express more than flat fields: a block may contain any number of
|
|
93
|
+
nested child blocks (`#bytes as payload` names raw dynamic bytes), items can be
|
|
93
94
|
marked `maybe` (optional) or `many` (a list), and aliases and dotted field
|
|
94
95
|
paths give off-chain tooling presentation names without changing a single byte
|
|
95
96
|
on the wire. The full schema language is specified in
|
|
@@ -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
|
|
|
@@ -330,7 +332,7 @@ bytes and produce the same output bytes for every endpoint.
|
|
|
330
332
|
|
|
331
333
|
Admin commands use the regular command shape but are gated to the host's admin
|
|
332
334
|
account: trust management (`authorize`, `unauthorize`), guardian management
|
|
333
|
-
(`appoint`, `dismiss`),
|
|
335
|
+
(`appoint`, `dismiss`), metadata (`label`, `publishSchema`), asset gating (`allowAssets`,
|
|
334
336
|
`denyAssets`, `allowance`), lifecycle (`init`, `destroy`), and raw calls
|
|
335
337
|
(`executePayable`). Guards go the other way: direct actions guardians can take
|
|
336
338
|
without any command context — the default is `revoke`, which lets a guardian
|
|
@@ -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`.
|
|
@@ -750,6 +726,19 @@ library Cursors {
|
|
|
750
726
|
cur.ensureAt(end);
|
|
751
727
|
}
|
|
752
728
|
|
|
729
|
+
/// @notice Consume a SCHEMA block and return its fields.
|
|
730
|
+
/// @param cur Cursor; advanced past the SCHEMA block.
|
|
731
|
+
/// @return key Block key being defined.
|
|
732
|
+
/// @return body Schema DSL string describing the block payload body.
|
|
733
|
+
/// @return name Optional block alias.
|
|
734
|
+
function unpackSchema(Cur memory cur) internal pure returns (bytes4 key, string memory body, bytes32 name) {
|
|
735
|
+
uint end = cur.enter(Keys.Schema, 36 + Sizes.Header, 0);
|
|
736
|
+
key = cur.read4();
|
|
737
|
+
body = cur.unpackString();
|
|
738
|
+
name = cur.read32();
|
|
739
|
+
cur.ensureAt(end);
|
|
740
|
+
}
|
|
741
|
+
|
|
753
742
|
/// @notice Consume a dynamic block with a single bytes32 payload.
|
|
754
743
|
/// @param cur Cursor; advanced past the block.
|
|
755
744
|
/// @param key Expected dynamic block key.
|
|
@@ -825,10 +814,7 @@ library Cursors {
|
|
|
825
814
|
/// @param key Expected block key.
|
|
826
815
|
/// @return asset Asset identifier.
|
|
827
816
|
/// @return amount Scalar amount value.
|
|
828
|
-
function unpackAssetAmount(
|
|
829
|
-
Cur memory cur,
|
|
830
|
-
bytes4 key
|
|
831
|
-
) internal pure returns (bytes32 asset, uint amount) {
|
|
817
|
+
function unpackAssetAmount(Cur memory cur, bytes4 key) internal pure returns (bytes32 asset, uint amount) {
|
|
832
818
|
uint abs = consume(cur, 0, key, 64, 64);
|
|
833
819
|
asset = bytes32(msg.data[abs:abs + 32]);
|
|
834
820
|
amount = uint(bytes32(msg.data[abs + 32:abs + 64]));
|
|
@@ -992,9 +978,7 @@ library Cursors {
|
|
|
992
978
|
/// @return host Host node ID.
|
|
993
979
|
/// @return account Account identifier.
|
|
994
980
|
/// @return asset Asset identifier.
|
|
995
|
-
function unpackHostAccountAsset(
|
|
996
|
-
Cur memory cur
|
|
997
|
-
) internal pure returns (uint host, bytes32 account, bytes32 asset) {
|
|
981
|
+
function unpackHostAccountAsset(Cur memory cur) internal pure returns (uint host, bytes32 account, bytes32 asset) {
|
|
998
982
|
return unpackHostAccountAsset(cur, Keys.HostAccountAsset);
|
|
999
983
|
}
|
|
1000
984
|
|
|
@@ -1010,9 +994,7 @@ library Cursors {
|
|
|
1010
994
|
/// @return account Account identifier.
|
|
1011
995
|
/// @return asset Asset identifier.
|
|
1012
996
|
/// @return amount Token amount.
|
|
1013
|
-
function unpackAccountAmount(
|
|
1014
|
-
Cur memory cur
|
|
1015
|
-
) internal pure returns (bytes32 account, bytes32 asset, uint amount) {
|
|
997
|
+
function unpackAccountAmount(Cur memory cur) internal pure returns (bytes32 account, bytes32 asset, uint amount) {
|
|
1016
998
|
return unpackAccountAmount(cur, Keys.AccountAmount);
|
|
1017
999
|
}
|
|
1018
1000
|
|
|
@@ -1028,9 +1010,7 @@ library Cursors {
|
|
|
1028
1010
|
/// @return host Host node ID.
|
|
1029
1011
|
/// @return asset Asset identifier.
|
|
1030
1012
|
/// @return amount Token amount.
|
|
1031
|
-
function unpackAllocation(
|
|
1032
|
-
Cur memory cur
|
|
1033
|
-
) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1013
|
+
function unpackAllocation(Cur memory cur) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1034
1014
|
return unpackHostAmount(cur, Keys.Allocation);
|
|
1035
1015
|
}
|
|
1036
1016
|
|
|
@@ -1046,9 +1026,7 @@ library Cursors {
|
|
|
1046
1026
|
/// @return host Host node ID.
|
|
1047
1027
|
/// @return asset Asset identifier.
|
|
1048
1028
|
/// @return amount Token amount.
|
|
1049
|
-
function unpackAllowance(
|
|
1050
|
-
Cur memory cur
|
|
1051
|
-
) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1029
|
+
function unpackAllowance(Cur memory cur) internal pure returns (uint host, bytes32 asset, uint amount) {
|
|
1052
1030
|
return unpackHostAmount(cur, Keys.Allowance);
|
|
1053
1031
|
}
|
|
1054
1032
|
|
|
@@ -1145,9 +1123,7 @@ library Cursors {
|
|
|
1145
1123
|
/// @return portal Destination portal identifier, often the destination host ID.
|
|
1146
1124
|
/// @return resources Chain-specific resources for the destination context.
|
|
1147
1125
|
/// @return request Embedded request block stream.
|
|
1148
|
-
function unpackRelay(
|
|
1149
|
-
Cur memory cur
|
|
1150
|
-
) internal pure returns (uint portal, uint resources, bytes calldata request) {
|
|
1126
|
+
function unpackRelay(Cur memory cur) internal pure returns (uint portal, uint resources, bytes calldata request) {
|
|
1151
1127
|
uint end = cur.enter(Keys.Relay, 64 + Sizes.Header, 0);
|
|
1152
1128
|
portal = cur.readUint();
|
|
1153
1129
|
resources = cur.readUint();
|
|
@@ -1214,11 +1190,7 @@ library Cursors {
|
|
|
1214
1190
|
/// @param key Expected block type key.
|
|
1215
1191
|
/// @param asset Expected asset identifier.
|
|
1216
1192
|
/// @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) {
|
|
1193
|
+
function requireAssetAmount(Cur memory cur, bytes4 key, bytes32 asset) internal pure returns (uint amount) {
|
|
1222
1194
|
uint abs = consume(cur, 0, key, 64, 64);
|
|
1223
1195
|
if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
|
|
1224
1196
|
amount = uint(bytes32(msg.data[abs + 32:abs + 64]));
|
|
@@ -1274,12 +1246,7 @@ library Cursors {
|
|
|
1274
1246
|
/// @param key Expected block type key.
|
|
1275
1247
|
/// @param host Expected host node ID.
|
|
1276
1248
|
/// @param asset Expected asset identifier.
|
|
1277
|
-
function requireUnitHostAmount(
|
|
1278
|
-
Cur memory cur,
|
|
1279
|
-
bytes4 key,
|
|
1280
|
-
uint host,
|
|
1281
|
-
bytes32 asset
|
|
1282
|
-
) internal pure {
|
|
1249
|
+
function requireUnitHostAmount(Cur memory cur, bytes4 key, uint host, bytes32 asset) internal pure {
|
|
1283
1250
|
uint abs = consume(cur, 0, key, 96, 96);
|
|
1284
1251
|
if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
|
|
1285
1252
|
if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
|
|
@@ -1326,10 +1293,7 @@ library Cursors {
|
|
|
1326
1293
|
/// @param host Expected host node ID.
|
|
1327
1294
|
/// @return account Account identifier from the block.
|
|
1328
1295
|
/// @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) {
|
|
1296
|
+
function requireHostAccountAsset(Cur memory cur, uint host) internal pure returns (bytes32 account, bytes32 asset) {
|
|
1333
1297
|
return requireHostAccountAsset(cur, Keys.HostAccountAsset, host);
|
|
1334
1298
|
}
|
|
1335
1299
|
|
package/blocks/Keys.sol
CHANGED
|
@@ -2,13 +2,27 @@
|
|
|
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.
|
|
11
23
|
bytes4 constant Any = 0xffffffff;
|
|
24
|
+
/// @dev Default context-local block key for hosts/endpoints that need one custom schema.
|
|
25
|
+
bytes4 constant Local = bytes4(uint32(1));
|
|
12
26
|
/// @dev Input amount - (bytes32 asset, uint amount)
|
|
13
27
|
bytes4 constant Amount = bytes4(keccak256("#amount"));
|
|
14
28
|
/// @dev Ledger balance - (bytes32 asset, uint amount)
|
|
@@ -27,8 +41,6 @@ library Keys {
|
|
|
27
41
|
bytes4 constant Fee = bytes4(keccak256("#fee"));
|
|
28
42
|
/// @dev List wrapper; payload is an embedded repeated block stream
|
|
29
43
|
bytes4 constant List = bytes4(keccak256("#list"));
|
|
30
|
-
/// @dev Extensible data field; layout is schema-defined
|
|
31
|
-
bytes4 constant Data = bytes4(keccak256("#data"));
|
|
32
44
|
/// @dev EVM-encoded payload field; layout follows standard ABI tuple encoding
|
|
33
45
|
bytes4 constant Evm = bytes4(keccak256("#evm"));
|
|
34
46
|
/// @dev Reserved raw bytes child block.
|
|
@@ -61,6 +73,8 @@ library Keys {
|
|
|
61
73
|
bytes4 constant Bounty = bytes4(keccak256("#bounty"));
|
|
62
74
|
/// @dev Mutable node label - (uint id, bytes32 namespace, #string as name)
|
|
63
75
|
bytes4 constant Label = bytes4(keccak256("#label"));
|
|
76
|
+
/// @dev Block schema publication - (bytes4 key, #string as body, bytes32 name)
|
|
77
|
+
bytes4 constant Schema = bytes4(keccak256("#schema"));
|
|
64
78
|
|
|
65
79
|
/// @dev Structural status form - (uint code)
|
|
66
80
|
bytes4 constant Status = bytes4(keccak256("#status"));
|