@rootzero/contracts 1.13.0 → 1.15.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 +87 -3
- package/Codec.sol +21 -0
- package/Commands.sol +14 -0
- package/Core.sol +8 -6
- package/Endpoints.sol +2 -7
- package/Events.sol +1 -2
- package/README.md +70 -37
- package/Utils.sol +2 -4
- package/annotations/Action.sol +17 -0
- package/annotations/Label.sol +23 -0
- package/annotations/Schema.sol +53 -0
- package/codec/Blocks.sol +1643 -0
- package/codec/Buffers.sol +165 -0
- package/codec/Decoders.sol +570 -0
- package/codec/Descriptors.sol +124 -0
- package/{blocks → codec}/Keys.sol +10 -18
- package/codec/Readers.sol +114 -0
- package/{blocks → codec}/Schema.sol +43 -80
- package/codec/Specs.sol +240 -0
- package/codec/Writers.sol +485 -0
- package/commands/Allocate.sol +21 -20
- package/commands/Base.sol +83 -45
- package/commands/Burn.sol +23 -14
- package/commands/Credit.sol +21 -20
- package/commands/Debit.sol +25 -28
- package/commands/Deposit.sol +41 -36
- package/commands/Payout.sol +26 -16
- package/commands/Provision.sol +37 -38
- package/commands/Recover.sol +20 -19
- package/commands/Relay.sol +24 -22
- package/commands/Withdraw.sol +20 -19
- package/commands/admin/AllowAssets.sol +19 -16
- package/commands/admin/Allowance.sol +18 -13
- package/commands/admin/Annotate.sol +36 -0
- package/commands/admin/Appoint.sol +19 -16
- package/commands/admin/Authorize.sol +23 -14
- package/commands/admin/Base.sol +9 -2
- package/commands/admin/DenyAssets.sol +19 -16
- package/commands/admin/Dismiss.sol +19 -16
- package/commands/admin/Execute.sol +21 -19
- package/commands/admin/Unauthorize.sol +23 -14
- package/core/Access.sol +91 -49
- package/core/Calls.sol +30 -33
- package/core/Endpoint.sol +46 -143
- package/core/Host.sol +63 -21
- package/core/Pipeline.sol +16 -15
- package/core/Types.sol +1 -1
- package/docs/Schema.md +48 -31
- package/events/Annotation.sol +24 -0
- package/events/Endpoint.sol +2 -2
- package/events/Guardian.sol +2 -2
- package/events/Introduction.sol +3 -2
- package/execution/Budget.sol +40 -0
- package/execution/Execution.sol +1104 -0
- package/guards/Base.sol +11 -12
- package/guards/Revoke.sol +11 -9
- package/package.json +1 -1
- package/ports/AllowAssets.sol +12 -15
- package/ports/Allowance.sol +11 -9
- package/ports/Base.sol +35 -16
- package/ports/Credit.sol +11 -9
- package/ports/Debit.sol +11 -9
- package/ports/DenyAssets.sol +10 -13
- package/ports/Dispatch.sol +13 -15
- package/ports/Pipe.sol +14 -12
- package/ports/Redeem.sol +12 -9
- package/ports/Settle.sol +16 -10
- package/queries/Assets.sol +15 -15
- package/queries/Balances.sol +17 -17
- package/queries/Base.sol +26 -12
- package/utils/Accounts.sol +0 -23
- package/utils/Actions.sol +1 -0
- package/utils/Cursors.sol +367 -0
- package/utils/Lanes.sol +14 -0
- package/utils/Layout.sol +0 -2
- package/utils/Selectors.sol +2 -2
- package/utils/Utils.sol +46 -0
- package/Cursors.sol +0 -16
- package/blocks/Cursors.sol +0 -1529
- package/blocks/Writers.sol +0 -1036
- package/commands/admin/Label.sol +0 -32
- package/commands/admin/Schemas.sol +0 -32
- package/core/Payable.sol +0 -53
- package/events/Labeled.sol +0 -21
- package/events/Schema.sol +0 -23
- package/utils/Value.sol +0 -43
package/queries/Assets.sol
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {Specs} from "../Codec.sol";
|
|
5
|
+
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
5
6
|
import {QueryBase} from "./Base.sol";
|
|
6
7
|
|
|
7
|
-
using
|
|
8
|
-
using Writers for Writer;
|
|
8
|
+
using Executions for Execution;
|
|
9
9
|
|
|
10
|
+
/// @notice Hook implemented by hosts that expose asset status queries.
|
|
10
11
|
abstract contract AssetStatusHook {
|
|
11
12
|
/// @notice Resolve support status for one asset.
|
|
12
13
|
/// Concrete implementations define the support policy and optional context codes.
|
|
@@ -17,28 +18,27 @@ abstract contract AssetStatusHook {
|
|
|
17
18
|
|
|
18
19
|
/// @title AssetStatus
|
|
19
20
|
/// @notice Rootzero query that checks support status for one or more assets.
|
|
20
|
-
/// The
|
|
21
|
-
/// The response returns one `STATUS` form block per query entry, preserving
|
|
21
|
+
/// The input is a run of `ASSET` blocks.
|
|
22
|
+
/// The response returns one `STATUS` form block per query entry, preserving input order.
|
|
22
23
|
abstract contract AssetStatus is QueryBase, AssetStatusHook {
|
|
23
|
-
|
|
24
|
+
uint private immutable descriptor;
|
|
24
25
|
|
|
25
26
|
constructor() {
|
|
26
|
-
(, descriptor) = query("assetStatus",
|
|
27
|
+
(, descriptor) = query("assetStatus", Specs.Asset, Specs.Status);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/// @notice Resolve asset support status for a run of requested assets.
|
|
30
|
-
/// @param
|
|
31
|
+
/// @param input Block-stream input consisting of `asset { bytes32 asset }` blocks.
|
|
31
32
|
/// @return Block-stream response containing one `status { uint code }` form block per asset block.
|
|
32
|
-
function assetStatus(bytes calldata
|
|
33
|
-
|
|
34
|
-
Writer memory response = Writers.allocStatuses(outputs);
|
|
33
|
+
function assetStatus(bytes calldata input) external view returns (bytes memory) {
|
|
34
|
+
Execution memory exec = openInput(input, descriptor, 0);
|
|
35
35
|
|
|
36
|
-
while (
|
|
37
|
-
bytes32 asset =
|
|
36
|
+
while (exec.more()) {
|
|
37
|
+
bytes32 asset = exec.unpackAsset(Lanes.Input);
|
|
38
38
|
uint status = assetStatus(asset);
|
|
39
|
-
|
|
39
|
+
exec.outputStatus(status);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
return
|
|
42
|
+
return close(exec);
|
|
43
43
|
}
|
|
44
44
|
}
|
package/queries/Balances.sol
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {Specs} from "../Codec.sol";
|
|
5
|
+
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
5
6
|
import {QueryBase} from "./Base.sol";
|
|
6
7
|
|
|
7
|
-
using
|
|
8
|
-
using Writers for Writer;
|
|
8
|
+
using Executions for Execution;
|
|
9
9
|
|
|
10
|
+
/// @notice Hook implemented by hosts that expose account balance queries.
|
|
10
11
|
abstract contract GetBalancesHook {
|
|
11
12
|
/// @notice Resolve one account's balance for one supported asset.
|
|
12
13
|
/// Concrete implementations define how assets are resolved.
|
|
@@ -18,28 +19,27 @@ abstract contract GetBalancesHook {
|
|
|
18
19
|
|
|
19
20
|
/// @title GetBalances
|
|
20
21
|
/// @notice Rootzero query that resolves balances for one or more `(account, asset)` tuples.
|
|
21
|
-
/// The
|
|
22
|
-
/// The response returns one `ACCOUNT_AMOUNT` form block per requested position, preserving
|
|
22
|
+
/// The input is a run of `ACCOUNT_ASSET` form blocks.
|
|
23
|
+
/// The response returns one `ACCOUNT_AMOUNT` form block per requested position, preserving input order.
|
|
23
24
|
abstract contract GetBalances is QueryBase, GetBalancesHook {
|
|
24
|
-
|
|
25
|
+
uint private immutable descriptor;
|
|
25
26
|
|
|
26
27
|
constructor() {
|
|
27
|
-
(, descriptor) = query("getBalances",
|
|
28
|
+
(, descriptor) = query("getBalances", Specs.AccountAsset, Specs.AccountAmount);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/// @notice Resolve balances for a run of requested `(account, asset)` tuples.
|
|
31
|
-
/// @param
|
|
32
|
-
/// @return Block-stream response containing one `accountAmount(account, asset, amount)` block per
|
|
33
|
-
function getBalances(bytes calldata
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(bytes32 account, bytes32 asset) = input.unpackAccountAsset();
|
|
32
|
+
/// @param input Block-stream input consisting of `accountAsset(account, asset)*`.
|
|
33
|
+
/// @return Block-stream response containing one `accountAmount(account, asset, amount)` block per input block.
|
|
34
|
+
function getBalances(bytes calldata input) external view returns (bytes memory) {
|
|
35
|
+
Execution memory exec = openInput(input, descriptor, 0);
|
|
36
|
+
|
|
37
|
+
while (exec.more()) {
|
|
38
|
+
(bytes32 account, bytes32 asset) = exec.unpackAccountAsset(Lanes.Input);
|
|
39
39
|
uint amount = getBalance(account, asset);
|
|
40
|
-
|
|
40
|
+
exec.outputAccountAmount(account, asset, amount);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
return
|
|
43
|
+
return close(exec);
|
|
44
44
|
}
|
|
45
45
|
}
|
package/queries/Base.sol
CHANGED
|
@@ -2,30 +2,44 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import { EndpointBase } from "../core/Endpoint.sol";
|
|
5
|
+
import { Descriptors } from "../codec/Descriptors.sol";
|
|
6
|
+
import { Specs } from "../codec/Specs.sol";
|
|
5
7
|
import { Nodes } from "../utils/Nodes.sol";
|
|
6
8
|
import { Selectors } from "../utils/Selectors.sol";
|
|
7
9
|
|
|
8
10
|
/// @title QueryBase
|
|
9
11
|
/// @notice Abstract base for rootzero query contracts.
|
|
10
|
-
/// Queries are view-only entry points that consume a block-stream
|
|
12
|
+
/// Queries are view-only entry points that consume a block-stream input and
|
|
11
13
|
/// return a block-stream response.
|
|
12
14
|
abstract contract QueryBase is EndpointBase {
|
|
13
15
|
|
|
14
16
|
/// @notice Publish query metadata and a default label.
|
|
15
|
-
/// @param name
|
|
16
|
-
///
|
|
17
|
-
/// @param
|
|
18
|
-
/// @param
|
|
17
|
+
/// @param name Query entrypoint name and default label. It must exactly
|
|
18
|
+
/// match the Solidity query function name used by the canonical ABI.
|
|
19
|
+
/// @param input Input block specification.
|
|
20
|
+
/// @param output Output block specification.
|
|
19
21
|
/// @return id Query node ID.
|
|
20
22
|
/// @return descriptor Packed endpoint lane metadata and flags.
|
|
21
23
|
function query(
|
|
22
24
|
string memory name,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
uint input,
|
|
26
|
+
uint output
|
|
27
|
+
) internal returns (uint id, uint descriptor) {
|
|
28
|
+
descriptor = Descriptors.create(Specs.Empty, input, output, 0, 0);
|
|
29
|
+
return query(name, descriptor);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// @notice Publish an already constructed query descriptor and default label.
|
|
33
|
+
/// @param name Query entrypoint name and default label. It must exactly
|
|
34
|
+
/// match the Solidity query function name used by the canonical ABI.
|
|
35
|
+
/// @param descriptor Packed query endpoint descriptor.
|
|
36
|
+
/// @return id Query node ID.
|
|
37
|
+
/// @return published Published endpoint descriptor.
|
|
38
|
+
function query(
|
|
39
|
+
string memory name,
|
|
40
|
+
uint descriptor
|
|
41
|
+
) internal returns (uint id, uint published) {
|
|
42
|
+
id = Nodes.toQuery(Selectors.query(name), address(this));
|
|
43
|
+
published = endpoint(id, name, descriptor);
|
|
30
44
|
}
|
|
31
45
|
}
|
package/utils/Accounts.sol
CHANGED
|
@@ -10,7 +10,6 @@ import {ensureAddr, isFamily, toLocalBase, toUnspecifiedBase} from "./Utils.sol"
|
|
|
10
10
|
///
|
|
11
11
|
/// Account IDs embed a 4-byte type tag in bits [255:224]:
|
|
12
12
|
/// - `Admin` — chain-local EVM address in bits [191:32]
|
|
13
|
-
/// - `Guardian` — chain-local EVM address in bits [191:32]
|
|
14
13
|
/// - `User` — chain-agnostic EVM address in bits [191:32]
|
|
15
14
|
///
|
|
16
15
|
/// If the first byte is zero, the account is an opaque
|
|
@@ -26,8 +25,6 @@ library Accounts {
|
|
|
26
25
|
uint24 constant Family = (uint24(Layout.Evm) << 8) | uint24(Layout.Account);
|
|
27
26
|
/// @dev Full 4-byte type prefix for admin accounts (chain-local EVM address).
|
|
28
27
|
uint32 constant Admin = (uint32(Layout.Evm) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.Admin);
|
|
29
|
-
/// @dev Full 4-byte type prefix for guardian accounts (chain-local EVM address).
|
|
30
|
-
uint32 constant Guardian = (uint32(Layout.Evm) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.Guardian);
|
|
31
28
|
/// @dev Full 4-byte type prefix for user accounts (chain-agnostic EVM address).
|
|
32
29
|
uint32 constant User = (uint32(Layout.Evm) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.User);
|
|
33
30
|
|
|
@@ -53,11 +50,6 @@ library Accounts {
|
|
|
53
50
|
return prefix(account) == Admin;
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
/// @notice Return true if `account` is a guardian account.
|
|
57
|
-
function isGuardian(bytes32 account) internal pure returns (bool) {
|
|
58
|
-
return prefix(account) == Guardian;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
53
|
/// @notice Return true if `account` is a user account.
|
|
62
54
|
function isUser(bytes32 account) internal pure returns (bool) {
|
|
63
55
|
return prefix(account) == User;
|
|
@@ -87,14 +79,6 @@ library Accounts {
|
|
|
87
79
|
return value;
|
|
88
80
|
}
|
|
89
81
|
|
|
90
|
-
/// @notice Assert that `value` is a guardian account and return it unchanged.
|
|
91
|
-
/// @param value Account identifier to validate.
|
|
92
|
-
/// @return account The same `value` if it is a guardian account.
|
|
93
|
-
function guardian(bytes32 value) internal pure returns (bytes32 account) {
|
|
94
|
-
if (!isGuardian(value)) revert InvalidAccount();
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
82
|
/// @notice Assert that `value` is a user account and return it unchanged.
|
|
99
83
|
/// @param value Account identifier to validate.
|
|
100
84
|
/// @return account The same `value` if it is a user account.
|
|
@@ -110,13 +94,6 @@ library Accounts {
|
|
|
110
94
|
return bytes32(toLocalBase(Admin) | (uint(uint160(account)) << 32));
|
|
111
95
|
}
|
|
112
96
|
|
|
113
|
-
/// @notice Encode an EVM address as a chain-local guardian account ID.
|
|
114
|
-
/// @param account EVM address to embed.
|
|
115
|
-
/// @return Guardian account ID bound to the current chain.
|
|
116
|
-
function toGuardian(address account) internal view returns (bytes32) {
|
|
117
|
-
return bytes32(toLocalBase(Guardian) | (uint(uint160(account)) << 32));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
97
|
/// @notice Encode an EVM address as a chain-agnostic user account ID.
|
|
121
98
|
/// @param account EVM address to embed.
|
|
122
99
|
/// @return User account ID without a chain binding.
|
package/utils/Actions.sol
CHANGED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {clear32, max16, max32, max128} from "./Utils.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Mutable memory wrapper around a packed cursor.
|
|
7
|
+
/// @dev A second tagged cursor may occupy the upper 128-bit lane of `state`.
|
|
8
|
+
/// Cursor operations target the lower lane; `Cursors.select` swaps the requested
|
|
9
|
+
/// tagged cursor into that position.
|
|
10
|
+
struct Cur {
|
|
11
|
+
uint state;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// @title Cursors
|
|
15
|
+
/// @notice Packed cursor state and navigation for grouped byte regions.
|
|
16
|
+
/// @dev Each 128-bit cursor uses the following layout:
|
|
17
|
+
/// bits 0-31 i
|
|
18
|
+
/// bits 32-63 offset
|
|
19
|
+
/// bits 64-95 len
|
|
20
|
+
/// bits 96-111 groups
|
|
21
|
+
/// bits 112-119 flags (consumer-defined)
|
|
22
|
+
/// bits 120-127 tag
|
|
23
|
+
///
|
|
24
|
+
/// A cursor is one 128-bit value with this layout. A pair is a 256-bit value
|
|
25
|
+
/// containing a lower cursor in bits 0-127 and an optional higher cursor in
|
|
26
|
+
/// bits 128-255. The lower cursor is the active cursor: navigation and
|
|
27
|
+
/// inspection operations target it, and `select` swaps a requested tagged
|
|
28
|
+
/// cursor into that position while preserving the pair.
|
|
29
|
+
///
|
|
30
|
+
/// A mark is a standalone cursor value used as an immutable positional
|
|
31
|
+
/// reference. It retains the cursor's offset, length, groups, flags, and tag,
|
|
32
|
+
/// but may carry a different `i`. A mark has no intrinsic boundary or movement
|
|
33
|
+
/// semantics; callers may later use its position for comparison, validation,
|
|
34
|
+
/// seeking, or another operation. Because it has the ordinary single-cursor
|
|
35
|
+
/// layout, existing positional decoding and absolute-position rules also apply
|
|
36
|
+
/// to marks. A zero mark identifies the empty cursor at position zero; `before`
|
|
37
|
+
/// therefore treats it as already reached.
|
|
38
|
+
///
|
|
39
|
+
/// The zero word represents an absent cursor.
|
|
40
|
+
library Cursors {
|
|
41
|
+
/// @dev A cursor position exceeds its logical length.
|
|
42
|
+
error OutOfBounds();
|
|
43
|
+
|
|
44
|
+
/// @dev Two optional group counts are both set but do not match.
|
|
45
|
+
error BadRatio();
|
|
46
|
+
|
|
47
|
+
/// @dev A cursor is not positioned at the expected offset.
|
|
48
|
+
error UnexpectedPosition();
|
|
49
|
+
|
|
50
|
+
/// @dev Paired cursors must have different identity tags.
|
|
51
|
+
error DuplicateTag(uint8 tag);
|
|
52
|
+
|
|
53
|
+
/// @dev Neither packed cursor matches the requested identity.
|
|
54
|
+
error MissingCursor();
|
|
55
|
+
|
|
56
|
+
// Creation and sources
|
|
57
|
+
|
|
58
|
+
/// @notice Create a cursor positioned at its beginning.
|
|
59
|
+
/// @param offset Absolute source or buffer offset.
|
|
60
|
+
/// @param len Logical byte length.
|
|
61
|
+
/// @param groups Logical group count.
|
|
62
|
+
/// @param flags Consumer-defined flags.
|
|
63
|
+
/// @param tag Cursor identity tag.
|
|
64
|
+
/// @return cur Packed cursor.
|
|
65
|
+
function create(uint offset, uint len, uint groups, uint8 flags, uint8 tag) internal pure returns (uint cur) {
|
|
66
|
+
cur |= max32(offset) << 32;
|
|
67
|
+
cur |= max32(len) << 64;
|
|
68
|
+
cur |= max16(groups) << 96;
|
|
69
|
+
cur |= uint(flags) << 112;
|
|
70
|
+
cur |= uint(tag) << 120;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// @notice Return the absolute calldata position where `source` begins.
|
|
74
|
+
/// @param source Calldata slice whose base is requested.
|
|
75
|
+
/// @return abs Absolute calldata position.
|
|
76
|
+
function base(bytes calldata source) internal pure returns (uint abs) {
|
|
77
|
+
assembly ("memory-safe") {
|
|
78
|
+
abs := source.offset
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// @notice Return the absolute calldata start and exclusive end of `source`.
|
|
83
|
+
/// @param source Calldata slice whose bounds are requested.
|
|
84
|
+
/// @return abs Absolute start position.
|
|
85
|
+
/// @return end Absolute exclusive end position.
|
|
86
|
+
function bounds(bytes calldata source) internal pure returns (uint abs, uint end) {
|
|
87
|
+
abs = base(source);
|
|
88
|
+
end = abs + source.length;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// @notice Create a cursor backed by a calldata slice.
|
|
92
|
+
/// @param source Calldata slice represented by the cursor.
|
|
93
|
+
/// @param flags Consumer-defined flags.
|
|
94
|
+
/// @param tag Cursor identity tag.
|
|
95
|
+
/// @return cur Packed cursor positioned at the slice beginning.
|
|
96
|
+
function wrap(bytes calldata source, uint8 flags, uint8 tag) internal pure returns (uint cur) {
|
|
97
|
+
cur = create(base(source), source.length, 0, flags, tag);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Inspection
|
|
101
|
+
|
|
102
|
+
/// @notice Decode the positional fields from the lower cursor.
|
|
103
|
+
/// @param cur Packed cursor or cursor pair.
|
|
104
|
+
/// @return i Current relative position.
|
|
105
|
+
/// @return offset Absolute base offset.
|
|
106
|
+
/// @return len Logical byte length.
|
|
107
|
+
function decode(uint cur) internal pure returns (uint i, uint offset, uint len) {
|
|
108
|
+
i = uint32(cur);
|
|
109
|
+
offset = uint32(cur >> 32);
|
|
110
|
+
len = uint32(cur >> 64);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// @notice Return the active cursor without its position.
|
|
114
|
+
/// @dev Ignores the upper cursor when `cur` is a pair.
|
|
115
|
+
/// @param cur Packed cursor or cursor pair.
|
|
116
|
+
/// @return The lower cursor's offset, length, groups, flags, and tag.
|
|
117
|
+
function frame(uint cur) internal pure returns (uint) {
|
|
118
|
+
return clear32(uint128(cur), 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// @notice Return the absolute position of the lower cursor as `offset + i`.
|
|
122
|
+
/// @dev Performs no bounds check.
|
|
123
|
+
/// @param cur Packed cursor or cursor pair.
|
|
124
|
+
/// @return Absolute current position.
|
|
125
|
+
function absolute(uint cur) internal pure returns (uint) {
|
|
126
|
+
return uint32(cur) + uint32(cur >> 32);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// @notice Decode the consumer metadata and identity tag from the lower cursor.
|
|
130
|
+
/// @param cur Packed cursor or cursor pair.
|
|
131
|
+
/// @return groups Logical group count.
|
|
132
|
+
/// @return flags Consumer-defined flags.
|
|
133
|
+
/// @return tag Cursor identity tag.
|
|
134
|
+
function meta(uint cur) internal pure returns (uint groups, uint8 flags, uint8 tag) {
|
|
135
|
+
groups = uint16(cur >> 96);
|
|
136
|
+
flags = uint8(cur >> 112);
|
|
137
|
+
tag = uint8(cur >> 120);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// @notice Return whether both packed cursors remain at their initial positions.
|
|
141
|
+
/// @param cur Packed cursor or cursor pair.
|
|
142
|
+
/// @return Whether both lane positions are zero.
|
|
143
|
+
function initial(uint cur) internal pure returns (bool) {
|
|
144
|
+
return uint32(cur) == 0 && uint32(cur >> 128) == 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// @notice Return whether the lower cursor's current position precedes its length.
|
|
148
|
+
/// @param cur Packed cursor or cursor pair.
|
|
149
|
+
/// @return Whether the lower cursor has bytes remaining.
|
|
150
|
+
function more(uint cur) internal pure returns (bool) {
|
|
151
|
+
return uint32(cur) < uint32(cur >> 64);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// @notice Return whether either packed cursor has remaining bytes.
|
|
155
|
+
/// @param cur Packed cursor or cursor pair.
|
|
156
|
+
/// @return Whether either lane has bytes remaining.
|
|
157
|
+
function any(uint cur) internal pure returns (bool) {
|
|
158
|
+
return more(cur) || more(cur >> 128);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// @notice Require the lower cursor to be positioned at `i`.
|
|
162
|
+
/// @param cur Packed cursor or cursor pair.
|
|
163
|
+
/// @param i Expected relative position.
|
|
164
|
+
function expect(uint cur, uint i) internal pure {
|
|
165
|
+
if (uint32(cur) != i) revert UnexpectedPosition();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// @notice Require the lower cursor to be positioned at absolute position `abs`.
|
|
169
|
+
/// @param cur Packed cursor or cursor pair.
|
|
170
|
+
/// @param abs Expected absolute position.
|
|
171
|
+
function expectAbs(uint cur, uint abs) internal pure {
|
|
172
|
+
if (absolute(cur) != abs) revert UnexpectedPosition();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// @notice Return whether the lower cursor contains `flag`.
|
|
176
|
+
/// @param cur Packed cursor or cursor pair.
|
|
177
|
+
/// @param flag Consumer-defined flag bit or bit set.
|
|
178
|
+
/// @return Whether any requested flag bit is present.
|
|
179
|
+
function flagged(uint cur, uint8 flag) internal pure returns (bool) {
|
|
180
|
+
return uint8(cur >> 112) & flag != 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Navigation
|
|
184
|
+
|
|
185
|
+
/// @dev Return `cur` after ensuring its lower position does not exceed its length.
|
|
186
|
+
/// @param cur Packed cursor or cursor pair.
|
|
187
|
+
/// @return Validated cursor unchanged.
|
|
188
|
+
function validate(uint cur) private pure returns (uint) {
|
|
189
|
+
if (uint32(cur) > uint32(cur >> 64)) revert OutOfBounds();
|
|
190
|
+
return cur;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// @notice Move the lower cursor forward to `i`.
|
|
194
|
+
/// @param cur Packed cursor or cursor pair.
|
|
195
|
+
/// @param i New relative position; must not move backward.
|
|
196
|
+
/// @return updated Cursor with the new lower position.
|
|
197
|
+
function seek(uint cur, uint i) internal pure returns (uint updated) {
|
|
198
|
+
uint current = uint32(cur);
|
|
199
|
+
uint len = uint32(cur >> 64);
|
|
200
|
+
if (i < current || i > len) revert OutOfBounds();
|
|
201
|
+
updated = clear32(cur, 0) | i;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// @notice Replace the lower cursor's position using an absolute position.
|
|
205
|
+
/// @param cur Packed cursor or cursor pair.
|
|
206
|
+
/// @param abs New absolute position.
|
|
207
|
+
/// @return updated Cursor with the corresponding relative position.
|
|
208
|
+
function seekAbs(uint cur, uint abs) internal pure returns (uint updated) {
|
|
209
|
+
uint offset = uint32(cur >> 32);
|
|
210
|
+
if (abs < offset) revert OutOfBounds();
|
|
211
|
+
updated = seek(cur, abs - offset);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// @notice Advance the current position of the lower cursor.
|
|
215
|
+
/// @param cur Packed cursor or cursor pair.
|
|
216
|
+
/// @param amount Number of bytes to advance.
|
|
217
|
+
/// @return updated Cursor advanced by `amount`.
|
|
218
|
+
function advance(uint cur, uint amount) internal pure returns (uint updated) {
|
|
219
|
+
uint i = uint32(validate(cur));
|
|
220
|
+
uint len = uint32(cur >> 64);
|
|
221
|
+
if (amount > len - i) revert OutOfBounds();
|
|
222
|
+
updated = cur + amount;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// @notice Replace the logical length of the lower cursor.
|
|
226
|
+
/// @dev A cursor cannot be resized below its current position.
|
|
227
|
+
/// @param cur Packed cursor or cursor pair.
|
|
228
|
+
/// @param len New logical length.
|
|
229
|
+
/// @return updated Cursor with the replaced length.
|
|
230
|
+
function resize(uint cur, uint len) internal pure returns (uint updated) {
|
|
231
|
+
if (uint32(cur) > max32(len)) revert OutOfBounds();
|
|
232
|
+
updated = clear32(cur, 64) | (len << 64);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// @notice Create a child cursor over `[start, end)` within the lower cursor.
|
|
236
|
+
/// @dev The child starts at position zero, has no groups, and uses the
|
|
237
|
+
/// supplied tag. Any higher cursor is omitted.
|
|
238
|
+
/// @param cur Parent cursor or cursor pair.
|
|
239
|
+
/// @param start Child start relative to the parent base.
|
|
240
|
+
/// @param end Child exclusive end relative to the parent base.
|
|
241
|
+
/// @param tag Child identity tag.
|
|
242
|
+
/// @return child Packed child cursor.
|
|
243
|
+
function slice(uint cur, uint start, uint end, uint8 tag) internal pure returns (uint child) {
|
|
244
|
+
(, uint offset, uint len) = decode(validate(cur));
|
|
245
|
+
if (start > end || end > len) revert OutOfBounds();
|
|
246
|
+
child = create(offset + start, end - start, 0, 0, tag);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Pairing and selection
|
|
250
|
+
|
|
251
|
+
/// @notice Reconcile both packed cursor lanes with an expected group count.
|
|
252
|
+
/// @dev Zero lanes and lanes with zero groups do not constrain the result.
|
|
253
|
+
/// @param cur Packed cursor or cursor pair.
|
|
254
|
+
/// @param expected Expected group count; zero accepts the encoded count.
|
|
255
|
+
/// @return groups Reconciled effective group count.
|
|
256
|
+
function reconcile(uint cur, uint expected) internal pure returns (uint groups) {
|
|
257
|
+
uint low = uint16(cur >> 96);
|
|
258
|
+
uint high = uint16(cur >> 224);
|
|
259
|
+
if (low != 0 && high != 0 && low != high) revert BadRatio();
|
|
260
|
+
|
|
261
|
+
groups = low != 0 ? low : high;
|
|
262
|
+
if (groups != 0 && expected != 0 && groups != expected) revert BadRatio();
|
|
263
|
+
if (groups == 0) groups = expected;
|
|
264
|
+
max16(groups);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// @notice Combine two cursors into one packed word.
|
|
268
|
+
/// @dev Zero represents absence and acts as the identity value.
|
|
269
|
+
/// @param low Cursor placed in the lower lane.
|
|
270
|
+
/// @param high Cursor placed in the higher lane.
|
|
271
|
+
/// @return cur Packed cursor pair, or the nonzero cursor when one is absent.
|
|
272
|
+
function pair(uint low, uint high) internal pure returns (uint cur) {
|
|
273
|
+
low = validate(max128(low));
|
|
274
|
+
high = validate(max128(high));
|
|
275
|
+
|
|
276
|
+
if (low == 0) return high;
|
|
277
|
+
if (high == 0) return low;
|
|
278
|
+
|
|
279
|
+
uint8 tag = uint8(low >> 120);
|
|
280
|
+
if (tag == uint8(high >> 120)) revert DuplicateTag(tag);
|
|
281
|
+
|
|
282
|
+
cur = low | (high << 128);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/// @notice Swap the lower and higher cursors.
|
|
286
|
+
/// @param cur Packed cursor pair.
|
|
287
|
+
/// @return updated Pair with its lanes exchanged.
|
|
288
|
+
function swap(uint cur) internal pure returns (uint updated) {
|
|
289
|
+
updated = (cur << 128) | (cur >> 128);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// @notice Return whether either packed cursor has the nonzero `expected` tag.
|
|
293
|
+
/// @dev Tag zero denotes an untagged cursor and is never matched by this helper.
|
|
294
|
+
/// @param cur Packed cursor or cursor pair.
|
|
295
|
+
/// @param expected Nonzero identity tag to find.
|
|
296
|
+
/// @return Whether either lane has the requested tag.
|
|
297
|
+
function contains(uint cur, uint8 expected) internal pure returns (bool) {
|
|
298
|
+
return expected != 0 && (uint8(cur >> 120) == expected || uint8(cur >> 248) == expected);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// @notice Move the cursor with `expected` into the lower position.
|
|
302
|
+
/// @dev If both cursors share the tag, the existing lower cursor is retained.
|
|
303
|
+
/// @param cur Packed cursor or cursor pair.
|
|
304
|
+
/// @param expected Identity tag to select.
|
|
305
|
+
/// @return updated Cursor word with the selected lane in the lower position.
|
|
306
|
+
function select(uint cur, uint8 expected) internal pure returns (uint updated) {
|
|
307
|
+
if (uint8(cur >> 120) == expected) return cur;
|
|
308
|
+
|
|
309
|
+
updated = swap(cur);
|
|
310
|
+
if (uint8(updated >> 120) != expected) revert MissingCursor();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Marks
|
|
314
|
+
|
|
315
|
+
/// @notice Select the live cursor whose frame matches the active cursor in `mark`.
|
|
316
|
+
/// @dev Only the lower 128 bits of `mark` are considered. The returned value
|
|
317
|
+
/// preserves the cursor pair and places the matching cursor in the lower half.
|
|
318
|
+
/// A zero frame may select an empty cursor lane.
|
|
319
|
+
/// @param cur Packed cursor or cursor pair to search.
|
|
320
|
+
/// @param mark Cursor-shaped positional reference to match.
|
|
321
|
+
/// @return located Cursor pair with the matching cursor active.
|
|
322
|
+
function locate(uint cur, uint mark) internal pure returns (uint located) {
|
|
323
|
+
uint expected = frame(mark);
|
|
324
|
+
if (frame(cur) == expected) return cur;
|
|
325
|
+
|
|
326
|
+
located = swap(cur);
|
|
327
|
+
if (frame(located) != expected) revert MissingCursor();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/// @notice Return whether a matched cursor is positioned before `mark`.
|
|
331
|
+
/// @dev Returns false at the mark and reverts after it. Only the active lower
|
|
332
|
+
/// cursor in `mark` participates in the comparison. A zero mark represents
|
|
333
|
+
/// the already-reached position of an empty cursor.
|
|
334
|
+
/// @param cur Packed cursor or cursor pair containing the marked cursor.
|
|
335
|
+
/// @param mark Cursor-shaped positional reference.
|
|
336
|
+
/// @return Whether the live cursor position precedes the marked position.
|
|
337
|
+
function before(uint cur, uint mark) internal pure returns (bool) {
|
|
338
|
+
uint i = uint32(locate(cur, mark));
|
|
339
|
+
uint target = uint32(mark);
|
|
340
|
+
if (i > target) revert OutOfBounds();
|
|
341
|
+
return i < target;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Consumption
|
|
345
|
+
|
|
346
|
+
/// @notice Return a cursor's absolute position and advance it.
|
|
347
|
+
/// @dev Intended for fixed-width consumers that know their complete encoded size.
|
|
348
|
+
/// @param cur Packed cursor or cursor pair.
|
|
349
|
+
/// @param amount Number of bytes to consume.
|
|
350
|
+
/// @return updated Cursor advanced by `amount`.
|
|
351
|
+
/// @return abs Absolute pre-advance position.
|
|
352
|
+
function consume(uint cur, uint amount) internal pure returns (uint updated, uint abs) {
|
|
353
|
+
abs = Cursors.absolute(cur);
|
|
354
|
+
updated = advance(cur, amount);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// @notice Select a tagged cursor, return its absolute position, and advance it.
|
|
358
|
+
/// @param cur Packed cursor or cursor pair.
|
|
359
|
+
/// @param tag Identity tag to select.
|
|
360
|
+
/// @param amount Number of bytes to consume.
|
|
361
|
+
/// @return updated Cursor pair with the selected lane advanced.
|
|
362
|
+
/// @return abs Absolute pre-advance position in the selected lane.
|
|
363
|
+
function consume(uint cur, uint8 tag, uint amount) internal pure returns (uint updated, uint abs) {
|
|
364
|
+
updated = select(cur, tag);
|
|
365
|
+
(updated, abs) = consume(updated, amount);
|
|
366
|
+
}
|
|
367
|
+
}
|
package/utils/Lanes.sol
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
/// @notice Conventional identifiers shared by endpoint execution lanes.
|
|
5
|
+
library Lanes {
|
|
6
|
+
/// @dev Decoder lane containing endpoint input blocks.
|
|
7
|
+
uint8 internal constant Input = 1;
|
|
8
|
+
/// @dev Decoder lane containing command state blocks.
|
|
9
|
+
uint8 internal constant State = 2;
|
|
10
|
+
/// @dev Writer lane containing regular endpoint output blocks.
|
|
11
|
+
uint8 internal constant Output = 3;
|
|
12
|
+
/// @dev Writer lane containing deferred transaction blocks.
|
|
13
|
+
uint8 internal constant Transactions = 4;
|
|
14
|
+
}
|
package/utils/Layout.sol
CHANGED
|
@@ -40,8 +40,6 @@ library Layout {
|
|
|
40
40
|
|
|
41
41
|
/// @dev Admin account — chain-local, backed by an EVM address.
|
|
42
42
|
uint8 constant Admin = 0x01;
|
|
43
|
-
/// @dev Guardian account — chain-local, backed by an EVM address.
|
|
44
|
-
uint8 constant Guardian = 0x02;
|
|
45
43
|
/// @dev User account — chain-agnostic, backed by an EVM address.
|
|
46
44
|
uint8 constant User = 0x03;
|
|
47
45
|
// -------------------------------------------------------------------------
|
package/utils/Selectors.sol
CHANGED
|
@@ -6,9 +6,9 @@ pragma solidity ^0.8.33;
|
|
|
6
6
|
library Selectors {
|
|
7
7
|
/// @notice Derive the ABI selector for a command entrypoint.
|
|
8
8
|
/// @param name Command function name.
|
|
9
|
-
/// @return Selector for `name(
|
|
9
|
+
/// @return Selector for `name(bytes32,bytes,bytes)`.
|
|
10
10
|
function command(string memory name) internal pure returns (bytes4) {
|
|
11
|
-
return derive(name, "(
|
|
11
|
+
return derive(name, "(bytes32,bytes,bytes)");
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/// @notice Derive the ABI selector for a port entrypoint.
|