@rootzero/contracts 0.9.8 → 1.0.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/Core.sol +1 -1
- package/Endpoints.sol +3 -1
- package/Events.sol +2 -0
- package/README.md +19 -18
- package/blocks/Cursors.sol +152 -162
- package/blocks/Keys.sol +10 -6
- package/blocks/Schema.sol +17 -9
- package/blocks/Writers.sol +4 -4
- package/commands/Burn.sol +7 -4
- package/commands/Credit.sol +9 -9
- package/commands/Debit.sol +7 -4
- package/commands/Deposit.sol +14 -8
- package/commands/Payout.sol +49 -0
- package/commands/Provision.sol +14 -8
- package/commands/Relay.sol +58 -0
- package/commands/Withdraw.sol +10 -9
- package/commands/admin/AllowAssets.sol +9 -4
- package/commands/admin/Allowance.sol +7 -4
- package/commands/admin/Appoint.sol +7 -4
- package/commands/admin/Authorize.sol +7 -4
- package/commands/admin/DenyAssets.sol +9 -4
- package/commands/admin/Destroy.sol +5 -3
- package/commands/admin/Dismiss.sol +7 -4
- package/commands/admin/Execute.sol +8 -5
- package/commands/admin/Init.sol +5 -3
- package/commands/admin/Unauthorize.sol +7 -4
- package/core/Host.sol +10 -1
- package/core/Pipeline.sol +4 -3
- package/core/Runtime.sol +3 -36
- package/core/Types.sol +1 -1
- package/docs/Schema.md +0 -1
- package/events/Admin.sol +11 -8
- package/events/Chain.sol +20 -0
- package/events/Command.sol +11 -8
- package/events/Peer.sol +5 -5
- package/events/Query.sol +2 -4
- package/events/Transfer.sol +22 -0
- package/guards/Revoke.sol +3 -3
- package/package.json +1 -1
- package/peer/AllowAssets.sol +5 -3
- package/peer/Allowance.sol +5 -3
- package/peer/BalancePull.sol +5 -3
- package/peer/DenyAssets.sol +5 -3
- package/peer/Dispatch.sol +51 -0
- package/peer/Pipe.sol +7 -5
- package/peer/Settle.sol +13 -8
- package/queries/Assets.sol +3 -3
- package/queries/Balances.sol +3 -3
- package/queries/Positions.sol +3 -3
- package/utils/Accounts.sol +6 -31
- package/utils/Actions.sol +11 -9
- package/utils/Assets.sol +21 -21
- package/utils/Ids.sol +12 -2
- package/utils/Layout.sol +21 -17
- package/utils/Utils.sol +2 -2
- package/commands/Transfer.sol +0 -54
package/Core.sol
CHANGED
|
@@ -5,7 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
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";
|
|
8
|
-
import { Balances } from "./core/Balances.sol";
|
|
8
|
+
import { Balances, InsufficientFunds } from "./core/Balances.sol";
|
|
9
9
|
import { Runtime } from "./core/Runtime.sol";
|
|
10
10
|
import { Host, IHostIntroduction } from "./core/Host.sol";
|
|
11
11
|
import { FailedCall, NodeCalls } from "./core/Calls.sol";
|
package/Endpoints.sol
CHANGED
|
@@ -14,8 +14,9 @@ import { Burn, BurnHook } from "./commands/Burn.sol";
|
|
|
14
14
|
import { CreditAccount, CreditAccountHook } from "./commands/Credit.sol";
|
|
15
15
|
import { DebitAccount, DebitAccountHook } from "./commands/Debit.sol";
|
|
16
16
|
import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
|
|
17
|
+
import { Payout, PayoutHook } from "./commands/Payout.sol";
|
|
17
18
|
import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
|
|
18
|
-
import {
|
|
19
|
+
import { RelayPayable, RelayPayableHook } from "./commands/Relay.sol";
|
|
19
20
|
import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
|
|
20
21
|
|
|
21
22
|
// Admin commands
|
|
@@ -37,6 +38,7 @@ import { PeerAllowance } from "./peer/Allowance.sol";
|
|
|
37
38
|
import { PeerBalancePull, BalancePullHook } from "./peer/BalancePull.sol";
|
|
38
39
|
import { PeerDenyAssets } from "./peer/DenyAssets.sol";
|
|
39
40
|
import { PeerPipePayable } from "./peer/Pipe.sol";
|
|
41
|
+
import { PeerDispatchPayable } from "./peer/Dispatch.sol";
|
|
40
42
|
import { PeerSettle } from "./peer/Settle.sol";
|
|
41
43
|
|
|
42
44
|
// Guard endpoints
|
package/Events.sol
CHANGED
|
@@ -8,6 +8,7 @@ import { AdminEvent } from "./events/Admin.sol";
|
|
|
8
8
|
import { AssetStatusEvent } from "./events/Asset.sol";
|
|
9
9
|
import { Actions } from "./utils/Actions.sol";
|
|
10
10
|
import { BalanceEvent } from "./events/Balance.sol";
|
|
11
|
+
import { ChainEvent } from "./events/Chain.sol";
|
|
11
12
|
import { CommandEvent } from "./events/Command.sol";
|
|
12
13
|
import { PositionEvent } from "./events/Position.sol";
|
|
13
14
|
import { ReceivedEvent } from "./events/Received.sol";
|
|
@@ -21,6 +22,7 @@ import { PeerEvent } from "./events/Peer.sol";
|
|
|
21
22
|
import { QueryEvent } from "./events/Query.sol";
|
|
22
23
|
import { RootedEvent } from "./events/Rooted.sol";
|
|
23
24
|
import { SpentEvent } from "./events/Spent.sol";
|
|
25
|
+
import { TransferEvent } from "./events/Transfer.sol";
|
|
24
26
|
import { UnlockedEvent } from "./events/Unlocked.sol";
|
|
25
27
|
|
|
26
28
|
|
package/README.md
CHANGED
|
@@ -8,11 +8,11 @@ It contains the reusable contracts, utilities, cursor parsers, and encoding help
|
|
|
8
8
|
|
|
9
9
|
Most consumers should start from the package root entry points:
|
|
10
10
|
|
|
11
|
-
- `@rootzero/contracts/Core.sol`
|
|
12
|
-
- `@rootzero/contracts/Endpoints.sol`
|
|
13
|
-
- `@rootzero/contracts/Cursors.sol`
|
|
14
|
-
- `@rootzero/contracts/Utils.sol`
|
|
15
|
-
- `@rootzero/contracts/Events.sol`
|
|
11
|
+
- `@rootzero/contracts/Core.sol` - host, access control, balances, and validator building blocks
|
|
12
|
+
- `@rootzero/contracts/Endpoints.sol` - command, peer, guard, and query base contracts plus standard endpoint mixins
|
|
13
|
+
- `@rootzero/contracts/Cursors.sol` - cursor reader (`Cur`), block schemas, key constants, typed block helpers, and writers
|
|
14
|
+
- `@rootzero/contracts/Utils.sol` - IDs, assets, accounts, layout, and value helpers
|
|
15
|
+
- `@rootzero/contracts/Events.sol` - reusable event emitters and event contracts
|
|
16
16
|
|
|
17
17
|
## Block Wire Format
|
|
18
18
|
|
|
@@ -28,7 +28,7 @@ All request and response data is encoded as a binary block stream. Each block is
|
|
|
28
28
|
|
|
29
29
|
Protocol blocks use schema strings and four-byte keys:
|
|
30
30
|
|
|
31
|
-
- `Schemas` describes semantic protocol blocks such as `#amount`, `#balance`, `#custody`, and `#
|
|
31
|
+
- `Schemas` describes semantic protocol blocks such as `#amount`, `#balance`, `#custody`, and `#relay`.
|
|
32
32
|
- `Forms` describes reusable structural blocks such as `#accountAsset` and `#accountAmount`, mostly used by queries.
|
|
33
33
|
- `Keys` contains the runtime `bytes4` keys derived from block names.
|
|
34
34
|
|
|
@@ -77,21 +77,21 @@ abstract contract ExampleCommand is CommandBase {
|
|
|
77
77
|
uint internal immutable myCommandId = commandId(NAME);
|
|
78
78
|
|
|
79
79
|
constructor() {
|
|
80
|
-
emit Command(host, myCommandId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
|
|
80
|
+
emit Command(host, myCommandId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, false);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function myCommand(
|
|
84
84
|
CommandContext calldata c
|
|
85
85
|
) external onlyCommand returns (bytes memory) {
|
|
86
|
-
(Cur memory request, uint groups) =
|
|
86
|
+
(Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
|
|
87
87
|
Writer memory writer = Writers.allocBalances(groups);
|
|
88
88
|
|
|
89
|
-
while (request.i < request.
|
|
89
|
+
while (request.i < request.len) {
|
|
90
90
|
(bytes32 asset, bytes32 meta, uint amount) = request.unpackAmount();
|
|
91
91
|
writer.appendBalance(asset, meta, amount);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
request.
|
|
94
|
+
request.complete();
|
|
95
95
|
return writer.finish();
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -99,14 +99,15 @@ abstract contract ExampleCommand is CommandBase {
|
|
|
99
99
|
|
|
100
100
|
## Repo Layout
|
|
101
101
|
|
|
102
|
-
- `contracts/core`
|
|
103
|
-
- `contracts/commands`
|
|
104
|
-
- `contracts/peer`
|
|
105
|
-
- `contracts/
|
|
106
|
-
- `contracts/
|
|
107
|
-
- `contracts/
|
|
108
|
-
- `contracts/
|
|
109
|
-
- `
|
|
102
|
+
- `contracts/core` - host, access control, balances, operation base, and signature validation
|
|
103
|
+
- `contracts/commands` - standard command building blocks and admin commands
|
|
104
|
+
- `contracts/peer` - peer protocol surfaces for inter-host asset flows and asset allow/deny
|
|
105
|
+
- `contracts/guards` - guard action surfaces for delegated protection flows
|
|
106
|
+
- `contracts/queries` - read-only query endpoints for protocol state
|
|
107
|
+
- `contracts/blocks` - block stream schema (`Schema`), cursor parsing (`Cursors`), and writers (`Writers`)
|
|
108
|
+
- `contracts/utils` - shared encoding helpers: IDs, assets, accounts, layout, ECDSA
|
|
109
|
+
- `contracts/events` - protocol event contracts and emitters
|
|
110
|
+
- `docs` - introductory documentation
|
|
110
111
|
|
|
111
112
|
## Install and Compile
|
|
112
113
|
|
package/blocks/Cursors.sol
CHANGED
|
@@ -6,7 +6,7 @@ import {Sizes} from "./Schema.sol";
|
|
|
6
6
|
import {Keys} from "./Keys.sol";
|
|
7
7
|
|
|
8
8
|
/// @notice Zero-copy view into a calldata block stream.
|
|
9
|
-
/// All positions (`i
|
|
9
|
+
/// All positions (`i`) are byte offsets relative to the start of the source region.
|
|
10
10
|
/// The absolute calldata location of byte `i` is `offset + i`.
|
|
11
11
|
struct Cur {
|
|
12
12
|
/// @dev Absolute calldata byte offset of the source region start.
|
|
@@ -15,9 +15,6 @@ struct Cur {
|
|
|
15
15
|
uint i;
|
|
16
16
|
/// @dev Total byte length of the source region.
|
|
17
17
|
uint len;
|
|
18
|
-
/// @dev Exclusive upper bound for the current iteration group, set by `primeRun`.
|
|
19
|
-
/// Zero until `primeRun` is called.
|
|
20
|
-
uint bound;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
using Cursors for Cur;
|
|
@@ -32,11 +29,11 @@ library Cursors {
|
|
|
32
29
|
error MalformedBlocks();
|
|
33
30
|
/// @dev Current block key does not match the expected key, or payload size is out of range.
|
|
34
31
|
error InvalidBlock();
|
|
35
|
-
/// @dev `complete` called but the cursor has not consumed exactly
|
|
32
|
+
/// @dev `complete` called but the cursor has not consumed exactly to `len`.
|
|
36
33
|
error IncompleteCursor();
|
|
37
|
-
/// @dev `
|
|
34
|
+
/// @dev `run` found zero blocks of the expected key; the cursor region is empty.
|
|
38
35
|
error ZeroCursor();
|
|
39
|
-
/// @dev `
|
|
36
|
+
/// @dev `run` was called with a zero group size.
|
|
40
37
|
error ZeroGroup();
|
|
41
38
|
/// @dev An account field was required but the block or fallback was zero.
|
|
42
39
|
error ZeroAccount();
|
|
@@ -65,15 +62,50 @@ library Cursors {
|
|
|
65
62
|
cur.len = source.length;
|
|
66
63
|
}
|
|
67
64
|
|
|
68
|
-
/// @notice Create a cursor
|
|
69
|
-
///
|
|
70
|
-
/// @param
|
|
65
|
+
/// @notice Create a cursor backed by `source[i:]`.
|
|
66
|
+
/// @param source Calldata slice that forms the parent block stream.
|
|
67
|
+
/// @param i Start byte offset within `source`.
|
|
68
|
+
/// @return cur Cursor positioned at the beginning of `source[i:]`.
|
|
69
|
+
function open(bytes calldata source, uint i) internal pure returns (Cur memory cur) {
|
|
70
|
+
return open(source[i:]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// @notice Create a cursor over `source[i:]` and restrict it to its first grouped run.
|
|
74
|
+
/// Equivalent to `open(source, i)`, reading the current key, then `run(key, group)`.
|
|
75
|
+
/// @param source Calldata slice that forms the parent block stream.
|
|
76
|
+
/// @param i Start byte offset within `source`.
|
|
71
77
|
/// @param group Expected block group size (e.g. 1 for single, 2 for paired).
|
|
72
|
-
/// @return cur Cursor with `
|
|
73
|
-
/// @return groups Number of block groups in the run (`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
/// @return cur Cursor with `len` truncated to the end of the first run in `source[i:]`.
|
|
79
|
+
/// @return groups Number of block groups in the run (`block count / group`).
|
|
80
|
+
/// @return next Byte offset immediately after the run, relative to `source`.
|
|
81
|
+
function init(
|
|
82
|
+
bytes calldata source,
|
|
83
|
+
uint i,
|
|
84
|
+
uint group
|
|
85
|
+
) internal pure returns (Cur memory cur, uint groups, uint next) {
|
|
86
|
+
cur = open(source, i);
|
|
87
|
+
if (cur.i == cur.len) revert ZeroCursor();
|
|
88
|
+
(bytes4 key, ) = cur.peek(cur.i);
|
|
89
|
+
groups = cur.run(key, group);
|
|
90
|
+
next = i + cur.len;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// @notice Create a cursor over `source[i:]`, restrict it to its first grouped run, and require an exact group count.
|
|
94
|
+
/// @param source Calldata slice that forms the parent block stream.
|
|
95
|
+
/// @param i Start byte offset within `source`.
|
|
96
|
+
/// @param group Expected block group size (e.g. 1 for single, 2 for paired).
|
|
97
|
+
/// @param expectedGroups Required number of groups in the run.
|
|
98
|
+
/// @return cur Cursor with `len` truncated to the end of the first run in `source[i:]`.
|
|
99
|
+
/// @return next Byte offset immediately after the run, relative to `source`.
|
|
100
|
+
function init(
|
|
101
|
+
bytes calldata source,
|
|
102
|
+
uint i,
|
|
103
|
+
uint group,
|
|
104
|
+
uint expectedGroups
|
|
105
|
+
) internal pure returns (Cur memory cur, uint next) {
|
|
106
|
+
uint groups;
|
|
107
|
+
(cur, groups, next) = init(source, i, group);
|
|
108
|
+
if (groups != expectedGroups) revert BadRatio();
|
|
77
109
|
}
|
|
78
110
|
|
|
79
111
|
/// @notice Move the cursor to an absolute position within the source region.
|
|
@@ -119,7 +151,7 @@ library Cursors {
|
|
|
119
151
|
}
|
|
120
152
|
|
|
121
153
|
/// @notice Return the full cursor region as a calldata slice.
|
|
122
|
-
/// Does not advance the cursor; `cur.i`
|
|
154
|
+
/// Does not advance the cursor; `cur.i` is ignored.
|
|
123
155
|
/// @param cur Cursor whose backing region should be returned.
|
|
124
156
|
/// @return data Calldata view over `[cur.offset, cur.offset + cur.len)`.
|
|
125
157
|
function raw(Cur memory cur) internal pure returns (bytes calldata data) {
|
|
@@ -128,7 +160,7 @@ library Cursors {
|
|
|
128
160
|
}
|
|
129
161
|
|
|
130
162
|
/// @notice Return a sub-range of the cursor region as a calldata slice.
|
|
131
|
-
/// Does not advance the cursor; `cur.i`
|
|
163
|
+
/// Does not advance the cursor; `cur.i` is ignored.
|
|
132
164
|
/// @param cur Source cursor.
|
|
133
165
|
/// @param from Start byte offset within the source region (inclusive).
|
|
134
166
|
/// @param to End byte offset within the source region (exclusive).
|
|
@@ -267,21 +299,19 @@ library Cursors {
|
|
|
267
299
|
}
|
|
268
300
|
}
|
|
269
301
|
|
|
270
|
-
/// @notice
|
|
271
|
-
///
|
|
272
|
-
///
|
|
273
|
-
///
|
|
274
|
-
/// @param
|
|
302
|
+
/// @notice Restrict the cursor to the consecutive run of `key` at its current position.
|
|
303
|
+
/// Counts the run, truncates `cur.len` to the run end, and validates that the
|
|
304
|
+
/// count is a multiple of `group`.
|
|
305
|
+
/// @param cur Cursor to restrict; `cur.len` is updated in place.
|
|
306
|
+
/// @param key Expected block type identifier of the run.
|
|
275
307
|
/// @param group Expected group size (e.g. 1 for single-asset, 2 for paired input/output).
|
|
276
|
-
/// @return key Block type identifier of the run.
|
|
277
308
|
/// @return groups Number of groups represented by the run (`block count / group`).
|
|
278
|
-
function
|
|
309
|
+
function run(Cur memory cur, bytes4 key, uint group) internal pure returns (uint groups) {
|
|
279
310
|
if (group == 0) revert ZeroGroup();
|
|
280
|
-
|
|
281
|
-
uint count;
|
|
282
|
-
(count, cur.bound) = countRun(cur, cur.i, key);
|
|
311
|
+
(uint count, uint next) = countRun(cur, cur.i, key);
|
|
283
312
|
if (count == 0) revert ZeroCursor();
|
|
284
313
|
if (count % group != 0) revert BadRatio();
|
|
314
|
+
cur.len = next;
|
|
285
315
|
groups = count / group;
|
|
286
316
|
}
|
|
287
317
|
|
|
@@ -361,17 +391,6 @@ library Cursors {
|
|
|
361
391
|
return maybeTake(cur, Keys.Data);
|
|
362
392
|
}
|
|
363
393
|
|
|
364
|
-
/// @notice Enter a List block, prime its member run, and return the group count.
|
|
365
|
-
/// @param cur Cursor positioned at a list block; advanced past the 8-byte header.
|
|
366
|
-
/// @param group Expected block group size for the list item stream.
|
|
367
|
-
/// @return groups Number of block groups in the list payload.
|
|
368
|
-
/// @return next Byte offset immediately after the list payload.
|
|
369
|
-
function list(Cur memory cur, uint group) internal pure returns (uint groups, uint next) {
|
|
370
|
-
next = list(cur);
|
|
371
|
-
(, groups) = cur.primeRun(group);
|
|
372
|
-
if (cur.bound != next) revert IncompleteCursor();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
394
|
/// @notice Exit a nested region at an exact boundary.
|
|
376
395
|
/// Reverts with `IncompleteCursor` if `end` exceeds the cursor region length
|
|
377
396
|
/// or `cur.i != end`.
|
|
@@ -381,13 +400,6 @@ library Cursors {
|
|
|
381
400
|
if (end > cur.len || cur.i != end) revert IncompleteCursor();
|
|
382
401
|
}
|
|
383
402
|
|
|
384
|
-
/// @notice Assert that the cursor has consumed exactly up to `bound`.
|
|
385
|
-
/// Reverts with `IncompleteCursor` if `bound` is zero or `cur.i != cur.bound`.
|
|
386
|
-
/// @param cur Cursor to check.
|
|
387
|
-
function close(Cur memory cur) internal pure {
|
|
388
|
-
if (cur.bound == 0 || cur.i != cur.bound) revert IncompleteCursor();
|
|
389
|
-
}
|
|
390
|
-
|
|
391
403
|
/// @notice Assert that the cursor has consumed its entire source region.
|
|
392
404
|
/// Reverts with `IncompleteCursor` when `cur.i != cur.len`.
|
|
393
405
|
/// @param cur Cursor to check.
|
|
@@ -527,7 +539,11 @@ library Cursors {
|
|
|
527
539
|
/// @param state Embedded state block stream.
|
|
528
540
|
/// @param request Embedded request block stream.
|
|
529
541
|
/// @return Encoded CONTEXT block bytes.
|
|
530
|
-
function toContextBlock(
|
|
542
|
+
function toContextBlock(
|
|
543
|
+
bytes32 account,
|
|
544
|
+
bytes memory state,
|
|
545
|
+
bytes memory request
|
|
546
|
+
) internal pure returns (bytes memory) {
|
|
531
547
|
return createBlock(Keys.Context, bytes.concat(account, toBytesBlock(state), toBytesBlock(request)));
|
|
532
548
|
}
|
|
533
549
|
|
|
@@ -535,15 +551,33 @@ library Cursors {
|
|
|
535
551
|
/// @param value Native value assigned to the pipe.
|
|
536
552
|
/// @param account Command account identifier.
|
|
537
553
|
/// @param state Embedded state block stream.
|
|
538
|
-
/// @param
|
|
554
|
+
/// @param steps Embedded step block stream.
|
|
539
555
|
/// @return Encoded PIPE block bytes.
|
|
540
556
|
function toPipeBlock(
|
|
541
557
|
uint value,
|
|
542
558
|
bytes32 account,
|
|
543
559
|
bytes memory state,
|
|
544
|
-
bytes memory
|
|
560
|
+
bytes memory steps
|
|
545
561
|
) internal pure returns (bytes memory) {
|
|
546
|
-
return createBlock(Keys.Pipe, bytes.concat(bytes32(value), toContextBlock(account, state,
|
|
562
|
+
return createBlock(Keys.Pipe, bytes.concat(bytes32(value), toContextBlock(account, state, steps)));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/// @notice Encode a RELAY block.
|
|
566
|
+
/// @param chain Destination chain node ID.
|
|
567
|
+
/// @param endowment Native value requested for the destination pipe.
|
|
568
|
+
/// @param steps Nested step block stream.
|
|
569
|
+
/// @return Encoded RELAY block bytes.
|
|
570
|
+
function toRelayBlock(uint chain, uint endowment, bytes memory steps) internal pure returns (bytes memory) {
|
|
571
|
+
return createBlock(Keys.Relay, bytes.concat(bytes32(chain), bytes32(endowment), toBytesBlock(steps)));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/// @notice Encode a DISPATCH block.
|
|
575
|
+
/// @param chain Destination chain node ID.
|
|
576
|
+
/// @param endowment Native value requested for the destination dispatch.
|
|
577
|
+
/// @param payload Encoded cross-chain payload.
|
|
578
|
+
/// @return Encoded DISPATCH block bytes.
|
|
579
|
+
function toDispatchBlock(uint chain, uint endowment, bytes memory payload) internal pure returns (bytes memory) {
|
|
580
|
+
return createBlock(Keys.Dispatch, bytes.concat(bytes32(chain), bytes32(endowment), toBytesBlock(payload)));
|
|
547
581
|
}
|
|
548
582
|
|
|
549
583
|
// -------------------------------------------------------------------------
|
|
@@ -902,38 +936,6 @@ library Cursors {
|
|
|
902
936
|
(value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Balance);
|
|
903
937
|
}
|
|
904
938
|
|
|
905
|
-
/// @notice Consume a MINIMUM block and return its fields as separate values.
|
|
906
|
-
/// @param cur Cursor; advanced past the block.
|
|
907
|
-
/// @return asset Asset identifier.
|
|
908
|
-
/// @return meta Asset metadata slot.
|
|
909
|
-
/// @return amount Minimum acceptable amount.
|
|
910
|
-
function unpackMinimum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
|
|
911
|
-
return unpackAssetAmount(cur, Keys.Minimum);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/// @notice Consume a MINIMUM block and return its fields as a struct.
|
|
915
|
-
/// @param cur Cursor; advanced past the block.
|
|
916
|
-
/// @return value Decoded asset, meta, and minimum amount.
|
|
917
|
-
function unpackMinimumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
|
|
918
|
-
(value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Minimum);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
/// @notice Consume a MAXIMUM block and return its fields as separate values.
|
|
922
|
-
/// @param cur Cursor; advanced past the block.
|
|
923
|
-
/// @return asset Asset identifier.
|
|
924
|
-
/// @return meta Asset metadata slot.
|
|
925
|
-
/// @return amount Maximum allowable spend.
|
|
926
|
-
function unpackMaximum(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta, uint amount) {
|
|
927
|
-
return unpackAssetAmount(cur, Keys.Maximum);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
/// @notice Consume a MAXIMUM block and return its fields as a struct.
|
|
931
|
-
/// @param cur Cursor; advanced past the block.
|
|
932
|
-
/// @return value Decoded asset, meta, and maximum amount.
|
|
933
|
-
function unpackMaximumValue(Cur memory cur) internal pure returns (AssetAmount memory value) {
|
|
934
|
-
(value.asset, value.meta, value.amount) = unpackAssetAmount(cur, Keys.Maximum);
|
|
935
|
-
}
|
|
936
|
-
|
|
937
939
|
/// @notice Consume a HOST_ACCOUNT_ASSET form block and return its fields as separate values.
|
|
938
940
|
/// @param cur Cursor; advanced past the block.
|
|
939
941
|
/// @return host Host node ID.
|
|
@@ -953,25 +955,6 @@ library Cursors {
|
|
|
953
955
|
(value.host, value.account, value.asset, value.meta) = unpackHostAccountAsset(cur, Keys.HostAccountAsset);
|
|
954
956
|
}
|
|
955
957
|
|
|
956
|
-
/// @notice Consume a PAYOUT block and return its fields as separate values.
|
|
957
|
-
/// @param cur Cursor; advanced past the block.
|
|
958
|
-
/// @return account Account identifier.
|
|
959
|
-
/// @return asset Asset identifier.
|
|
960
|
-
/// @return meta Asset metadata slot.
|
|
961
|
-
/// @return amount Token amount.
|
|
962
|
-
function unpackPayout(
|
|
963
|
-
Cur memory cur
|
|
964
|
-
) internal pure returns (bytes32 account, bytes32 asset, bytes32 meta, uint amount) {
|
|
965
|
-
return unpackAccountAmount(cur, Keys.Payout);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
/// @notice Consume a PAYOUT block and return its fields as a struct.
|
|
969
|
-
/// @param cur Cursor; advanced past the block.
|
|
970
|
-
/// @return value Decoded account, asset, meta, and amount.
|
|
971
|
-
function unpackPayoutValue(Cur memory cur) internal pure returns (AccountAmount memory value) {
|
|
972
|
-
(value.account, value.asset, value.meta, value.amount) = unpackAccountAmount(cur, Keys.Payout);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
958
|
/// @notice Consume an ACCOUNT_AMOUNT form block and return its fields as separate values.
|
|
976
959
|
/// @param cur Cursor; advanced past the block.
|
|
977
960
|
/// @return account Account identifier.
|
|
@@ -1102,7 +1085,9 @@ library Cursors {
|
|
|
1102
1085
|
/// @return account Command account identifier.
|
|
1103
1086
|
/// @return state Embedded state block stream.
|
|
1104
1087
|
/// @return request Embedded request block stream.
|
|
1105
|
-
function unpackContext(
|
|
1088
|
+
function unpackContext(
|
|
1089
|
+
Cur memory cur
|
|
1090
|
+
) internal pure returns (bytes32 account, bytes calldata state, bytes calldata request) {
|
|
1106
1091
|
uint end = cur.enter(Keys.Context, 32 + 2 * Sizes.Header, 0);
|
|
1107
1092
|
account = cur.read32();
|
|
1108
1093
|
state = cur.unpackBytes();
|
|
@@ -1115,13 +1100,43 @@ library Cursors {
|
|
|
1115
1100
|
/// @return value Native value assigned to the pipe.
|
|
1116
1101
|
/// @return account Command account identifier.
|
|
1117
1102
|
/// @return state Embedded state block stream.
|
|
1118
|
-
/// @return
|
|
1103
|
+
/// @return steps Embedded step block stream.
|
|
1119
1104
|
function unpackPipe(
|
|
1120
1105
|
Cur memory cur
|
|
1121
|
-
) internal pure returns (uint value, bytes32 account, bytes calldata state, bytes calldata
|
|
1106
|
+
) internal pure returns (uint value, bytes32 account, bytes calldata state, bytes calldata steps) {
|
|
1122
1107
|
uint end = cur.enter(Keys.Pipe, 32 + Sizes.Header + 32 + 2 * Sizes.Header, 0);
|
|
1123
1108
|
value = uint(cur.read32());
|
|
1124
|
-
(account, state,
|
|
1109
|
+
(account, state, steps) = cur.unpackContext();
|
|
1110
|
+
cur.exit(end);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/// @notice Consume a RELAY block and return its destination chain, endowment, and step stream.
|
|
1114
|
+
/// @param cur Cursor; advanced past the block.
|
|
1115
|
+
/// @return chain Destination chain node ID.
|
|
1116
|
+
/// @return endowment Native value requested for the destination pipe.
|
|
1117
|
+
/// @return steps Embedded step block stream.
|
|
1118
|
+
function unpackRelay(
|
|
1119
|
+
Cur memory cur
|
|
1120
|
+
) internal pure returns (uint chain, uint endowment, bytes calldata steps) {
|
|
1121
|
+
uint end = cur.enter(Keys.Relay, 64 + Sizes.Header, 0);
|
|
1122
|
+
chain = uint(cur.read32());
|
|
1123
|
+
endowment = uint(cur.read32());
|
|
1124
|
+
steps = cur.unpackBytes();
|
|
1125
|
+
cur.exit(end);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/// @notice Consume a DISPATCH block and return its destination chain, endowment, and payload.
|
|
1129
|
+
/// @param cur Cursor; advanced past the block.
|
|
1130
|
+
/// @return chain Destination chain node ID.
|
|
1131
|
+
/// @return endowment Native value requested for the destination dispatch.
|
|
1132
|
+
/// @return payload Encoded cross-chain payload.
|
|
1133
|
+
function unpackDispatch(
|
|
1134
|
+
Cur memory cur
|
|
1135
|
+
) internal pure returns (uint chain, uint endowment, bytes calldata payload) {
|
|
1136
|
+
uint end = cur.enter(Keys.Dispatch, 64 + Sizes.Header, 0);
|
|
1137
|
+
chain = uint(cur.read32());
|
|
1138
|
+
endowment = uint(cur.read32());
|
|
1139
|
+
payload = cur.unpackBytes();
|
|
1125
1140
|
cur.exit(end);
|
|
1126
1141
|
}
|
|
1127
1142
|
|
|
@@ -1194,15 +1209,6 @@ library Cursors {
|
|
|
1194
1209
|
if (uint(bytes32(msg.data[abs + 64:abs + 96])) != 1) revert UnexpectedValue();
|
|
1195
1210
|
}
|
|
1196
1211
|
|
|
1197
|
-
/// @notice Consume a MINIMUM block and assert it matches the expected asset and meta.
|
|
1198
|
-
/// @param cur Cursor; advanced past the block.
|
|
1199
|
-
/// @param asset Expected asset identifier.
|
|
1200
|
-
/// @param meta Expected metadata slot.
|
|
1201
|
-
/// @return amount Minimum amount from the block.
|
|
1202
|
-
function requireMinimum(Cur memory cur, bytes32 asset, bytes32 meta) internal pure returns (uint amount) {
|
|
1203
|
-
return requireAssetAmount(cur, Keys.Minimum, asset, meta);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
1212
|
/// @notice Consume a host amount block and assert it matches the expected host.
|
|
1207
1213
|
/// @param cur Cursor; advanced past the block.
|
|
1208
1214
|
/// @param key Expected block type key.
|
|
@@ -1324,7 +1330,39 @@ library Cursors {
|
|
|
1324
1330
|
}
|
|
1325
1331
|
|
|
1326
1332
|
// -------------------------------------------------------------------------
|
|
1327
|
-
//
|
|
1333
|
+
// ensure* - validate constraint blocks against provided values
|
|
1334
|
+
// -------------------------------------------------------------------------
|
|
1335
|
+
|
|
1336
|
+
/// @notice Consume a BALANCE_LIMIT block and assert all constraint fields match the provided balance.
|
|
1337
|
+
/// @param cur Cursor; advanced past the block.
|
|
1338
|
+
/// @param asset Expected asset identifier.
|
|
1339
|
+
/// @param meta Expected metadata slot.
|
|
1340
|
+
/// @param amount Amount that must fall within the encoded min/max range.
|
|
1341
|
+
function ensureBalanceLimit(Cur memory cur, bytes32 asset, bytes32 meta, uint amount) internal pure {
|
|
1342
|
+
uint abs = consume(cur, 0, Keys.BalanceLimit, 128, 128);
|
|
1343
|
+
if (bytes32(msg.data[abs:abs + 32]) != asset) revert UnexpectedValue();
|
|
1344
|
+
if (bytes32(msg.data[abs + 32:abs + 64]) != meta) revert UnexpectedValue();
|
|
1345
|
+
if (uint(bytes32(msg.data[abs + 64:abs + 96])) > amount) revert UnexpectedValue();
|
|
1346
|
+
if (uint(bytes32(msg.data[abs + 96:abs + 128])) < amount) revert UnexpectedValue();
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
/// @notice Consume a CUSTODY_LIMIT block and assert all constraint fields match the provided custody.
|
|
1350
|
+
/// @param cur Cursor; advanced past the block.
|
|
1351
|
+
/// @param host Expected host node ID.
|
|
1352
|
+
/// @param asset Expected asset identifier.
|
|
1353
|
+
/// @param meta Expected metadata slot.
|
|
1354
|
+
/// @param amount Amount that must fall within the encoded min/max range.
|
|
1355
|
+
function ensureCustodyLimit(Cur memory cur, uint host, bytes32 asset, bytes32 meta, uint amount) internal pure {
|
|
1356
|
+
uint abs = consume(cur, 0, Keys.CustodyLimit, 160, 160);
|
|
1357
|
+
if (uint(bytes32(msg.data[abs:abs + 32])) != host) revert UnexpectedValue();
|
|
1358
|
+
if (bytes32(msg.data[abs + 32:abs + 64]) != asset) revert UnexpectedValue();
|
|
1359
|
+
if (bytes32(msg.data[abs + 64:abs + 96]) != meta) revert UnexpectedValue();
|
|
1360
|
+
if (uint(bytes32(msg.data[abs + 96:abs + 128])) > amount) revert UnexpectedValue();
|
|
1361
|
+
if (uint(bytes32(msg.data[abs + 128:abs + 160])) < amount) revert UnexpectedValue();
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// -------------------------------------------------------------------------
|
|
1365
|
+
// Search helpers
|
|
1328
1366
|
// -------------------------------------------------------------------------
|
|
1329
1367
|
|
|
1330
1368
|
/// @notice Look for a NODE block anywhere in a calldata source and return its value.
|
|
@@ -1364,52 +1402,4 @@ library Cursors {
|
|
|
1364
1402
|
(uint abs, ) = expect(cur, i, 0, Keys.Account, 32, 32);
|
|
1365
1403
|
return bytes32(msg.data[abs:abs + 32]);
|
|
1366
1404
|
}
|
|
1367
|
-
|
|
1368
|
-
/// @notice Look for a NODE block after the current run boundary and return its value.
|
|
1369
|
-
/// Searches from `cur.bound` to the end of the source region.
|
|
1370
|
-
/// @param cur Source cursor; `bound` marks the end of the primary run.
|
|
1371
|
-
/// @param backup Value to return if no NODE block is found.
|
|
1372
|
-
/// @return node Node ID from the NODE block, or `backup` if absent.
|
|
1373
|
-
function nodeAfter(Cur memory cur, uint backup) internal pure returns (uint node) {
|
|
1374
|
-
uint i = find(cur, cur.bound, Keys.Node);
|
|
1375
|
-
if (i == cur.len) return backup;
|
|
1376
|
-
|
|
1377
|
-
(uint abs, ) = expect(cur, i, 0, Keys.Node, 32, 32);
|
|
1378
|
-
return uint(bytes32(msg.data[abs:abs + 32]));
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
/// @notice Look for an ACCOUNT block after the current run boundary and return its value.
|
|
1382
|
-
/// Searches from `cur.bound` to the end of the source region.
|
|
1383
|
-
/// @param cur Source cursor; `bound` marks the end of the primary run.
|
|
1384
|
-
/// @param backup Account to return if no ACCOUNT block is found.
|
|
1385
|
-
/// @return account Account from the ACCOUNT block, or `backup` if absent.
|
|
1386
|
-
function accountAfter(Cur memory cur, bytes32 backup) internal pure returns (bytes32 account) {
|
|
1387
|
-
uint i = find(cur, cur.bound, Keys.Account);
|
|
1388
|
-
if (i == cur.len) return backup;
|
|
1389
|
-
|
|
1390
|
-
(uint abs, ) = expect(cur, i, 0, Keys.Account, 32, 32);
|
|
1391
|
-
return bytes32(msg.data[abs:abs + 32]);
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
/// @notice Parse the trailing AUTH block and compute the signed message hash.
|
|
1395
|
-
/// The AUTH block must occupy the final `Sizes.Auth` bytes of the source region
|
|
1396
|
-
/// and must begin after `cur.bound`.
|
|
1397
|
-
/// The signed slice covers from `cur.i` up to (but not including) the AUTH proof bytes.
|
|
1398
|
-
/// @param cur Source cursor; `bound` marks the end of the primary data region.
|
|
1399
|
-
/// @param cid Command ID that the signature must be bound to.
|
|
1400
|
-
/// @return digest keccak256 of the signed message slice.
|
|
1401
|
-
/// @return deadline Expiry timestamp from the AUTH block.
|
|
1402
|
-
/// @return proof Raw proof bytes (layout: `[bytes20 signer][bytes65 sig]`).
|
|
1403
|
-
function authLast(
|
|
1404
|
-
Cur memory cur,
|
|
1405
|
-
uint cid
|
|
1406
|
-
) internal pure returns (bytes32 digest, uint deadline, bytes calldata proof) {
|
|
1407
|
-
if (cur.len - cur.i < Sizes.Auth) revert MalformedBlocks();
|
|
1408
|
-
|
|
1409
|
-
uint i = cur.len - Sizes.Auth;
|
|
1410
|
-
if (i < cur.bound) revert MalformedBlocks();
|
|
1411
|
-
|
|
1412
|
-
(deadline, proof) = expectAuth(cur, i, cid);
|
|
1413
|
-
digest = cur.hash(cur.i, cur.len - Sizes.Proof);
|
|
1414
|
-
}
|
|
1415
1405
|
}
|
package/blocks/Keys.sol
CHANGED
|
@@ -7,20 +7,22 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
library Keys {
|
|
8
8
|
/// @dev Empty / unset key.
|
|
9
9
|
bytes4 constant Empty = bytes4(0);
|
|
10
|
+
/// @dev Wildcard key used in discovery when any block stream is accepted.
|
|
11
|
+
bytes4 constant Any = 0xffffffff;
|
|
10
12
|
/// @dev Input amount - (bytes32 asset, bytes32 meta, uint amount)
|
|
11
13
|
bytes4 constant Amount = bytes4(keccak256("#amount"));
|
|
12
14
|
/// @dev Ledger balance - (bytes32 asset, bytes32 meta, uint amount)
|
|
13
15
|
bytes4 constant Balance = bytes4(keccak256("#balance"));
|
|
16
|
+
/// @dev Balance constraint - (bytes32 asset, bytes32 meta, uint min, uint max)
|
|
17
|
+
bytes4 constant BalanceLimit = bytes4(keccak256("#balanceLimit"));
|
|
14
18
|
/// @dev Host-scoped request amount - (uint host, bytes32 asset, bytes32 meta, uint amount)
|
|
15
19
|
bytes4 constant Allocation = bytes4(keccak256("#allocation"));
|
|
16
20
|
/// @dev Host-scoped allowance cap - (uint host, bytes32 asset, bytes32 meta, uint amount)
|
|
17
21
|
bytes4 constant Allowance = bytes4(keccak256("#allowance"));
|
|
18
22
|
/// @dev Cross-host custody state - (uint host, bytes32 asset, bytes32 meta, uint amount)
|
|
19
23
|
bytes4 constant Custody = bytes4(keccak256("#custody"));
|
|
20
|
-
/// @dev
|
|
21
|
-
bytes4 constant
|
|
22
|
-
/// @dev Maximum allowable spend - (bytes32 asset, bytes32 meta, uint amount)
|
|
23
|
-
bytes4 constant Maximum = bytes4(keccak256("#maximum"));
|
|
24
|
+
/// @dev Cross-host custody constraint - (uint host, bytes32 asset, bytes32 meta, uint min, uint max)
|
|
25
|
+
bytes4 constant CustodyLimit = bytes4(keccak256("#custodyLimit"));
|
|
24
26
|
/// @dev Fee amount - (uint amount)
|
|
25
27
|
bytes4 constant Fee = bytes4(keccak256("#fee"));
|
|
26
28
|
/// @dev List wrapper; payload is an embedded repeated block stream
|
|
@@ -33,12 +35,14 @@ library Keys {
|
|
|
33
35
|
bytes4 constant Bytes = bytes4(keccak256("#bytes"));
|
|
34
36
|
/// @dev Account identifier - (bytes32 account)
|
|
35
37
|
bytes4 constant Account = bytes4(keccak256("#account"));
|
|
36
|
-
/// @dev Transfer payout request - (bytes32 account, bytes32 asset, bytes32 meta, uint amount)
|
|
37
|
-
bytes4 constant Payout = bytes4(keccak256("#payout"));
|
|
38
38
|
/// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount)
|
|
39
39
|
bytes4 constant Transaction = bytes4(keccak256("#transaction"));
|
|
40
40
|
/// @dev Sub-command invocation - (uint target, uint value, #bytes as request)
|
|
41
41
|
bytes4 constant Step = bytes4(keccak256("#step"));
|
|
42
|
+
/// @dev Cross-chain pipe relay - (uint chain, uint endowment, #bytes as steps)
|
|
43
|
+
bytes4 constant Relay = bytes4(keccak256("#relay"));
|
|
44
|
+
/// @dev Cross-chain encoded payload dispatch - (uint chain, uint endowment, #bytes as payload)
|
|
45
|
+
bytes4 constant Dispatch = bytes4(keccak256("#dispatch"));
|
|
42
46
|
/// @dev Raw external call - (uint target, uint value, #bytes as payload)
|
|
43
47
|
bytes4 constant Call = bytes4(keccak256("#call"));
|
|
44
48
|
/// @dev Command context transport - (bytes32 account, #bytes as state, #bytes as request)
|