@rootzero/contracts 1.11.0 → 1.13.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 +52 -0
- package/Core.sol +2 -1
- package/Cursors.sol +2 -2
- package/Endpoints.sol +5 -2
- package/README.md +40 -17
- package/blocks/Cursors.sol +142 -0
- package/blocks/Keys.sol +2 -10
- package/blocks/Schema.sol +4 -3
- package/blocks/Writers.sol +17 -9
- package/commands/Allocate.sol +49 -0
- package/commands/Burn.sol +3 -2
- package/commands/Credit.sol +6 -18
- package/commands/Debit.sol +6 -18
- package/commands/Deposit.sol +6 -5
- package/commands/Payout.sol +3 -2
- package/commands/Provision.sol +6 -5
- package/commands/Recover.sol +3 -3
- package/commands/Relay.sol +3 -3
- package/commands/Withdraw.sol +3 -2
- package/commands/admin/AllowAssets.sol +3 -2
- package/commands/admin/Allowance.sol +3 -2
- package/commands/admin/Appoint.sol +3 -2
- package/commands/admin/Authorize.sol +7 -12
- package/commands/admin/DenyAssets.sol +3 -2
- package/commands/admin/Dismiss.sol +3 -2
- package/commands/admin/Execute.sol +3 -2
- package/commands/admin/Label.sol +3 -2
- package/commands/admin/Schemas.sol +32 -0
- package/commands/admin/Unauthorize.sol +7 -12
- package/core/Calls.sol +4 -3
- package/core/Endpoint.sol +31 -22
- package/core/Host.sol +11 -3
- package/core/Payable.sol +19 -19
- package/core/Pipeline.sol +17 -14
- package/core/Settlement.sol +39 -0
- package/docs/Schema.md +17 -11
- package/events/Introduction.sol +4 -3
- package/package.json +1 -1
- package/ports/Credit.sol +1 -1
- package/ports/Debit.sol +1 -1
- package/ports/Settle.sol +3 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,58 @@
|
|
|
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.13.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Replaced the virtual `Pipeline.settle(bytes transactions)` stream hook with
|
|
11
|
+
decoded settlement through `Settlement`. Pipeline implementations now provide
|
|
12
|
+
`debitAccount` and `creditAccount` hooks, while the pipeline decodes each
|
|
13
|
+
returned TRANSACTION block and settles it before dispatching the next step.
|
|
14
|
+
- Moved `DebitAccountHook` and `CreditAccountHook` from their command modules to
|
|
15
|
+
`core/Settlement.sol`. They remain available from the `Core.sol` and
|
|
16
|
+
`Endpoints.sol` package entry points.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Added the memory-backed `Reader` and `Readers` block-stream API with balance
|
|
21
|
+
and transaction unpackers, bounds and block validation, and positive
|
|
22
|
+
iteration through `more()`.
|
|
23
|
+
- Added the shared `Settlement` core mixin used by pipelines and `PortSettle` to
|
|
24
|
+
debit transaction sources and credit destinations.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Unified pipeline and port transaction handling through `Settlement`,
|
|
29
|
+
including zero-account handling and zero-amount no-op settlement.
|
|
30
|
+
|
|
31
|
+
## 1.12.0
|
|
32
|
+
|
|
33
|
+
### Breaking Changes
|
|
34
|
+
|
|
35
|
+
- Removed `Keys.Local` and replaced `EndpointBase.localSchema(...)` with the
|
|
36
|
+
explicit `EndpointBase.schema(uint32 key, string body)` helper for
|
|
37
|
+
context-local endpoint schemas.
|
|
38
|
+
- Changed `Introduction` to emit the receiving `host` and introduced `peer`:
|
|
39
|
+
`Introduction(uint indexed host, uint peer, uint blocknum)`.
|
|
40
|
+
- Replaced `isDebitAccount`, `isCreditAccount`, `isAuthorize`, and
|
|
41
|
+
`isUnauthorize` helper predicates with internal command ID fields:
|
|
42
|
+
`debitAccountId`, `creditAccountId`, `authorizeId`, and `unauthorizeId`.
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- Added the `EndpointBase.schema(...)` helper for publishing local endpoint
|
|
47
|
+
schemas.
|
|
48
|
+
- Added the standard `#schema` block, `unpackSchema`, and an opt-in
|
|
49
|
+
`publishSchema` admin command for emitting schema claims from `#schema`
|
|
50
|
+
blocks.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Updated the schema DSL documentation to allow any number of child blocks in
|
|
55
|
+
declaration order, including fixed fields before, after, or between child
|
|
56
|
+
blocks.
|
|
57
|
+
|
|
6
58
|
## 1.11.0
|
|
7
59
|
|
|
8
60
|
### Breaking Changes
|
package/Core.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 the core host, runtime, access, ledger, node-call, and validation layer.
|
|
4
|
+
// Aggregator: re-exports the core host, runtime, access, ledger, settlement, pipeline, node-call, and validation layer.
|
|
5
5
|
// Import this file to bring the full rootzero host base layer into scope.
|
|
6
6
|
|
|
7
7
|
import { AccessControl } from "./core/Access.sol";
|
|
@@ -13,6 +13,7 @@ import { CommandCalls, FailedCall, NodeCalls, PortCalls } from "./core/Calls.sol
|
|
|
13
13
|
import { EndpointBase, Lane } from "./core/Endpoint.sol";
|
|
14
14
|
import { Payable } from "./core/Payable.sol";
|
|
15
15
|
import { Pipeline } from "./core/Pipeline.sol";
|
|
16
|
+
import { CreditAccountHook, DebitAccountHook, Settlement } from "./core/Settlement.sol";
|
|
16
17
|
import { Portal } from "./core/Portal.sol";
|
|
17
18
|
import { RecoverHook } from "./commands/Recover.sol";
|
|
18
19
|
import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
|
package/Cursors.sol
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
// Aggregator: re-exports all block stream primitives (Cursors, Writers, Schema, Keys, Sizes).
|
|
4
|
+
// Aggregator: re-exports all block stream primitives (Cursors, Readers, Writers, Schema, Keys, Sizes).
|
|
5
5
|
// Import this file to get access to the full block encoding/decoding surface in one import.
|
|
6
6
|
|
|
7
7
|
import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
|
|
8
8
|
import { Forms, Sizes } from "./blocks/Schema.sol";
|
|
9
9
|
import { Keys } from "./blocks/Keys.sol";
|
|
10
10
|
import { Schemas } from "./blocks/Schema.sol";
|
|
11
|
-
import { Cursors, Cur } from "./blocks/Cursors.sol";
|
|
11
|
+
import { Cursors, Cur, Readers, Reader } from "./blocks/Cursors.sol";
|
|
12
12
|
import { Writer, Writers, Hints } from "./blocks/Writers.sol";
|
|
13
13
|
|
|
14
14
|
|
package/Endpoints.sol
CHANGED
|
@@ -9,11 +9,13 @@ import { Keys } from "./blocks/Keys.sol";
|
|
|
9
9
|
import { CommandBase, CommandContext } from "./commands/Base.sol";
|
|
10
10
|
import { EndpointBase, Lane } from "./core/Endpoint.sol";
|
|
11
11
|
import { Payable } from "./core/Payable.sol";
|
|
12
|
+
import { CreditAccountHook, DebitAccountHook } from "./core/Settlement.sol";
|
|
12
13
|
|
|
13
14
|
// Commands
|
|
15
|
+
import { Allocate, AllocateHook } from "./commands/Allocate.sol";
|
|
14
16
|
import { Burn, BurnHook } from "./commands/Burn.sol";
|
|
15
|
-
import { CreditAccount
|
|
16
|
-
import { DebitAccount
|
|
17
|
+
import { CreditAccount } from "./commands/Credit.sol";
|
|
18
|
+
import { DebitAccount } from "./commands/Debit.sol";
|
|
17
19
|
import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
|
|
18
20
|
import { Payout, PayoutHook } from "./commands/Payout.sol";
|
|
19
21
|
import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
|
|
@@ -31,6 +33,7 @@ import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
|
|
|
31
33
|
import { Dismiss } from "./commands/admin/Dismiss.sol";
|
|
32
34
|
import { ExecutePayable } from "./commands/admin/Execute.sol";
|
|
33
35
|
import { Label } from "./commands/admin/Label.sol";
|
|
36
|
+
import { PublishSchema } from "./commands/admin/Schemas.sol";
|
|
34
37
|
import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
35
38
|
|
|
36
39
|
// Port endpoints
|
package/README.md
CHANGED
|
@@ -89,8 +89,8 @@ request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
|
|
|
89
89
|
port would parse; what differs per chain is how a host *resolves* the
|
|
90
90
|
identifiers inside, never how the bytes are laid out.
|
|
91
91
|
|
|
92
|
-
Schemas can express more than flat fields: a block may
|
|
93
|
-
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
|
|
94
94
|
marked `maybe` (optional) or `many` (a list), and aliases and dotted field
|
|
95
95
|
paths give off-chain tooling presentation names without changing a single byte
|
|
96
96
|
on the wire. The full schema language is specified in
|
|
@@ -121,7 +121,8 @@ const request = concat([
|
|
|
121
121
|
encodeAmountBlock(usdc, 250_000_000n),
|
|
122
122
|
encodeAmountBlock(dai, 250n * 10n ** 18n),
|
|
123
123
|
]);
|
|
124
|
-
// deposit(request) returns two #balance blocks
|
|
124
|
+
// deposit(request) returns two #balance blocks in its state output and an
|
|
125
|
+
// empty transaction output
|
|
125
126
|
```
|
|
126
127
|
|
|
127
128
|
Everything downstream keeps this shape: commands loop over request blocks,
|
|
@@ -216,6 +217,11 @@ struct CommandContext {
|
|
|
216
217
|
}
|
|
217
218
|
```
|
|
218
219
|
|
|
220
|
+
Every command returns two block streams: `state`, which is threaded into the
|
|
221
|
+
next pipeline step, and `transactions`, which contains `#transaction` blocks
|
|
222
|
+
for the pipeline host to settle outside the state lane. Either stream may be
|
|
223
|
+
empty.
|
|
224
|
+
|
|
219
225
|
The input carries instructions; the state carries live value. While a
|
|
220
226
|
sequence of commands executes, `#balance` and `#custody` blocks in the state
|
|
221
227
|
are the funds being moved — produced by one command, consumed by the next.
|
|
@@ -224,7 +230,9 @@ The standard `Deposit` mixin shows the canonical shape — open the input,
|
|
|
224
230
|
loop the batch, call the hook, write the output run:
|
|
225
231
|
|
|
226
232
|
```solidity
|
|
227
|
-
function deposit(
|
|
233
|
+
function deposit(
|
|
234
|
+
CommandContext calldata c
|
|
235
|
+
) external onlyCommand returns (bytes memory, bytes memory) {
|
|
228
236
|
(Cur memory input, uint outputs) = openInput(c.input, descriptor);
|
|
229
237
|
Writer memory output = Writers.allocBalances(outputs);
|
|
230
238
|
|
|
@@ -234,7 +242,7 @@ function deposit(CommandContext calldata c) external onlyCommand returns (bytes
|
|
|
234
242
|
output.appendBalance(asset, amount);
|
|
235
243
|
}
|
|
236
244
|
|
|
237
|
-
return output.finish();
|
|
245
|
+
return (output.finish(), "");
|
|
238
246
|
}
|
|
239
247
|
```
|
|
240
248
|
|
|
@@ -250,8 +258,10 @@ abstract contract MyCommand is CommandBase {
|
|
|
250
258
|
(, descriptor) = command("myCommand", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
|
|
251
259
|
}
|
|
252
260
|
|
|
253
|
-
function myCommand(
|
|
254
|
-
|
|
261
|
+
function myCommand(
|
|
262
|
+
CommandContext calldata c
|
|
263
|
+
) external onlyCommand returns (bytes memory, bytes memory) {
|
|
264
|
+
// parse c.input, loop, return the output state run and any transactions
|
|
255
265
|
}
|
|
256
266
|
}
|
|
257
267
|
```
|
|
@@ -259,8 +269,9 @@ abstract contract MyCommand is CommandBase {
|
|
|
259
269
|
The standard commands cover the common ledger movements: `deposit` and
|
|
260
270
|
`depositPayable` (external funds in), `withdraw` and `burn` (funds out),
|
|
261
271
|
`debitAccount` and `creditAccount` (internal movements), `payout` (deliver
|
|
262
|
-
state to other accounts), `
|
|
263
|
-
`
|
|
272
|
+
state to other accounts), `allocate` (turn balance state into custody),
|
|
273
|
+
`provision` (provision custody from an external allocation), and `relayPayable`
|
|
274
|
+
(hand a pipeline to another portal).
|
|
264
275
|
|
|
265
276
|
## Pipelines
|
|
266
277
|
|
|
@@ -272,14 +283,26 @@ step { uint target, uint resources, #bytes as request }
|
|
|
272
283
|
```
|
|
273
284
|
|
|
274
285
|
Each step names a target command, the resources it may spend, and its request.
|
|
275
|
-
The state threads
|
|
276
|
-
|
|
277
|
-
|
|
286
|
+
The returned state threads into the next command and the final state must be
|
|
287
|
+
empty. Returned transactions do not enter the state lane; the pipeline passes
|
|
288
|
+
each decoded transaction to the shared settlement implementation before
|
|
289
|
+
running the next step. This is the core of `Pipeline.pipe`:
|
|
278
290
|
|
|
279
291
|
```solidity
|
|
280
292
|
while (input.i < input.len) {
|
|
281
293
|
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
|
282
|
-
|
|
294
|
+
Reader memory transactions;
|
|
295
|
+
(state, transactions.source) = dispatch(
|
|
296
|
+
target,
|
|
297
|
+
account,
|
|
298
|
+
state,
|
|
299
|
+
request,
|
|
300
|
+
useValue(budget, resources)
|
|
301
|
+
);
|
|
302
|
+
while (transactions.more()) {
|
|
303
|
+
(bytes32 from, bytes32 to, bytes32 asset, uint amount) = transactions.unpackTransaction();
|
|
304
|
+
settle(from, to, asset, amount);
|
|
305
|
+
}
|
|
283
306
|
}
|
|
284
307
|
if (state.length != 0) revert UnexpectedState();
|
|
285
308
|
```
|
|
@@ -332,7 +355,7 @@ bytes and produce the same output bytes for every endpoint.
|
|
|
332
355
|
|
|
333
356
|
Admin commands use the regular command shape but are gated to the host's admin
|
|
334
357
|
account: trust management (`authorize`, `unauthorize`), guardian management
|
|
335
|
-
(`appoint`, `dismiss`),
|
|
358
|
+
(`appoint`, `dismiss`), metadata (`label`, `publishSchema`), asset gating (`allowAssets`,
|
|
336
359
|
`denyAssets`, `allowance`), lifecycle (`init`, `destroy`), and raw calls
|
|
337
360
|
(`executePayable`). Guards go the other way: direct actions guardians can take
|
|
338
361
|
without any command context — the default is `revoke`, which lets a guardian
|
|
@@ -353,11 +376,11 @@ names, access sets, balances — from logs alone, with no artifact files.
|
|
|
353
376
|
Import from the package entry points rather than deep paths:
|
|
354
377
|
|
|
355
378
|
- `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
|
|
356
|
-
`Pipeline`, `Portal`, validator
|
|
379
|
+
`Settlement`, `Pipeline`, `Portal`, validator
|
|
357
380
|
- `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
|
|
358
381
|
mixins and their hooks
|
|
359
|
-
- `@rootzero/contracts/Cursors.sol` — `Cur
|
|
360
|
-
`Keys`
|
|
382
|
+
- `@rootzero/contracts/Cursors.sol` — calldata `Cur`/`Cursors`, memory
|
|
383
|
+
`Reader`/`Readers`, `Writers`, `Schemas`, `Keys`
|
|
361
384
|
- `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
|
|
362
385
|
layout and value helpers
|
|
363
386
|
- `@rootzero/contracts/Events.sol` — protocol event contracts
|
package/blocks/Cursors.sol
CHANGED
|
@@ -17,7 +17,109 @@ struct Cur {
|
|
|
17
17
|
uint len;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/// @notice Mutable reader over a block stream stored in memory.
|
|
21
|
+
/// All positions (`i`) are byte offsets relative to the start of `source`.
|
|
22
|
+
struct Reader {
|
|
23
|
+
/// @dev Current read position, relative to the source start.
|
|
24
|
+
uint i;
|
|
25
|
+
/// @dev Memory bytes containing the complete source region.
|
|
26
|
+
bytes source;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
using Cursors for Cur;
|
|
30
|
+
using Readers for Reader;
|
|
31
|
+
|
|
32
|
+
/// @title Readers
|
|
33
|
+
/// @notice Memory block stream parser for the rootzero protocol.
|
|
34
|
+
/// A `Reader` advances through an existing `bytes memory` source without copying its contents.
|
|
35
|
+
/// Blocks are encoded as `[bytes4 key][bytes4 payloadLen][payload]`.
|
|
36
|
+
library Readers {
|
|
37
|
+
/// @dev The current block has a truncated header or payload, an unexpected key,
|
|
38
|
+
/// or a payload size outside the accepted range.
|
|
39
|
+
error InvalidBlock();
|
|
40
|
+
|
|
41
|
+
/// @notice Create a reader backed by a memory byte array.
|
|
42
|
+
/// @param source Memory bytes containing the block stream.
|
|
43
|
+
/// @return cur Reader positioned at the beginning of `source`.
|
|
44
|
+
function open(bytes memory source) internal pure returns (Reader memory cur) {
|
|
45
|
+
cur.source = source;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @notice Return whether the reader has consumed its entire source.
|
|
49
|
+
/// @param cur Reader whose position should be checked.
|
|
50
|
+
/// @return Whether `cur.i` equals the source length.
|
|
51
|
+
function done(Reader memory cur) internal pure returns (bool) {
|
|
52
|
+
return cur.i == cur.source.length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// @notice Return whether the reader has bytes left to consume.
|
|
56
|
+
/// @param cur Reader whose position should be checked.
|
|
57
|
+
/// @return Whether `cur.i` differs from the source length.
|
|
58
|
+
function more(Reader memory cur) internal pure returns (bool) {
|
|
59
|
+
return cur.i != cur.source.length;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// @notice Validate and consume the current block, advancing `cur.i` past it.
|
|
63
|
+
/// @param cur Reader to advance.
|
|
64
|
+
/// @param key Expected block key.
|
|
65
|
+
/// @param min Minimum payload length.
|
|
66
|
+
/// @param max Maximum payload length; zero means unbounded.
|
|
67
|
+
/// @return abs Absolute memory address of the payload start.
|
|
68
|
+
function consume(
|
|
69
|
+
Reader memory cur,
|
|
70
|
+
bytes4 key,
|
|
71
|
+
uint min,
|
|
72
|
+
uint max
|
|
73
|
+
) internal pure returns (uint abs) {
|
|
74
|
+
bytes memory source = cur.source;
|
|
75
|
+
uint i = cur.i;
|
|
76
|
+
|
|
77
|
+
if (i > source.length || source.length - i < Sizes.Header) revert InvalidBlock();
|
|
78
|
+
|
|
79
|
+
bytes4 current;
|
|
80
|
+
uint len;
|
|
81
|
+
assembly ("memory-safe") {
|
|
82
|
+
let header := mload(add(add(source, 0x20), i))
|
|
83
|
+
current := header
|
|
84
|
+
len := and(shr(192, header), 0xffffffff)
|
|
85
|
+
abs := add(add(source, 0x28), i)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (current != key || len < min || (max != 0 && len > max)) revert InvalidBlock();
|
|
89
|
+
if (len > source.length - i - Sizes.Header) revert InvalidBlock();
|
|
90
|
+
cur.i = i + Sizes.Header + len;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// @notice Consume a BALANCE block and return its fields.
|
|
94
|
+
/// @param cur Reader; advanced past the block.
|
|
95
|
+
/// @return asset Asset identifier.
|
|
96
|
+
/// @return amount Token amount.
|
|
97
|
+
function unpackBalance(Reader memory cur) internal pure returns (bytes32 asset, uint amount) {
|
|
98
|
+
uint abs = consume(cur, Keys.Balance, 64, 64);
|
|
99
|
+
assembly ("memory-safe") {
|
|
100
|
+
asset := mload(abs)
|
|
101
|
+
amount := mload(add(abs, 0x20))
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// @notice Consume a TRANSACTION block and return its fields.
|
|
106
|
+
/// @param cur Reader; advanced past the block.
|
|
107
|
+
/// @return from Source account identifier.
|
|
108
|
+
/// @return to Destination account identifier.
|
|
109
|
+
/// @return asset Asset identifier.
|
|
110
|
+
/// @return amount Token amount.
|
|
111
|
+
function unpackTransaction(
|
|
112
|
+
Reader memory cur
|
|
113
|
+
) internal pure returns (bytes32 from, bytes32 to, bytes32 asset, uint amount) {
|
|
114
|
+
uint abs = consume(cur, Keys.Transaction, 128, 128);
|
|
115
|
+
assembly ("memory-safe") {
|
|
116
|
+
from := mload(abs)
|
|
117
|
+
to := mload(add(abs, 0x20))
|
|
118
|
+
asset := mload(add(abs, 0x40))
|
|
119
|
+
amount := mload(add(abs, 0x60))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
21
123
|
|
|
22
124
|
/// @title Cursors
|
|
23
125
|
/// @notice Calldata block stream parser for the rootzero protocol.
|
|
@@ -493,6 +595,21 @@ library Cursors {
|
|
|
493
595
|
return createBlock96(Keys.Custody, bytes32(host), asset, bytes32(amount));
|
|
494
596
|
}
|
|
495
597
|
|
|
598
|
+
/// @notice Encode a TRANSACTION block.
|
|
599
|
+
/// @param from Source account identifier.
|
|
600
|
+
/// @param to Destination account identifier.
|
|
601
|
+
/// @param asset Asset identifier.
|
|
602
|
+
/// @param amount Transfer amount.
|
|
603
|
+
/// @return Encoded TRANSACTION block bytes.
|
|
604
|
+
function toTransactionBlock(
|
|
605
|
+
bytes32 from,
|
|
606
|
+
bytes32 to,
|
|
607
|
+
bytes32 asset,
|
|
608
|
+
uint amount
|
|
609
|
+
) internal pure returns (bytes memory) {
|
|
610
|
+
return createBlock128(Keys.Transaction, from, to, asset, bytes32(amount));
|
|
611
|
+
}
|
|
612
|
+
|
|
496
613
|
/// @notice Encode a STEP block.
|
|
497
614
|
/// @param target Command target identifier.
|
|
498
615
|
/// @param resources Packed resources assigned to the step.
|
|
@@ -726,6 +843,19 @@ library Cursors {
|
|
|
726
843
|
cur.ensureAt(end);
|
|
727
844
|
}
|
|
728
845
|
|
|
846
|
+
/// @notice Consume a SCHEMA block and return its fields.
|
|
847
|
+
/// @param cur Cursor; advanced past the SCHEMA block.
|
|
848
|
+
/// @return key Block key being defined.
|
|
849
|
+
/// @return body Schema DSL string describing the block payload body.
|
|
850
|
+
/// @return name Optional block alias.
|
|
851
|
+
function unpackSchema(Cur memory cur) internal pure returns (bytes4 key, string memory body, bytes32 name) {
|
|
852
|
+
uint end = cur.enter(Keys.Schema, 36 + Sizes.Header, 0);
|
|
853
|
+
key = cur.read4();
|
|
854
|
+
body = cur.unpackString();
|
|
855
|
+
name = cur.read32();
|
|
856
|
+
cur.ensureAt(end);
|
|
857
|
+
}
|
|
858
|
+
|
|
729
859
|
/// @notice Consume a dynamic block with a single bytes32 payload.
|
|
730
860
|
/// @param cur Cursor; advanced past the block.
|
|
731
861
|
/// @param key Expected dynamic block key.
|
|
@@ -1326,6 +1456,18 @@ library Cursors {
|
|
|
1326
1456
|
// Transform helpers
|
|
1327
1457
|
// -------------------------------------------------------------------------
|
|
1328
1458
|
|
|
1459
|
+
/// @notice Consume a BALANCE block and scope its amount to a host.
|
|
1460
|
+
/// @param cur Cursor; advanced past the BALANCE block.
|
|
1461
|
+
/// @param host Host node ID to attach to the decoded balance.
|
|
1462
|
+
/// @return value Host-scoped balance amount.
|
|
1463
|
+
function unpackBalanceForHost(
|
|
1464
|
+
Cur memory cur,
|
|
1465
|
+
uint host
|
|
1466
|
+
) internal pure returns (HostAmount memory value) {
|
|
1467
|
+
value.host = host;
|
|
1468
|
+
(value.asset, value.amount) = cur.unpackBalance();
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1329
1471
|
/// @notice Consume a RELAY block and encode its destination context payload.
|
|
1330
1472
|
/// @param cur Cursor; advanced past the RELAY block.
|
|
1331
1473
|
/// @param account Account identifier to embed in the destination context.
|
package/blocks/Keys.sol
CHANGED
|
@@ -7,16 +7,6 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
/// Custom block keys only need to be unique in the context where they are used;
|
|
8
8
|
/// hosts may publish custom key meanings with the `Schema` event.
|
|
9
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
|
-
|
|
20
10
|
/// @dev Empty / unset key.
|
|
21
11
|
bytes4 constant Empty = bytes4(0);
|
|
22
12
|
/// @dev Wildcard key used in discovery when any block stream is accepted.
|
|
@@ -71,6 +61,8 @@ library Keys {
|
|
|
71
61
|
bytes4 constant Bounty = bytes4(keccak256("#bounty"));
|
|
72
62
|
/// @dev Mutable node label - (uint id, bytes32 namespace, #string as name)
|
|
73
63
|
bytes4 constant Label = bytes4(keccak256("#label"));
|
|
64
|
+
/// @dev Block schema publication - (bytes4 key, #string as body, bytes32 name)
|
|
65
|
+
bytes4 constant Schema = bytes4(keccak256("#schema"));
|
|
74
66
|
|
|
75
67
|
/// @dev Structural status form - (uint code)
|
|
76
68
|
bytes4 constant Status = bytes4(keccak256("#status"));
|
package/blocks/Schema.sol
CHANGED
|
@@ -28,9 +28,9 @@ pragma solidity ^0.8.33;
|
|
|
28
28
|
// are offchain projection metadata only and do not change runtime encoding
|
|
29
29
|
// - child blocks resolve by alias in the active schema context; unresolved aliases are invalid
|
|
30
30
|
// - schema strings describe the payload body only; the `Block` event carries the alias
|
|
31
|
-
// -
|
|
32
|
-
// -
|
|
33
|
-
// - child
|
|
31
|
+
// - items are encoded in declaration order
|
|
32
|
+
// - fixed fields are packed inline and any number of child blocks are embedded directly
|
|
33
|
+
// - child blocks may appear between fixed fields because each block carries its own length
|
|
34
34
|
// - `#bytes` is a reserved child block that stores raw bytes and has no body
|
|
35
35
|
// - `#string` is a reserved child block that stores UTF-8 string bytes and has no body
|
|
36
36
|
// - generic lists use the stable key derived from `#list`
|
|
@@ -87,6 +87,7 @@ library Schemas {
|
|
|
87
87
|
string constant Fee = "{ uint amount }";
|
|
88
88
|
string constant Auth = "{ uint cid, uint deadline, #bytes as proof }";
|
|
89
89
|
string constant Label = "{ uint id, bytes32 namespace, #string as name }";
|
|
90
|
+
string constant Schema = "{ bytes4 key, #string as body, bytes32 name }";
|
|
90
91
|
string constant Bytes = "";
|
|
91
92
|
string constant String = "";
|
|
92
93
|
string constant List = "";
|
package/blocks/Writers.sol
CHANGED
|
@@ -993,19 +993,27 @@ library Writers {
|
|
|
993
993
|
appendCustody(writer, value.host, value.asset, value.amount);
|
|
994
994
|
}
|
|
995
995
|
|
|
996
|
+
/// @notice Append a TRANSACTION block using separate field values.
|
|
997
|
+
/// @param writer Destination writer; `i` is advanced by `Sizes.Transaction`.
|
|
998
|
+
/// @param from Source account identifier.
|
|
999
|
+
/// @param to Destination account identifier.
|
|
1000
|
+
/// @param asset Asset identifier.
|
|
1001
|
+
/// @param amount Transfer amount.
|
|
1002
|
+
function appendTransaction(
|
|
1003
|
+
Writer memory writer,
|
|
1004
|
+
bytes32 from,
|
|
1005
|
+
bytes32 to,
|
|
1006
|
+
bytes32 asset,
|
|
1007
|
+
uint amount
|
|
1008
|
+
) internal pure {
|
|
1009
|
+
appendBlock128(writer, Keys.Transaction, from, to, asset, bytes32(amount), 32);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
996
1012
|
/// @notice Append a TRANSACTION block from a struct.
|
|
997
1013
|
/// @param writer Destination writer; `i` is advanced by `Sizes.Transaction`.
|
|
998
1014
|
/// @param value Transfer record fields to encode.
|
|
999
1015
|
function appendTransaction(Writer memory writer, Tx memory value) internal pure {
|
|
1000
|
-
|
|
1001
|
-
writer,
|
|
1002
|
-
Keys.Transaction,
|
|
1003
|
-
bytes32(value.from),
|
|
1004
|
-
bytes32(value.to),
|
|
1005
|
-
value.asset,
|
|
1006
|
-
bytes32(value.amount),
|
|
1007
|
-
32
|
|
1008
|
-
);
|
|
1016
|
+
appendTransaction(writer, value.from, value.to, value.asset, value.amount);
|
|
1009
1017
|
}
|
|
1010
1018
|
|
|
1011
1019
|
// -------------------------------------------------------------------------
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {CommandContext, CommandBase, Keys} from "./Base.sol";
|
|
5
|
+
import {HostAmount, Cursors, Cur, Writer, Writers} from "../Cursors.sol";
|
|
6
|
+
|
|
7
|
+
using Cursors for Cur;
|
|
8
|
+
using Writers for Writer;
|
|
9
|
+
|
|
10
|
+
/// @notice Shared allocation hook used by `Allocate`.
|
|
11
|
+
abstract contract AllocateHook {
|
|
12
|
+
/// @notice Override to allocate a live balance into custody on a host.
|
|
13
|
+
/// Called once per paired BALANCE state block and NODE request block.
|
|
14
|
+
/// Implementations should perform only the custody side effect; output
|
|
15
|
+
/// blocks are written by the caller.
|
|
16
|
+
/// @param account Caller's account identifier.
|
|
17
|
+
/// @param custody Host-scoped amount to allocate into custody.
|
|
18
|
+
function allocate(bytes32 account, HostAmount memory custody) internal virtual;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @title Allocate
|
|
22
|
+
/// @notice Command that allocates BALANCE state to custody on requested hosts.
|
|
23
|
+
/// Each BALANCE state block is paired with one NODE request block at the same
|
|
24
|
+
/// position; the output is a matching CUSTODY state stream.
|
|
25
|
+
abstract contract Allocate is CommandBase, AllocateHook {
|
|
26
|
+
bytes32 private immutable descriptor;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
(, descriptor) = command("allocate", Keys.Balance, Keys.Node, Keys.Custody, 0, false, false);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// @notice Allocate BALANCE state blocks to matching NODE request blocks.
|
|
33
|
+
/// @param c Command context; `c.state` must contain BALANCE blocks and
|
|
34
|
+
/// `c.input` must contain the same number of NODE blocks.
|
|
35
|
+
/// @return CUSTODY block stream matching the allocated balances.
|
|
36
|
+
/// @return Empty transaction stream.
|
|
37
|
+
function allocate(CommandContext calldata c) external onlyCommand returns (bytes memory, bytes memory) {
|
|
38
|
+
(Cur memory input, Cur memory state, uint outputs) = openCommand(c, descriptor);
|
|
39
|
+
Writer memory output = Writers.allocCustodies(outputs);
|
|
40
|
+
|
|
41
|
+
while (state.i < state.len) {
|
|
42
|
+
HostAmount memory custody = state.unpackBalanceForHost(input.unpackNode());
|
|
43
|
+
allocate(c.account, custody);
|
|
44
|
+
output.appendCustody(custody);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (output.finish(), "");
|
|
48
|
+
}
|
|
49
|
+
}
|
package/commands/Burn.sol
CHANGED
|
@@ -28,14 +28,15 @@ abstract contract Burn is CommandBase, BurnHook {
|
|
|
28
28
|
/// @notice Burn each BALANCE block from the command state.
|
|
29
29
|
/// @param c Command context; `c.state` must contain BALANCE blocks.
|
|
30
30
|
/// @return Empty output state.
|
|
31
|
-
|
|
31
|
+
/// @return Empty transaction stream.
|
|
32
|
+
function burn(CommandContext calldata c) external onlyCommand returns (bytes memory, bytes memory) {
|
|
32
33
|
(Cur memory state, ) = openState(c.state, descriptor);
|
|
33
34
|
|
|
34
35
|
while (state.i < state.len) {
|
|
35
36
|
(bytes32 asset, uint amount) = state.unpackBalance();
|
|
36
37
|
burn(c.account, asset, amount);
|
|
37
38
|
}
|
|
38
|
-
return "";
|
|
39
|
+
return ("", "");
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
package/commands/Credit.sol
CHANGED
|
@@ -3,47 +3,35 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { CommandBase, CommandContext, Keys } from "./Base.sol";
|
|
5
5
|
import { Cursors, Cur } from "../Cursors.sol";
|
|
6
|
+
import { CreditAccountHook } from "../core/Settlement.sol";
|
|
6
7
|
|
|
7
8
|
using Cursors for Cur;
|
|
8
9
|
|
|
9
|
-
abstract contract CreditAccountHook {
|
|
10
|
-
/// @notice Override to credit externally managed funds to `account`.
|
|
11
|
-
/// Called once per BALANCE block in state.
|
|
12
|
-
/// @param account Destination account identifier.
|
|
13
|
-
/// @param asset Asset identifier.
|
|
14
|
-
/// @param amount Amount to credit.
|
|
15
|
-
function creditAccount(bytes32 account, bytes32 asset, uint amount) internal virtual;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
10
|
/// @title CreditAccount
|
|
19
11
|
/// @notice Command that delivers BALANCE state blocks to an account via a virtual hook.
|
|
20
12
|
/// Use for internally recording credits that have already been settled externally.
|
|
21
13
|
abstract contract CreditAccount is CommandBase, CreditAccountHook {
|
|
22
14
|
bytes32 private immutable descriptor;
|
|
23
|
-
uint
|
|
15
|
+
uint internal immutable creditAccountId;
|
|
24
16
|
|
|
25
17
|
constructor() {
|
|
26
|
-
(
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/// @notice Return true if `candidate` is this command's credit account ID.
|
|
30
|
-
function isCreditAccount(uint candidate) internal view returns (bool) {
|
|
31
|
-
return candidate == id;
|
|
18
|
+
(creditAccountId, descriptor) = command("creditAccount", Keys.Balance, Keys.Empty, Keys.Empty, 0, false, false);
|
|
32
19
|
}
|
|
33
20
|
|
|
34
21
|
/// @notice Credit each BALANCE block from the command state to the command account.
|
|
35
22
|
/// @param c Command context; `c.state` must contain BALANCE blocks.
|
|
36
23
|
/// @return Empty output state.
|
|
24
|
+
/// @return Empty transaction stream.
|
|
37
25
|
function creditAccount(
|
|
38
26
|
CommandContext calldata c
|
|
39
|
-
) external onlyCommand returns (bytes memory) {
|
|
27
|
+
) external onlyCommand returns (bytes memory, bytes memory) {
|
|
40
28
|
(Cur memory state, ) = openState(c.state, descriptor);
|
|
41
29
|
|
|
42
30
|
while (state.i < state.len) {
|
|
43
31
|
(bytes32 asset, uint amount) = state.unpackBalance();
|
|
44
32
|
creditAccount(c.account, asset, amount);
|
|
45
33
|
}
|
|
46
|
-
return "";
|
|
34
|
+
return ("", "");
|
|
47
35
|
}
|
|
48
36
|
}
|
|
49
37
|
|