@rootzero/contracts 1.4.0 → 1.5.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 +24 -0
- package/Endpoints.sol +4 -3
- package/Events.sol +1 -1
- package/README.md +58 -31
- package/Utils.sol +4 -3
- package/blocks/Cursors.sol +83 -143
- package/blocks/Keys.sol +15 -15
- package/blocks/Schema.sol +27 -28
- package/blocks/Writers.sol +26 -33
- package/commands/Base.sol +2 -2
- package/commands/Burn.sol +3 -4
- package/commands/Credit.sol +3 -4
- package/commands/Debit.sol +4 -5
- package/commands/Deposit.sol +8 -10
- package/commands/Payout.sol +3 -6
- package/commands/Withdraw.sol +3 -4
- package/commands/admin/AllowAssets.sol +5 -6
- package/commands/admin/Allowance.sol +3 -4
- package/commands/admin/DenyAssets.sol +5 -6
- package/commands/admin/Execute.sol +2 -2
- package/core/Access.sol +2 -2
- package/core/Balances.sol +10 -11
- package/core/Calls.sol +7 -7
- package/core/Host.sol +2 -2
- package/core/Runtime.sol +3 -3
- package/core/Types.sol +0 -14
- package/docs/Schema.md +29 -10
- package/events/Asset.sol +17 -3
- package/events/Balance.sol +2 -3
- package/events/Locked.sol +2 -3
- package/events/Position.sol +2 -3
- package/events/Received.sol +2 -3
- package/events/Spent.sol +2 -3
- package/events/Unlocked.sol +2 -3
- package/guards/Base.sol +4 -4
- package/package.json +1 -1
- package/peer/AllowAssets.sol +3 -3
- package/peer/Allowance.sol +2 -2
- package/peer/Base.sol +4 -4
- package/peer/Credit.sol +10 -10
- package/peer/Debit.sol +10 -10
- package/peer/DenyAssets.sol +3 -3
- package/peer/Recover.sol +51 -0
- package/peer/Redeem.sol +48 -0
- package/peer/Settle.sol +3 -3
- package/queries/Assets.sol +7 -8
- package/queries/Balances.sol +8 -9
- package/queries/Base.sol +4 -4
- package/queries/Positions.sol +4 -6
- package/utils/Accounts.sol +76 -58
- package/utils/Assets.sol +55 -115
- package/utils/Ids.sol +33 -233
- package/utils/Layout.sol +11 -17
- package/utils/Nodes.sol +263 -0
- package/utils/Utils.sol +9 -24
- package/peer/BalancePull.sol +0 -49
package/utils/Ids.sol
CHANGED
|
@@ -1,250 +1,50 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {Layout} from "./Layout.sol";
|
|
5
|
-
import {isLocalFamily, matchesBase, toLocalBase} from "./Utils.sol";
|
|
6
|
-
|
|
7
4
|
/// @title Ids
|
|
8
|
-
/// @notice
|
|
5
|
+
/// @notice Shared helpers for the protocol-wide 256-bit ID convention.
|
|
6
|
+
///
|
|
7
|
+
/// IDs whose first byte is zero are opaque:
|
|
8
|
+
/// `[0x00][bytes31 truncated hash]`
|
|
9
9
|
///
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
/// - bits [223:192] — current `block.chainid` (makes IDs chain-local)
|
|
13
|
-
/// - bits [191:160] — 4-byte ABI selector (commands and peers only)
|
|
14
|
-
/// - bits [159:0] — 160-bit EVM contract address
|
|
10
|
+
/// Opaque keccak preimages start with `0x01`, and the ID is derived as:
|
|
11
|
+
/// `0x00 || bytes31(keccak256(preimage))`
|
|
15
12
|
library Ids {
|
|
16
|
-
/// @dev Thrown when an ID does not match the expected
|
|
13
|
+
/// @dev Thrown when an ID does not match the expected convention.
|
|
17
14
|
error InvalidId();
|
|
15
|
+
/// @dev Thrown when an opaque ID preimage is missing or uses an unsupported format/hash tag.
|
|
16
|
+
error InvalidPreimage();
|
|
18
17
|
|
|
19
|
-
/// @dev
|
|
20
|
-
|
|
21
|
-
/// @dev Full 4-byte type prefix for chain/domain nodes.
|
|
22
|
-
uint32 constant Chain = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Chain);
|
|
23
|
-
/// @dev Full 4-byte type prefix for host nodes.
|
|
24
|
-
uint32 constant Host = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
|
|
25
|
-
/// @dev Full 4-byte type prefix for command nodes.
|
|
26
|
-
uint32 constant Command = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
|
|
27
|
-
/// @dev Full 4-byte type prefix for peer nodes.
|
|
28
|
-
uint32 constant Peer = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Peer);
|
|
29
|
-
/// @dev Full 4-byte type prefix for query nodes.
|
|
30
|
-
uint32 constant Query = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Query);
|
|
31
|
-
/// @dev Full 4-byte type prefix for guard action nodes.
|
|
32
|
-
uint32 constant Guard = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Guard);
|
|
33
|
-
|
|
34
|
-
/// @notice Return true if `id` is a host node ID.
|
|
35
|
-
function isHost(uint id) internal pure returns (bool) {
|
|
36
|
-
return uint32(id >> 224) == Host;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/// @notice Return true if `id` is a command node ID.
|
|
40
|
-
function isCommand(uint id) internal pure returns (bool) {
|
|
41
|
-
return uint32(id >> 224) == Command;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/// @notice Return true if `id` is a peer node ID.
|
|
45
|
-
function isPeer(uint id) internal pure returns (bool) {
|
|
46
|
-
return uint32(id >> 224) == Peer;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/// @notice Return true if `id` is a query node ID.
|
|
50
|
-
function isQuery(uint id) internal pure returns (bool) {
|
|
51
|
-
return uint32(id >> 224) == Query;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/// @notice Return true if `id` is a guard action node ID.
|
|
55
|
-
function isGuard(uint id) internal pure returns (bool) {
|
|
56
|
-
return uint32(id >> 224) == Guard;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/// @notice Return true if `id` is a local node ID for this contract.
|
|
60
|
-
function isLocalNode(uint id) internal view returns (bool) {
|
|
61
|
-
return uint32(id >> 224) != Chain && isLocalFamily(id, Node) && address(uint160(id)) == address(this);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// @notice Assert that `id` is a command ID and return it unchanged.
|
|
65
|
-
/// @param id Node ID to validate.
|
|
66
|
-
/// @return cid The same `id` value if it is a command.
|
|
67
|
-
function command(uint id) internal pure returns (uint cid) {
|
|
68
|
-
if (!isCommand(id)) revert InvalidId();
|
|
69
|
-
return id;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/// @notice Assert that `id` is a peer ID and return it unchanged.
|
|
73
|
-
/// @param id Node ID to validate.
|
|
74
|
-
/// @return pid The same `id` value if it is a peer.
|
|
75
|
-
function peer(uint id) internal pure returns (uint pid) {
|
|
76
|
-
if (!isPeer(id)) revert InvalidId();
|
|
77
|
-
return id;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/// @notice Assert that `id` is a query ID and return it unchanged.
|
|
81
|
-
/// @param id Node ID to validate.
|
|
82
|
-
/// @return queryId The same `id` value if it is a query.
|
|
83
|
-
function query(uint id) internal pure returns (uint queryId) {
|
|
84
|
-
if (!isQuery(id)) revert InvalidId();
|
|
85
|
-
return id;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/// @notice Assert that `id` is a guard action ID and return it unchanged.
|
|
89
|
-
/// @param id Node ID to validate.
|
|
90
|
-
/// @return guardId The same `id` value if it is a guard action.
|
|
91
|
-
function guard(uint id) internal pure returns (uint guardId) {
|
|
92
|
-
if (!isGuard(id)) revert InvalidId();
|
|
93
|
-
return id;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/// @notice Assert that `id` is a command ID and return its embedded ABI selector.
|
|
97
|
-
/// @param id Node ID to validate.
|
|
98
|
-
/// @return selector 4-byte command selector stored in bits [191:160].
|
|
99
|
-
function commandSelector(uint id) internal pure returns (bytes4 selector) {
|
|
100
|
-
return bytes4(uint32(command(id) >> 160));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/// @notice Assert that `id` is a peer ID and return its embedded ABI selector.
|
|
104
|
-
/// @param id Node ID to validate.
|
|
105
|
-
/// @return selector 4-byte peer selector stored in bits [191:160].
|
|
106
|
-
function peerSelector(uint id) internal pure returns (bytes4 selector) {
|
|
107
|
-
return bytes4(uint32(peer(id) >> 160));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/// @notice Assert that `id` is a query ID and return its embedded ABI selector.
|
|
111
|
-
/// @param id Node ID to validate.
|
|
112
|
-
/// @return selector 4-byte query selector stored in bits [191:160].
|
|
113
|
-
function querySelector(uint id) internal pure returns (bytes4 selector) {
|
|
114
|
-
return bytes4(uint32(query(id) >> 160));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/// @notice Assert that `id` is a guard action ID and return its embedded ABI selector.
|
|
118
|
-
/// @param id Node ID to validate.
|
|
119
|
-
/// @return selector 4-byte guard selector stored in bits [191:160].
|
|
120
|
-
function guardSelector(uint id) internal pure returns (bytes4 selector) {
|
|
121
|
-
return bytes4(uint32(guard(id) >> 160));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/// @notice Assert that `id` is the host ID of `addr` on the current chain.
|
|
125
|
-
/// @param id Node ID to validate.
|
|
126
|
-
/// @param addr Expected host contract address.
|
|
127
|
-
/// @return hid The same `id` value if it matches `addr`.
|
|
128
|
-
function matchHost(uint id, address addr) internal view returns (uint hid) {
|
|
129
|
-
if (id != toHost(addr)) revert InvalidId();
|
|
130
|
-
return id;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/// @notice Build the chain node ID for the current chain.
|
|
134
|
-
/// @return Chain node ID with the Chain prefix and `block.chainid` payload.
|
|
135
|
-
function localChain() internal view returns (uint) {
|
|
136
|
-
uint id = block.chainid;
|
|
137
|
-
if (id >> 224 != 0) revert InvalidId();
|
|
138
|
-
return (uint(Chain) << 224) | id;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/// @notice Build a chain-local host ID for `target`.
|
|
142
|
-
/// @param target Host contract address.
|
|
143
|
-
/// @return Host node ID on the current chain.
|
|
144
|
-
function toHost(address target) internal view returns (uint) {
|
|
145
|
-
return toLocalBase(Host) | uint(uint160(target));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/// @notice Build a chain-local command ID for the given selector and contract.
|
|
149
|
-
/// @param selector 4-byte ABI selector of the command entry point.
|
|
150
|
-
/// @param target Command contract address.
|
|
151
|
-
/// @return Command node ID embedding both the selector and address.
|
|
152
|
-
function toCommand(bytes4 selector, address target) internal view returns (uint) {
|
|
153
|
-
uint id = toLocalBase(Command) | uint(uint160(target));
|
|
154
|
-
id |= uint(uint32(selector)) << 160;
|
|
155
|
-
return id;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/// @notice Build a chain-local peer ID for the given selector and contract.
|
|
159
|
-
/// @param selector 4-byte ABI selector of the peer entry point.
|
|
160
|
-
/// @param target Peer contract address.
|
|
161
|
-
/// @return Peer node ID embedding both the selector and address.
|
|
162
|
-
function toPeer(bytes4 selector, address target) internal view returns (uint) {
|
|
163
|
-
uint id = toLocalBase(Peer) | uint(uint160(target));
|
|
164
|
-
id |= uint(uint32(selector)) << 160;
|
|
165
|
-
return id;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/// @notice Build a chain-local query ID for the given selector and contract.
|
|
169
|
-
/// @param selector 4-byte ABI selector of the query entry point.
|
|
170
|
-
/// @param target Query contract address.
|
|
171
|
-
/// @return Query node ID embedding both the selector and address.
|
|
172
|
-
function toQuery(bytes4 selector, address target) internal view returns (uint) {
|
|
173
|
-
uint id = toLocalBase(Query) | uint(uint160(target));
|
|
174
|
-
id |= uint(uint32(selector)) << 160;
|
|
175
|
-
return id;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/// @notice Build a chain-local guard action ID for the given selector and contract.
|
|
179
|
-
/// @param selector 4-byte ABI selector of the guard action entry point.
|
|
180
|
-
/// @param target Guard action contract address.
|
|
181
|
-
/// @return Guard action node ID embedding both the selector and address.
|
|
182
|
-
function toGuard(bytes4 selector, address target) internal view returns (uint) {
|
|
183
|
-
uint id = toLocalBase(Guard) | uint(uint160(target));
|
|
184
|
-
id |= uint(uint32(selector)) << 160;
|
|
185
|
-
return id;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/// @notice Extract the contract address from any local node ID.
|
|
189
|
-
/// Reverts if `id` does not belong to the local node family.
|
|
190
|
-
/// @param id Node ID (host, command, or peer).
|
|
191
|
-
/// @return Contract address in the lower 160 bits of `id`.
|
|
192
|
-
function nodeAddr(uint id) internal view returns (address) {
|
|
193
|
-
if (uint32(id >> 224) == Chain || !isLocalFamily(id, Node)) revert InvalidId();
|
|
194
|
-
return address(uint160(id));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/// @notice Extract the contract address from a local host ID.
|
|
198
|
-
/// Reverts if `id` does not match the local host base.
|
|
199
|
-
/// @param id Host node ID.
|
|
200
|
-
/// @return Host contract address in the lower 160 bits of `id`.
|
|
201
|
-
function hostAddr(uint id) internal view returns (address) {
|
|
202
|
-
if (!matchesBase(bytes32(id), toLocalBase(Host))) revert InvalidId();
|
|
203
|
-
return address(uint160(id));
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/// @title Selectors
|
|
208
|
-
/// @notice ABI-selector derivation helpers for command, peer, and query dispatch.
|
|
209
|
-
library Selectors {
|
|
210
|
-
/// @dev ABI argument encoding for command entry points: `((bytes32,bytes,bytes))`.
|
|
211
|
-
string constant CommandArgs = "((bytes32,bytes,bytes))";
|
|
212
|
-
/// @dev ABI argument encoding for peer entry points: `(bytes)`.
|
|
213
|
-
string constant PeerArgs = "(bytes)";
|
|
214
|
-
/// @dev ABI argument encoding for query entry points: `(bytes)`.
|
|
215
|
-
string constant QueryArgs = "(bytes)";
|
|
216
|
-
/// @dev ABI argument encoding for guard action entry points: `(bytes)`.
|
|
217
|
-
string constant GuardArgs = "(bytes)";
|
|
18
|
+
/// @dev Preimage format/hash tag for keccak256-derived opaque IDs.
|
|
19
|
+
uint8 constant Keccak = 0x01;
|
|
218
20
|
|
|
219
|
-
/// @notice
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
/// @return 4-byte selector.
|
|
223
|
-
function command(string memory name) internal pure returns (bytes4) {
|
|
224
|
-
return bytes4(keccak256(bytes.concat(bytes(name), bytes(CommandArgs))));
|
|
21
|
+
/// @notice Return true if `id` is opaque.
|
|
22
|
+
function isOpaque(bytes32 id) internal pure returns (bool) {
|
|
23
|
+
return uint8(uint(id) >> 248) == 0;
|
|
225
24
|
}
|
|
226
25
|
|
|
227
|
-
/// @notice
|
|
228
|
-
///
|
|
229
|
-
/// @
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return
|
|
26
|
+
/// @notice Assert that `value` is opaque and return it unchanged.
|
|
27
|
+
/// @param value ID to validate.
|
|
28
|
+
/// @return id The same `value` if it is opaque.
|
|
29
|
+
function opaque(bytes32 value) internal pure returns (bytes32 id) {
|
|
30
|
+
if (!isOpaque(value)) revert InvalidId();
|
|
31
|
+
return value;
|
|
233
32
|
}
|
|
234
33
|
|
|
235
|
-
/// @notice Derive
|
|
236
|
-
///
|
|
237
|
-
/// @
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return
|
|
34
|
+
/// @notice Derive an opaque ID from a keccak preimage.
|
|
35
|
+
/// @param preimage Preimage whose first byte is `0x01`.
|
|
36
|
+
/// @return id `0x00 || bytes31(keccak256(preimage))`.
|
|
37
|
+
function toKeccak(bytes memory preimage) internal pure returns (bytes32 id) {
|
|
38
|
+
if (preimage.length == 0 || uint8(preimage[0]) != Keccak) revert InvalidPreimage();
|
|
39
|
+
return bytes32(uint(keccak256(preimage)) >> 8);
|
|
241
40
|
}
|
|
242
41
|
|
|
243
|
-
/// @notice
|
|
244
|
-
///
|
|
245
|
-
/// @param
|
|
246
|
-
/// @return
|
|
247
|
-
function
|
|
248
|
-
|
|
42
|
+
/// @notice Assert that `value` matches the opaque keccak ID for `preimage`.
|
|
43
|
+
/// @param value Opaque ID to validate.
|
|
44
|
+
/// @param preimage Preimage whose first byte is `0x01`.
|
|
45
|
+
/// @return id The same `value` if it matches.
|
|
46
|
+
function matchKeccak(bytes32 value, bytes memory preimage) internal pure returns (bytes32 id) {
|
|
47
|
+
if (value != toKeccak(preimage)) revert InvalidId();
|
|
48
|
+
return value;
|
|
249
49
|
}
|
|
250
50
|
}
|
package/utils/Layout.sol
CHANGED
|
@@ -7,23 +7,21 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
///
|
|
8
8
|
/// IDs are structured as:
|
|
9
9
|
/// `[uint32 type][uint32 chainid][192-bit payload]`
|
|
10
|
-
/// where `type` is `[
|
|
10
|
+
/// where `type` is `[uint16 representation][uint8 category][uint8 subtype]`.
|
|
11
|
+
///
|
|
12
|
+
/// Values whose first byte is zero are opaque IDs:
|
|
13
|
+
/// `[0x00][bytes31 truncated hash]`
|
|
14
|
+
/// They require lookup or witness data when the native preimage is needed.
|
|
15
|
+
/// Opaque preimages start with a one-byte format/hash tag. `0x01` means the ID
|
|
16
|
+
/// is `0x00 || bytes31(keccak256(preimage))`; remaining bytes are host/domain-specific.
|
|
17
|
+
/// Values whose first byte is nonzero follow the structured layout above.
|
|
11
18
|
library Layout {
|
|
12
19
|
// -------------------------------------------------------------------------
|
|
13
|
-
//
|
|
20
|
+
// Representation tags (top 2 bytes of the ID type field)
|
|
14
21
|
// -------------------------------------------------------------------------
|
|
15
22
|
|
|
16
|
-
/// @dev EVM-compatible
|
|
17
|
-
|
|
18
|
-
/// @dev 32-byte ID; no paired metadata word is required.
|
|
19
|
-
uint8 constant Width32 = 0x20;
|
|
20
|
-
/// @dev 64-byte ID; a paired metadata word completes the identity.
|
|
21
|
-
uint8 constant Width64 = 0x40;
|
|
22
|
-
|
|
23
|
-
/// @dev 32-byte EVM-compatible value; lower 20 bytes hold an address.
|
|
24
|
-
uint16 constant Evm32 = (uint16(Evm) << 8) | uint16(Width32);
|
|
25
|
-
/// @dev 64-byte EVM-compatible value; used when a paired metadata word completes the identity.
|
|
26
|
-
uint16 constant Evm64 = (uint16(Evm) << 8) | uint16(Width64);
|
|
23
|
+
/// @dev EVM-compatible ID; lower 20 payload bytes hold an address when present.
|
|
24
|
+
uint16 constant Evm = 0x0120;
|
|
27
25
|
|
|
28
26
|
// -------------------------------------------------------------------------
|
|
29
27
|
// Category tags (uint8, third byte of the ID type field)
|
|
@@ -71,8 +69,4 @@ library Layout {
|
|
|
71
69
|
uint8 constant Native = 0x01;
|
|
72
70
|
/// @dev ERC-20 fungible token; lower 20 bytes of the ID hold the contract address.
|
|
73
71
|
uint8 constant Erc20 = 0x02;
|
|
74
|
-
/// @dev ERC-721 non-fungible token; lower 20 bytes of the ID hold the collection address.
|
|
75
|
-
uint8 constant Erc721 = 0x03;
|
|
76
|
-
/// @dev ERC-1155 multi-token asset; lower 20 bytes of the ID hold the collection address.
|
|
77
|
-
uint8 constant Erc1155 = 0x04;
|
|
78
72
|
}
|
package/utils/Nodes.sol
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {Layout} from "./Layout.sol";
|
|
5
|
+
import {Ids} from "./Ids.sol";
|
|
6
|
+
import {ensureAddr, isFamily, matchesBase, toLocalBase} from "./Utils.sol";
|
|
7
|
+
|
|
8
|
+
/// @title Nodes
|
|
9
|
+
/// @notice Encoding and decoding helpers for 256-bit node identifiers.
|
|
10
|
+
///
|
|
11
|
+
/// Node IDs share a common layout:
|
|
12
|
+
/// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `Peer`)
|
|
13
|
+
/// - bits [223:192] — current `block.chainid` (makes IDs chain-local)
|
|
14
|
+
/// - bits [191:160] — 4-byte ABI selector (commands and peers only)
|
|
15
|
+
/// - bits [159:0] — 160-bit EVM contract address
|
|
16
|
+
///
|
|
17
|
+
/// If the first byte is zero, the node is an opaque
|
|
18
|
+
/// `0x00 || bytes31(hash)` ID. The callable target must be resolved by lookup
|
|
19
|
+
/// or witness data before dispatch.
|
|
20
|
+
///
|
|
21
|
+
/// The helpers in this library validate and deconstruct structured node IDs.
|
|
22
|
+
library Nodes {
|
|
23
|
+
/// @dev Thrown when an ID does not match the expected node type or chain.
|
|
24
|
+
error InvalidId();
|
|
25
|
+
|
|
26
|
+
/// @dev 24-bit family tag shared by all node types (Evm + Node category).
|
|
27
|
+
uint24 constant Family = (uint24(Layout.Evm) << 8) | uint24(Layout.Node);
|
|
28
|
+
/// @dev Full 4-byte type prefix for chain/domain nodes.
|
|
29
|
+
uint32 constant Chain = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Chain);
|
|
30
|
+
/// @dev Full 4-byte type prefix for host nodes.
|
|
31
|
+
uint32 constant Host = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
|
|
32
|
+
/// @dev Full 4-byte type prefix for command nodes.
|
|
33
|
+
uint32 constant Command = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
|
|
34
|
+
/// @dev Full 4-byte type prefix for peer nodes.
|
|
35
|
+
uint32 constant Peer = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Peer);
|
|
36
|
+
/// @dev Full 4-byte type prefix for query nodes.
|
|
37
|
+
uint32 constant Query = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Query);
|
|
38
|
+
/// @dev Full 4-byte type prefix for guard action nodes.
|
|
39
|
+
uint32 constant Guard = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Guard);
|
|
40
|
+
|
|
41
|
+
/// @notice Return true if `node` is a host node ID.
|
|
42
|
+
function isHost(uint node) internal pure returns (bool) {
|
|
43
|
+
return uint32(node >> 224) == Host;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @notice Return true if `node` is a command node ID.
|
|
47
|
+
function isCommand(uint node) internal pure returns (bool) {
|
|
48
|
+
return uint32(node >> 224) == Command;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// @notice Return true if `node` is a peer node ID.
|
|
52
|
+
function isPeer(uint node) internal pure returns (bool) {
|
|
53
|
+
return uint32(node >> 224) == Peer;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// @notice Return true if `node` is a query node ID.
|
|
57
|
+
function isQuery(uint node) internal pure returns (bool) {
|
|
58
|
+
return uint32(node >> 224) == Query;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @notice Return true if `node` is a guard action node ID.
|
|
62
|
+
function isGuard(uint node) internal pure returns (bool) {
|
|
63
|
+
return uint32(node >> 224) == Guard;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @notice Return true if `node` belongs to the EVM node family.
|
|
67
|
+
function isEvm(uint node) internal pure returns (bool) {
|
|
68
|
+
return isFamily(node, Family);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// @notice Return true if `node` is opaque.
|
|
72
|
+
function isOpaque(uint node) internal pure returns (bool) {
|
|
73
|
+
return Ids.isOpaque(bytes32(node));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// @notice Return true if `node` belongs to the EVM node family on the current chain.
|
|
77
|
+
function isLocal(uint node) internal view returns (bool) {
|
|
78
|
+
return isEvm(node) && uint32(node >> 192) == block.chainid;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// @notice Assert that `value` is a host node ID and return it as a node.
|
|
82
|
+
/// @param value Value to validate.
|
|
83
|
+
/// @return node The same `value` if it is a host node.
|
|
84
|
+
function host(uint value) internal pure returns (uint node) {
|
|
85
|
+
if (!isHost(value)) revert InvalidId();
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @notice Assert that `value` is a command node ID and return it as a node.
|
|
90
|
+
/// @param value Value to validate.
|
|
91
|
+
/// @return node The same `value` if it is a command node.
|
|
92
|
+
function command(uint value) internal pure returns (uint node) {
|
|
93
|
+
if (!isCommand(value)) revert InvalidId();
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @notice Assert that `value` is a peer node ID and return it as a node.
|
|
98
|
+
/// @param value Value to validate.
|
|
99
|
+
/// @return node The same `value` if it is a peer node.
|
|
100
|
+
function peer(uint value) internal pure returns (uint node) {
|
|
101
|
+
if (!isPeer(value)) revert InvalidId();
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// @notice Assert that `value` is a query node ID and return it as a node.
|
|
106
|
+
/// @param value Value to validate.
|
|
107
|
+
/// @return node The same `value` if it is a query node.
|
|
108
|
+
function query(uint value) internal pure returns (uint node) {
|
|
109
|
+
if (!isQuery(value)) revert InvalidId();
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// @notice Assert that `value` is a guard action node ID and return it as a node.
|
|
114
|
+
/// @param value Value to validate.
|
|
115
|
+
/// @return node The same `value` if it is a guard action node.
|
|
116
|
+
function guard(uint value) internal pure returns (uint node) {
|
|
117
|
+
if (!isGuard(value)) revert InvalidId();
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// @notice Assert that `value` belongs to the EVM node family and return it as a node.
|
|
122
|
+
/// @param value Value to validate.
|
|
123
|
+
/// @return node The same `value` if it is an EVM node.
|
|
124
|
+
function evm(uint value) internal pure returns (uint node) {
|
|
125
|
+
if (!isEvm(value)) revert InvalidId();
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// @notice Assert that `value` is an opaque node and return it unchanged.
|
|
130
|
+
/// @param value Node ID to validate.
|
|
131
|
+
/// @return node The same `value` if it is opaque.
|
|
132
|
+
function opaque(uint value) internal pure returns (uint node) {
|
|
133
|
+
if (!Ids.isOpaque(bytes32(value))) revert InvalidId();
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// @notice Assert that `value` belongs to the EVM node family on the current chain and return it as a node.
|
|
138
|
+
/// @param value Value to validate.
|
|
139
|
+
/// @return node The same `value` if it is local.
|
|
140
|
+
function local(uint value) internal view returns (uint node) {
|
|
141
|
+
if (!isLocal(value)) revert InvalidId();
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// @notice Assert that `node` is a command ID and return its embedded ABI selector.
|
|
146
|
+
/// @param node Node ID to validate.
|
|
147
|
+
/// @return selector 4-byte command selector stored in bits [191:160].
|
|
148
|
+
function commandSelector(uint node) internal pure returns (bytes4 selector) {
|
|
149
|
+
return bytes4(uint32(command(node) >> 160));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// @notice Assert that `node` is a peer ID and return its embedded ABI selector.
|
|
153
|
+
/// @param node Node ID to validate.
|
|
154
|
+
/// @return selector 4-byte peer selector stored in bits [191:160].
|
|
155
|
+
function peerSelector(uint node) internal pure returns (bytes4 selector) {
|
|
156
|
+
return bytes4(uint32(peer(node) >> 160));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// @notice Assert that `node` is a query ID and return its embedded ABI selector.
|
|
160
|
+
/// @param node Node ID to validate.
|
|
161
|
+
/// @return selector 4-byte query selector stored in bits [191:160].
|
|
162
|
+
function querySelector(uint node) internal pure returns (bytes4 selector) {
|
|
163
|
+
return bytes4(uint32(query(node) >> 160));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// @notice Assert that `node` is a guard action ID and return its embedded ABI selector.
|
|
167
|
+
/// @param node Node ID to validate.
|
|
168
|
+
/// @return selector 4-byte guard selector stored in bits [191:160].
|
|
169
|
+
function guardSelector(uint node) internal pure returns (bytes4 selector) {
|
|
170
|
+
return bytes4(uint32(guard(node) >> 160));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// @notice Assert that `value` is the host node of `target` on the current chain.
|
|
174
|
+
/// @param value Value to validate.
|
|
175
|
+
/// @param target Expected host contract address.
|
|
176
|
+
/// @return node The same `value` if it matches `target`.
|
|
177
|
+
function matchHost(uint value, address target) internal view returns (uint node) {
|
|
178
|
+
if (value != toHost(target)) revert InvalidId();
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// @notice Build the chain node ID for the current chain.
|
|
183
|
+
/// @return node Chain node ID with zero selector and `address(0)` payload.
|
|
184
|
+
function localChain() internal view returns (uint node) {
|
|
185
|
+
return toLocalBase(Chain);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// @notice Build a chain-local host ID for `target`.
|
|
189
|
+
/// @param target Host contract address.
|
|
190
|
+
/// @return node Host node ID on the current chain.
|
|
191
|
+
function toHost(address target) internal view returns (uint node) {
|
|
192
|
+
return toLocalBase(Host) | uint(uint160(target));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// @notice Build a chain-local command ID for the given selector and contract.
|
|
196
|
+
/// @param selector 4-byte ABI selector of the command entry point.
|
|
197
|
+
/// @param target Command contract address.
|
|
198
|
+
/// @return node Command node ID embedding both the selector and address.
|
|
199
|
+
function toCommand(bytes4 selector, address target) internal view returns (uint node) {
|
|
200
|
+
node = toLocalBase(Command) | uint(uint160(target));
|
|
201
|
+
node |= uint(uint32(selector)) << 160;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// @notice Build a chain-local peer ID for the given selector and contract.
|
|
205
|
+
/// @param selector 4-byte ABI selector of the peer entry point.
|
|
206
|
+
/// @param target Peer contract address.
|
|
207
|
+
/// @return node Peer node ID embedding both the selector and address.
|
|
208
|
+
function toPeer(bytes4 selector, address target) internal view returns (uint node) {
|
|
209
|
+
node = toLocalBase(Peer) | uint(uint160(target));
|
|
210
|
+
node |= uint(uint32(selector)) << 160;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// @notice Build a chain-local query ID for the given selector and contract.
|
|
214
|
+
/// @param selector 4-byte ABI selector of the query entry point.
|
|
215
|
+
/// @param target Query contract address.
|
|
216
|
+
/// @return node Query node ID embedding both the selector and address.
|
|
217
|
+
function toQuery(bytes4 selector, address target) internal view returns (uint node) {
|
|
218
|
+
node = toLocalBase(Query) | uint(uint160(target));
|
|
219
|
+
node |= uint(uint32(selector)) << 160;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// @notice Build a chain-local guard action ID for the given selector and contract.
|
|
223
|
+
/// @param selector 4-byte ABI selector of the guard action entry point.
|
|
224
|
+
/// @param target Guard action contract address.
|
|
225
|
+
/// @return node Guard action node ID embedding both the selector and address.
|
|
226
|
+
function toGuard(bytes4 selector, address target) internal view returns (uint node) {
|
|
227
|
+
node = toLocalBase(Guard) | uint(uint160(target));
|
|
228
|
+
node |= uint(uint32(selector)) << 160;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// @notice Derive an opaque node ID from a keccak preimage.
|
|
232
|
+
/// @param preimage Preimage whose first byte is `0x01`.
|
|
233
|
+
/// @return node `0x00 || bytes31(keccak256(preimage))`.
|
|
234
|
+
function toKeccak(bytes memory preimage) internal pure returns (uint node) {
|
|
235
|
+
return uint(Ids.toKeccak(preimage));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// @notice Assert that `node` matches the opaque keccak ID for `preimage`.
|
|
239
|
+
/// @param node Opaque node ID to validate.
|
|
240
|
+
/// @param preimage Preimage whose first byte is `0x01`.
|
|
241
|
+
/// @return The same `node` value if it matches.
|
|
242
|
+
function matchKeccak(uint node, bytes memory preimage) internal pure returns (uint) {
|
|
243
|
+
if (node != uint(Ids.toKeccak(preimage))) revert InvalidId();
|
|
244
|
+
return node;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// @notice Extract the contract address from any local node ID.
|
|
248
|
+
/// Reverts if `node` does not belong to the local node family or carries `address(0)`.
|
|
249
|
+
/// @param node Node ID.
|
|
250
|
+
/// @return Contract address in the lower 160 bits of `node`.
|
|
251
|
+
function addr(uint node) internal view returns (address) {
|
|
252
|
+
return ensureAddr(address(uint160(local(node))));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// @notice Extract the contract address from a local host ID.
|
|
256
|
+
/// Reverts if `node` does not match the local host base or carries `address(0)`.
|
|
257
|
+
/// @param node Host node ID.
|
|
258
|
+
/// @return Host contract address in the lower 160 bits of `node`.
|
|
259
|
+
function hostAddr(uint node) internal view returns (address) {
|
|
260
|
+
if (!matchesBase(bytes32(node), toLocalBase(Host))) revert InvalidId();
|
|
261
|
+
return ensureAddr(address(uint160(node)));
|
|
262
|
+
}
|
|
263
|
+
}
|
package/utils/Utils.sol
CHANGED
|
@@ -8,6 +8,8 @@ uint16 constant MAX_BPS = 10_000;
|
|
|
8
8
|
error ValueOverflow();
|
|
9
9
|
/// @dev Thrown by `divisible` when `n` is not evenly divisible by `divisor`.
|
|
10
10
|
error NotDivisible();
|
|
11
|
+
/// @dev Thrown when an ID claims to carry an address but the embedded address is zero.
|
|
12
|
+
error ZeroAddress();
|
|
11
13
|
|
|
12
14
|
/// @notice Assert that `value` fits in uint8 and return it unchanged.
|
|
13
15
|
function max8(uint value) pure returns (uint) {
|
|
@@ -92,6 +94,12 @@ function addrOr(address addr, address or) pure returns (address) {
|
|
|
92
94
|
return addr == address(0) ? or : addr;
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
/// @notice Assert that `addr` is non-zero and return it unchanged.
|
|
98
|
+
function ensureAddr(address addr) pure returns (address) {
|
|
99
|
+
if (addr == address(0)) revert ZeroAddress();
|
|
100
|
+
return addr;
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
/// @notice Convert a signed integer to its 32-byte two's-complement representation.
|
|
96
104
|
function intToBytes32(int value) pure returns (bytes32) {
|
|
97
105
|
return bytes32(uint(value));
|
|
@@ -136,20 +144,12 @@ function retryTicket(bytes32 account, bytes calldata state) pure returns (bytes3
|
|
|
136
144
|
|
|
137
145
|
/// @notice Build the chain-local base prefix for a 256-bit ID.
|
|
138
146
|
/// Embeds the current `block.chainid` so IDs are not portable across chains.
|
|
139
|
-
/// @param prefix Four-byte type tag (e.g. `
|
|
147
|
+
/// @param prefix Four-byte type tag (e.g. `Nodes.Host`, `Nodes.Command`).
|
|
140
148
|
/// @return Base value with the type tag in bits [255:224] and chainid in bits [223:192].
|
|
141
149
|
function toLocalBase(uint32 prefix) view returns (uint) {
|
|
142
150
|
return (uint(prefix) << 224) | (uint(max32(block.chainid)) << 192);
|
|
143
151
|
}
|
|
144
152
|
|
|
145
|
-
/// @notice Build the chain-local family prefix for a 256-bit ID.
|
|
146
|
-
/// Uses a 24-bit family tag (the top 3 bytes of the type field).
|
|
147
|
-
/// @param family Three-byte family tag.
|
|
148
|
-
/// @return Family prefix with the family in bits [255:232] and chainid in bits [223:192].
|
|
149
|
-
function toLocalFamily(uint24 family) view returns (uint) {
|
|
150
|
-
return (uint(family) << 232) | (uint(max32(block.chainid)) << 192);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
153
|
/// @notice Build a chain-unspecified base prefix (no chainid embedded).
|
|
154
154
|
/// Used for IDs that must be portable across chains (e.g. user accounts).
|
|
155
155
|
/// @param prefix Four-byte type tag.
|
|
@@ -167,21 +167,6 @@ function isFamily(uint value, uint24 family) pure returns (bool) {
|
|
|
167
167
|
return uint24(value >> 232) == family;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/// @notice Check whether `value` was created on the current chain.
|
|
171
|
-
/// @param value ID to test.
|
|
172
|
-
/// @return True if bits [223:192] of `value` equal `block.chainid`.
|
|
173
|
-
function isLocalChain(uint value) view returns (bool) {
|
|
174
|
-
return uint32(value >> 192) == block.chainid;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/// @notice Check whether `value` belongs to the given family and was created on the current chain.
|
|
178
|
-
/// @param value ID to test.
|
|
179
|
-
/// @param family Expected 24-bit family tag.
|
|
180
|
-
/// @return True if both the family and chainid fields match.
|
|
181
|
-
function isLocalFamily(uint value, uint24 family) view returns (bool) {
|
|
182
|
-
return isFamily(value, family) && isLocalChain(value);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
170
|
/// @notice Check whether two IDs share the same 64-bit base (type tag + chainid).
|
|
186
171
|
/// Used to verify that an ID matches a locally-constructed base without comparing
|
|
187
172
|
/// the lower 192-bit payload.
|