@rootzero/contracts 0.9.1 → 0.9.2
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/Commands.sol +16 -16
- package/Events.sol +3 -3
- package/Utils.sol +1 -1
- package/commands/Base.sol +0 -19
- package/commands/Pipe.sol +15 -10
- package/commands/Provision.sol +2 -2
- package/commands/{control → admin}/AllowAssets.sol +4 -4
- package/commands/{control → admin}/Allowance.sol +8 -8
- package/commands/{control → admin}/Authorize.sol +4 -4
- package/commands/{control → admin}/DenyAssets.sol +4 -4
- package/commands/{control → admin}/Destroy.sol +4 -4
- package/commands/{control → admin}/Execute.sol +4 -4
- package/commands/{control → admin}/Init.sol +4 -4
- package/commands/{control → admin}/Unauthorize.sol +4 -4
- package/core/Access.sol +2 -2
- package/core/Calls.sol +16 -2
- package/core/Host.sol +4 -4
- package/core/Types.sol +1 -1
- package/events/{Control.sol → Admin.sol} +5 -5
- package/events/Peer.sol +24 -0
- package/events/{RootZero.sol → Piped.sol} +3 -3
- package/package.json +1 -1
- package/peer/AllowAssets.sol +38 -0
- package/peer/Allowance.sol +35 -0
- package/peer/AssetPull.sol +43 -0
- package/peer/Base.sol +40 -0
- package/peer/DenyAssets.sol +38 -0
- package/peer/Settle.sol +32 -0
- package/queries/Assets.sol +1 -2
- package/queries/Balances.sol +1 -2
- package/queries/Positions.sol +1 -2
- package/utils/Accounts.sol +8 -0
- package/utils/Ids.sol +35 -30
- package/utils/Layout.sol +3 -3
- package/utils/Utils.sol +2 -2
- package/events/Remote.sol +0 -24
- package/remote/AllowAssets.sol +0 -39
- package/remote/Allowance.sol +0 -36
- package/remote/AssetPull.sol +0 -44
- package/remote/Base.sol +0 -40
- package/remote/DenyAssets.sol +0 -39
- package/remote/Settle.sol +0 -33
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PeerBase} from "./Base.sol";
|
|
5
|
+
import {AllowanceHook} from "../commands/admin/Allowance.sol";
|
|
6
|
+
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
7
|
+
|
|
8
|
+
using Cursors for Cur;
|
|
9
|
+
|
|
10
|
+
/// @title PeerAllowance
|
|
11
|
+
/// @notice Peer that lets a trusted peer host request or refresh its own allowance.
|
|
12
|
+
/// Each AMOUNT block in the request is scoped to the peer host and passed to the
|
|
13
|
+
/// shared allowance hook as a host-scoped allowance. Restricted to trusted peers.
|
|
14
|
+
abstract contract PeerAllowance is PeerBase, AllowanceHook {
|
|
15
|
+
string private constant NAME = "peerAllowance";
|
|
16
|
+
uint internal immutable peerAllowanceId = peerId(NAME);
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
emit Peer(host, peerAllowanceId, NAME, Schemas.Amount, false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// @notice Execute the allowance peer call.
|
|
23
|
+
function peerAllowance(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
24
|
+
(Cur memory amounts, , ) = cursor(request, 1);
|
|
25
|
+
uint peer = caller();
|
|
26
|
+
|
|
27
|
+
while (amounts.i < amounts.bound) {
|
|
28
|
+
(bytes32 asset, bytes32 meta, uint amount) = amounts.unpackAmount();
|
|
29
|
+
allowance(peer, asset, meta, amount);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
amounts.complete();
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PeerBase} from "./Base.sol";
|
|
5
|
+
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
6
|
+
|
|
7
|
+
using Cursors for Cur;
|
|
8
|
+
|
|
9
|
+
abstract contract AssetPullHook {
|
|
10
|
+
/// @notice Override to process one incoming amount-based asset pull request from a peer host.
|
|
11
|
+
/// @param peer Peer host node ID for this request.
|
|
12
|
+
/// @param asset Requested asset identifier.
|
|
13
|
+
/// @param meta Requested asset metadata slot.
|
|
14
|
+
/// @param amount Requested amount in the asset's native units.
|
|
15
|
+
function assetPull(uint peer, bytes32 asset, bytes32 meta, uint amount) internal virtual;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// @title PeerAssetPull
|
|
19
|
+
/// @notice Peer that pulls requested asset amounts from a peer host into this one.
|
|
20
|
+
/// Each AMOUNT block in the request calls `assetPull(peer, asset, meta, amount)`.
|
|
21
|
+
/// Restricted to trusted peers.
|
|
22
|
+
abstract contract PeerAssetPull is PeerBase, AssetPullHook {
|
|
23
|
+
string private constant NAME = "peerAssetPull";
|
|
24
|
+
uint internal immutable peerAssetPullId = peerId(NAME);
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
emit Peer(host, peerAssetPullId, NAME, Schemas.Amount, false);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// @notice Execute the asset-pull peer call.
|
|
31
|
+
function peerAssetPull(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
32
|
+
(Cur memory assets, , ) = cursor(request, 1);
|
|
33
|
+
uint peer = caller();
|
|
34
|
+
|
|
35
|
+
while (assets.i < assets.bound) {
|
|
36
|
+
(bytes32 asset, bytes32 meta, uint amount) = assets.unpackAmount();
|
|
37
|
+
assetPull(peer, asset, meta, amount);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
assets.complete();
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
}
|
package/peer/Base.sol
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import { NodeCalls } from "../core/Calls.sol";
|
|
5
|
+
import { PeerEvent } from "../events/Peer.sol";
|
|
6
|
+
import { Ids, Selectors } from "../utils/Ids.sol";
|
|
7
|
+
|
|
8
|
+
/// @notice ABI-encode a peer call from a target peer ID and request block stream.
|
|
9
|
+
/// @dev Derives the function selector from `target` via `Ids.peerSelector(target)`.
|
|
10
|
+
/// Reverts if `target` is not a valid peer ID.
|
|
11
|
+
/// @param target Destination peer node ID embedding the target selector.
|
|
12
|
+
/// @param request Input block stream for the peer invocation.
|
|
13
|
+
/// @return ABI-encoded calldata for the peer entry point.
|
|
14
|
+
function encodePeerCall(uint target, bytes calldata request) pure returns (bytes memory) {
|
|
15
|
+
bytes4 selector = Ids.peerSelector(target);
|
|
16
|
+
return abi.encodeWithSelector(selector, request);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// @title PeerBase
|
|
20
|
+
/// @notice Abstract base for all rootzero peer contracts.
|
|
21
|
+
/// Peers handle inter-host operations and asset allow/deny management
|
|
22
|
+
/// between cooperating hosts. Access is restricted to trusted callers via `onlyPeer`.
|
|
23
|
+
abstract contract PeerBase is NodeCalls, PeerEvent {
|
|
24
|
+
/// @dev Thrown when the commander attempts to call a peer entrypoint directly.
|
|
25
|
+
error CommanderNotAllowed();
|
|
26
|
+
|
|
27
|
+
/// @dev Restrict execution to trusted callers, excluding the commander.
|
|
28
|
+
modifier onlyPeer() {
|
|
29
|
+
if (msg.sender == commander) revert CommanderNotAllowed();
|
|
30
|
+
enforceCaller(msg.sender);
|
|
31
|
+
_;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @notice Derive the deterministic node ID for a named peer on this contract.
|
|
35
|
+
/// @param name Peer function name (without argument list).
|
|
36
|
+
/// @return Peer node ID.
|
|
37
|
+
function peerId(string memory name) internal view returns (uint) {
|
|
38
|
+
return Ids.toPeer(Selectors.peer(name), address(this));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PeerBase} from "./Base.sol";
|
|
5
|
+
import {DenyAssetsHook} from "../commands/admin/DenyAssets.sol";
|
|
6
|
+
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
7
|
+
|
|
8
|
+
using Cursors for Cur;
|
|
9
|
+
|
|
10
|
+
/// @title PeerDenyAssets
|
|
11
|
+
/// @notice Peer that blocks a list of (asset, meta) pairs on behalf of a peer host.
|
|
12
|
+
/// Each ASSET block in the request calls `denyAsset`. Restricted to trusted peers.
|
|
13
|
+
abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook {
|
|
14
|
+
string private constant NAME = "peerDenyAssets";
|
|
15
|
+
uint internal immutable peerDenyAssetsId = peerId(NAME);
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
emit Peer(host, peerDenyAssetsId, NAME, Schemas.Asset, false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @notice Execute the deny-assets peer call.
|
|
22
|
+
function peerDenyAssets(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
23
|
+
(Cur memory assets, , ) = cursor(request, 1);
|
|
24
|
+
|
|
25
|
+
while (assets.i < assets.bound) {
|
|
26
|
+
(bytes32 asset, bytes32 meta) = assets.unpackAsset();
|
|
27
|
+
denyAsset(asset, meta);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
assets.complete();
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
package/peer/Settle.sol
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import { PeerBase } from "./Base.sol";
|
|
5
|
+
import { TransferHook } from "../commands/Transfer.sol";
|
|
6
|
+
import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
7
|
+
|
|
8
|
+
using Cursors for Cur;
|
|
9
|
+
|
|
10
|
+
/// @title PeerSettle
|
|
11
|
+
/// @notice Peer that consumes peer-supplied TRANSACTION blocks through the shared transfer hook.
|
|
12
|
+
/// Each TRANSACTION block in the request calls `transfer(value)`. Restricted to trusted peers.
|
|
13
|
+
abstract contract PeerSettle is PeerBase, TransferHook {
|
|
14
|
+
string private constant NAME = "peerSettle";
|
|
15
|
+
uint internal immutable peerSettleId = peerId(NAME);
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
emit Peer(host, peerSettleId, NAME, Schemas.Transaction, false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @notice Execute the peer-settle call.
|
|
22
|
+
function peerSettle(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
23
|
+
(Cur memory state, , ) = cursor(request, 1);
|
|
24
|
+
|
|
25
|
+
while (state.i < state.bound) {
|
|
26
|
+
transfer(state.unpackTxValue());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
state.complete();
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
}
|
package/queries/Assets.sol
CHANGED
|
@@ -8,8 +8,6 @@ import {QueryBase} from "./Base.sol";
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
using Writers for Writer;
|
|
10
10
|
|
|
11
|
-
string constant NAME = "isAllowedAsset";
|
|
12
|
-
|
|
13
11
|
abstract contract IsAllowedAssetHook {
|
|
14
12
|
/// @notice Resolve whether one asset tuple is allowed.
|
|
15
13
|
/// Concrete implementations define the allowlist policy.
|
|
@@ -24,6 +22,7 @@ abstract contract IsAllowedAssetHook {
|
|
|
24
22
|
/// The request is a run of `ASSET` blocks.
|
|
25
23
|
/// The response returns one `STATUS` form block per query entry, preserving request order.
|
|
26
24
|
abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
|
|
25
|
+
string private constant NAME = "isAllowedAsset";
|
|
27
26
|
uint public immutable isAllowedAssetId = queryId(NAME);
|
|
28
27
|
|
|
29
28
|
constructor() {
|
package/queries/Balances.sol
CHANGED
|
@@ -7,8 +7,6 @@ import {QueryBase} from "./Base.sol";
|
|
|
7
7
|
using Cursors for Cur;
|
|
8
8
|
using Writers for Writer;
|
|
9
9
|
|
|
10
|
-
string constant NAME = "getBalances";
|
|
11
|
-
|
|
12
10
|
abstract contract GetBalancesHook {
|
|
13
11
|
/// @notice Resolve one account's balance for one supported asset.
|
|
14
12
|
/// Concrete implementations define how assets are resolved.
|
|
@@ -24,6 +22,7 @@ abstract contract GetBalancesHook {
|
|
|
24
22
|
/// The request is a run of `ACCOUNT_ASSET` form blocks.
|
|
25
23
|
/// The response returns one `ACCOUNT_AMOUNT` form block per requested position, preserving request order.
|
|
26
24
|
abstract contract GetBalances is QueryBase, GetBalancesHook {
|
|
25
|
+
string private constant NAME = "getBalances";
|
|
27
26
|
uint public immutable getBalancesId = queryId(NAME);
|
|
28
27
|
|
|
29
28
|
constructor() {
|
package/queries/Positions.sol
CHANGED
|
@@ -7,8 +7,6 @@ import {QueryBase} from "./Base.sol";
|
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
10
|
-
string constant NAME = "getPosition";
|
|
11
|
-
|
|
12
10
|
abstract contract GetPositionHook {
|
|
13
11
|
/// @notice Resolve the position payload for one requested position.
|
|
14
12
|
/// Concrete implementations must append exactly one `RESPONSE` block whose payload
|
|
@@ -30,6 +28,7 @@ abstract contract GetPositionHook {
|
|
|
30
28
|
/// The request is a run of `ACCOUNT_ASSET` form blocks.
|
|
31
29
|
/// The response returns one dynamic `RESPONSE` block per position entry, preserving request order.
|
|
32
30
|
abstract contract GetPosition is QueryBase, GetPositionHook {
|
|
31
|
+
string private constant NAME = "getPosition";
|
|
33
32
|
uint public immutable getPositionId = queryId(NAME);
|
|
34
33
|
uint internal immutable positionResponseSize;
|
|
35
34
|
|
package/utils/Accounts.sol
CHANGED
|
@@ -92,6 +92,14 @@ library Accounts {
|
|
|
92
92
|
return account;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/// @notice Assert that `account` is not an admin account and return it unchanged.
|
|
96
|
+
/// @param account Account ID to validate.
|
|
97
|
+
/// @return The same `account` value if valid.
|
|
98
|
+
function ensureNotAdmin(bytes32 account) internal pure returns (bytes32) {
|
|
99
|
+
if (isAdmin(account)) revert InvalidAccount();
|
|
100
|
+
return account;
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
/// @notice Extract the EVM address embedded in an EVM-family account ID.
|
|
96
104
|
/// Reverts if `account` is not an EVM-family account.
|
|
97
105
|
/// @param account EVM-family account ID.
|
package/utils/Ids.sol
CHANGED
|
@@ -8,9 +8,9 @@ import {isLocalFamily, matchesBase, toLocalBase} from "./Utils.sol";
|
|
|
8
8
|
/// @notice Encoding and decoding helpers for 256-bit node identifiers.
|
|
9
9
|
///
|
|
10
10
|
/// Node IDs share a common layout:
|
|
11
|
-
/// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `
|
|
11
|
+
/// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `Peer`)
|
|
12
12
|
/// - bits [223:192] — current `block.chainid` (makes IDs chain-local)
|
|
13
|
-
/// - bits [191:160] — 4-byte ABI selector (commands and
|
|
13
|
+
/// - bits [191:160] — 4-byte ABI selector (commands and peers only)
|
|
14
14
|
/// - bits [159:0] — 160-bit EVM contract address
|
|
15
15
|
library Ids {
|
|
16
16
|
/// @dev Thrown when an ID does not match the expected node type or chain.
|
|
@@ -22,8 +22,8 @@ library Ids {
|
|
|
22
22
|
uint32 constant Host = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
|
|
23
23
|
/// @dev Full 4-byte type prefix for command nodes.
|
|
24
24
|
uint32 constant Command = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
|
|
25
|
-
/// @dev Full 4-byte type prefix for
|
|
26
|
-
uint32 constant
|
|
25
|
+
/// @dev Full 4-byte type prefix for peer nodes.
|
|
26
|
+
uint32 constant Peer = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Peer);
|
|
27
27
|
/// @dev Full 4-byte type prefix for query nodes.
|
|
28
28
|
uint32 constant Query = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Query);
|
|
29
29
|
|
|
@@ -37,9 +37,9 @@ library Ids {
|
|
|
37
37
|
return uint32(id >> 224) == Command;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/// @notice Return true if `id` is a
|
|
41
|
-
function
|
|
42
|
-
return uint32(id >> 224) ==
|
|
40
|
+
/// @notice Return true if `id` is a peer node ID.
|
|
41
|
+
function isPeer(uint id) internal pure returns (bool) {
|
|
42
|
+
return uint32(id >> 224) == Peer;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/// @notice Return true if `id` is a query node ID.
|
|
@@ -47,6 +47,11 @@ library Ids {
|
|
|
47
47
|
return uint32(id >> 224) == Query;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/// @notice Return true if `id` is a local node ID for this contract.
|
|
51
|
+
function isLocalNode(uint id) internal view returns (bool) {
|
|
52
|
+
return isLocalFamily(id, Node) && address(uint160(id)) == address(this);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
/// @notice Assert that `id` is a command ID and return it unchanged.
|
|
51
56
|
/// @param id Node ID to validate.
|
|
52
57
|
/// @return cid The same `id` value if it is a command.
|
|
@@ -63,19 +68,19 @@ library Ids {
|
|
|
63
68
|
return bytes4(uint32(id >> 160));
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
/// @notice Assert that `id` is a
|
|
71
|
+
/// @notice Assert that `id` is a peer ID and return it unchanged.
|
|
67
72
|
/// @param id Node ID to validate.
|
|
68
|
-
/// @return pid The same `id` value if it is a
|
|
69
|
-
function
|
|
70
|
-
if (!
|
|
73
|
+
/// @return pid The same `id` value if it is a peer.
|
|
74
|
+
function peer(uint id) internal pure returns (uint pid) {
|
|
75
|
+
if (!isPeer(id)) revert InvalidId();
|
|
71
76
|
return id;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
/// @notice Assert that `id` is a
|
|
79
|
+
/// @notice Assert that `id` is a peer ID and return its embedded ABI selector.
|
|
75
80
|
/// @param id Node ID to validate.
|
|
76
|
-
/// @return selector 4-byte
|
|
77
|
-
function
|
|
78
|
-
if (!
|
|
81
|
+
/// @return selector 4-byte peer selector stored in bits [191:160].
|
|
82
|
+
function peerSelector(uint id) internal pure returns (bytes4 selector) {
|
|
83
|
+
if (!isPeer(id)) revert InvalidId();
|
|
79
84
|
return bytes4(uint32(id >> 160));
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -121,12 +126,12 @@ library Ids {
|
|
|
121
126
|
return id;
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
/// @notice Build a chain-local
|
|
125
|
-
/// @param selector 4-byte ABI selector of the
|
|
126
|
-
/// @param target
|
|
127
|
-
/// @return
|
|
128
|
-
function
|
|
129
|
-
uint id = toLocalBase(
|
|
129
|
+
/// @notice Build a chain-local peer ID for the given selector and contract.
|
|
130
|
+
/// @param selector 4-byte ABI selector of the peer entry point.
|
|
131
|
+
/// @param target Peer contract address.
|
|
132
|
+
/// @return Peer node ID embedding both the selector and address.
|
|
133
|
+
function toPeer(bytes4 selector, address target) internal view returns (uint) {
|
|
134
|
+
uint id = toLocalBase(Peer) | uint(uint160(target));
|
|
130
135
|
id |= uint(uint32(selector)) << 160;
|
|
131
136
|
return id;
|
|
132
137
|
}
|
|
@@ -143,7 +148,7 @@ library Ids {
|
|
|
143
148
|
|
|
144
149
|
/// @notice Extract the contract address from any local node ID.
|
|
145
150
|
/// Reverts if `id` does not belong to the local node family.
|
|
146
|
-
/// @param id Node ID (host, command, or
|
|
151
|
+
/// @param id Node ID (host, command, or peer).
|
|
147
152
|
/// @return Contract address in the lower 160 bits of `id`.
|
|
148
153
|
function nodeAddr(uint id) internal view returns (address) {
|
|
149
154
|
if (!isLocalFamily(id, Node)) revert InvalidId();
|
|
@@ -161,12 +166,12 @@ library Ids {
|
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
/// @title Selectors
|
|
164
|
-
/// @notice ABI-selector derivation helpers for command,
|
|
169
|
+
/// @notice ABI-selector derivation helpers for command, peer, and query dispatch.
|
|
165
170
|
library Selectors {
|
|
166
171
|
/// @dev ABI argument encoding for command entry points: `((bytes32,bytes,bytes))`.
|
|
167
172
|
string constant CommandArgs = "((bytes32,bytes,bytes))";
|
|
168
|
-
/// @dev ABI argument encoding for
|
|
169
|
-
string constant
|
|
173
|
+
/// @dev ABI argument encoding for peer entry points: `(bytes)`.
|
|
174
|
+
string constant PeerArgs = "(bytes)";
|
|
170
175
|
/// @dev ABI argument encoding for query entry points: `(bytes)`.
|
|
171
176
|
string constant QueryArgs = "(bytes)";
|
|
172
177
|
|
|
@@ -178,12 +183,12 @@ library Selectors {
|
|
|
178
183
|
return bytes4(keccak256(bytes.concat(bytes(name), bytes(CommandArgs))));
|
|
179
184
|
}
|
|
180
185
|
|
|
181
|
-
/// @notice Derive the 4-byte ABI selector for a named
|
|
182
|
-
/// The selector is `keccak256(name ++
|
|
183
|
-
/// @param name
|
|
186
|
+
/// @notice Derive the 4-byte ABI selector for a named peer.
|
|
187
|
+
/// The selector is `keccak256(name ++ PeerArgs)[0:4]`.
|
|
188
|
+
/// @param name Peer function name (without arguments).
|
|
184
189
|
/// @return 4-byte selector.
|
|
185
|
-
function
|
|
186
|
-
return bytes4(keccak256(bytes.concat(bytes(name), bytes(
|
|
190
|
+
function peer(string memory name) internal pure returns (bytes4) {
|
|
191
|
+
return bytes4(keccak256(bytes.concat(bytes(name), bytes(PeerArgs))));
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
/// @notice Derive the 4-byte ABI selector for a named query.
|
package/utils/Layout.sol
CHANGED
|
@@ -26,7 +26,7 @@ library Layout {
|
|
|
26
26
|
|
|
27
27
|
/// @dev ID encodes an account.
|
|
28
28
|
uint8 constant Account = 0x01;
|
|
29
|
-
/// @dev ID encodes a network node (host, command, or
|
|
29
|
+
/// @dev ID encodes a network node (host, command, or peer).
|
|
30
30
|
uint8 constant Node = 0x02;
|
|
31
31
|
/// @dev ID encodes an asset.
|
|
32
32
|
uint8 constant Asset = 0x03;
|
|
@@ -50,8 +50,8 @@ library Layout {
|
|
|
50
50
|
uint8 constant Host = 0x01;
|
|
51
51
|
/// @dev Node is a command contract.
|
|
52
52
|
uint8 constant Command = 0x02;
|
|
53
|
-
/// @dev Node is a
|
|
54
|
-
uint8 constant
|
|
53
|
+
/// @dev Node is a peer contract.
|
|
54
|
+
uint8 constant Peer = 0x03;
|
|
55
55
|
/// @dev Node is a query contract.
|
|
56
56
|
uint8 constant Query = 0x04;
|
|
57
57
|
|
package/utils/Utils.sol
CHANGED
|
@@ -170,7 +170,7 @@ function isFamily(uint value, uint24 family) pure returns (bool) {
|
|
|
170
170
|
/// @notice Check whether `value` was created on the current chain.
|
|
171
171
|
/// @param value ID to test.
|
|
172
172
|
/// @return True if bits [223:192] of `value` equal `block.chainid`.
|
|
173
|
-
function
|
|
173
|
+
function isLocalChain(uint value) view returns (bool) {
|
|
174
174
|
return uint32(value >> 192) == block.chainid;
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -179,7 +179,7 @@ function isLocal(uint value) view returns (bool) {
|
|
|
179
179
|
/// @param family Expected 24-bit family tag.
|
|
180
180
|
/// @return True if both the family and chainid fields match.
|
|
181
181
|
function isLocalFamily(uint value, uint24 family) view returns (bool) {
|
|
182
|
-
return isFamily(value, family) &&
|
|
182
|
+
return isFamily(value, family) && isLocalChain(value);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/// @notice Check whether two IDs share the same 64-bit base (type tag + chainid).
|
package/events/Remote.sol
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { EventEmitter } from "./Emitter.sol";
|
|
5
|
-
|
|
6
|
-
string constant ABI =
|
|
7
|
-
"event Remote(uint indexed host, uint id, string name, string request, bool acceptsValue)";
|
|
8
|
-
|
|
9
|
-
/// @notice Emitted once per remote during host deployment to publish its request schema.
|
|
10
|
-
abstract contract RemoteEvent is EventEmitter {
|
|
11
|
-
/// @param host Host node ID that owns this remote.
|
|
12
|
-
/// @param id Remote node ID.
|
|
13
|
-
/// @param name Human-readable remote name.
|
|
14
|
-
/// @param request Schema DSL string describing the remote request shape.
|
|
15
|
-
/// @param acceptsValue Whether the remote entrypoint accepts nonzero `msg.value`.
|
|
16
|
-
event Remote(uint indexed host, uint id, string name, string request, bool acceptsValue);
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
emit EventAbi(ABI);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
package/remote/AllowAssets.sol
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { RemoteBase } from "./Base.sol";
|
|
5
|
-
import { AllowAssetsHook } from "../commands/control/AllowAssets.sol";
|
|
6
|
-
import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
7
|
-
|
|
8
|
-
using Cursors for Cur;
|
|
9
|
-
|
|
10
|
-
string constant NAME = "remoteAllowAssets";
|
|
11
|
-
|
|
12
|
-
/// @title RemoteAllowAssets
|
|
13
|
-
/// @notice Remote that permits a list of (asset, meta) pairs on behalf of a remote host.
|
|
14
|
-
/// Each ASSET block in the request calls `allowAsset`. Restricted to trusted remotes.
|
|
15
|
-
abstract contract RemoteAllowAssets is RemoteBase, AllowAssetsHook {
|
|
16
|
-
uint internal immutable remoteAllowAssetsId = remoteId(NAME);
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
emit Remote(host, remoteAllowAssetsId, NAME, Schemas.Asset, false);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/// @notice Execute the allow-assets remote call.
|
|
23
|
-
function remoteAllowAssets(bytes calldata request) external onlyRemote returns (bytes memory) {
|
|
24
|
-
(Cur memory assets, , ) = cursor(request, 1);
|
|
25
|
-
|
|
26
|
-
while (assets.i < assets.bound) {
|
|
27
|
-
(bytes32 asset, bytes32 meta) = assets.unpackAsset();
|
|
28
|
-
allowAsset(asset, meta);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
assets.complete();
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
package/remote/Allowance.sol
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {RemoteBase} from "./Base.sol";
|
|
5
|
-
import {AllowanceHook} from "../commands/control/Allowance.sol";
|
|
6
|
-
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
7
|
-
|
|
8
|
-
using Cursors for Cur;
|
|
9
|
-
|
|
10
|
-
string constant NAME = "remoteAllowance";
|
|
11
|
-
|
|
12
|
-
/// @title RemoteAllowance
|
|
13
|
-
/// @notice Remote that lets a trusted remote host request or refresh its own allowance.
|
|
14
|
-
/// Each AMOUNT block in the request is scoped to the remote host and passed to the
|
|
15
|
-
/// shared allowance hook as a host-scoped allowance. Restricted to trusted remotes.
|
|
16
|
-
abstract contract RemoteAllowance is RemoteBase, AllowanceHook {
|
|
17
|
-
uint internal immutable remoteAllowanceId = remoteId(NAME);
|
|
18
|
-
|
|
19
|
-
constructor() {
|
|
20
|
-
emit Remote(host, remoteAllowanceId, NAME, Schemas.Amount, false);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/// @notice Execute the allowance remote call.
|
|
24
|
-
function remoteAllowance(bytes calldata request) external onlyRemote returns (bytes memory) {
|
|
25
|
-
(Cur memory amounts, , ) = cursor(request, 1);
|
|
26
|
-
uint remote = caller();
|
|
27
|
-
|
|
28
|
-
while (amounts.i < amounts.bound) {
|
|
29
|
-
(bytes32 asset, bytes32 meta, uint amount) = amounts.unpackAmount();
|
|
30
|
-
allowance(remote, asset, meta, amount);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
amounts.complete();
|
|
34
|
-
return "";
|
|
35
|
-
}
|
|
36
|
-
}
|
package/remote/AssetPull.sol
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {RemoteBase} from "./Base.sol";
|
|
5
|
-
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
6
|
-
|
|
7
|
-
string constant NAME = "remoteAssetPull";
|
|
8
|
-
|
|
9
|
-
using Cursors for Cur;
|
|
10
|
-
|
|
11
|
-
abstract contract AssetPullHook {
|
|
12
|
-
/// @notice Override to process one incoming amount-based asset pull request from a remote host.
|
|
13
|
-
/// @param remote Remote host node ID for this request.
|
|
14
|
-
/// @param asset Requested asset identifier.
|
|
15
|
-
/// @param meta Requested asset metadata slot.
|
|
16
|
-
/// @param amount Requested amount in the asset's native units.
|
|
17
|
-
function assetPull(uint remote, bytes32 asset, bytes32 meta, uint amount) internal virtual;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/// @title RemoteAssetPull
|
|
21
|
-
/// @notice Remote that pulls requested asset amounts from a remote host into this one.
|
|
22
|
-
/// Each AMOUNT block in the request calls `assetPull(remote, asset, meta, amount)`.
|
|
23
|
-
/// Restricted to trusted remotes.
|
|
24
|
-
abstract contract RemoteAssetPull is RemoteBase, AssetPullHook {
|
|
25
|
-
uint internal immutable remoteAssetPullId = remoteId(NAME);
|
|
26
|
-
|
|
27
|
-
constructor() {
|
|
28
|
-
emit Remote(host, remoteAssetPullId, NAME, Schemas.Amount, false);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/// @notice Execute the asset-pull remote call.
|
|
32
|
-
function remoteAssetPull(bytes calldata request) external onlyRemote returns (bytes memory) {
|
|
33
|
-
(Cur memory assets, , ) = cursor(request, 1);
|
|
34
|
-
uint remote = caller();
|
|
35
|
-
|
|
36
|
-
while (assets.i < assets.bound) {
|
|
37
|
-
(bytes32 asset, bytes32 meta, uint amount) = assets.unpackAmount();
|
|
38
|
-
assetPull(remote, asset, meta, amount);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
assets.complete();
|
|
42
|
-
return "";
|
|
43
|
-
}
|
|
44
|
-
}
|
package/remote/Base.sol
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { NodeCalls } from "../core/Calls.sol";
|
|
5
|
-
import { RemoteEvent } from "../events/Remote.sol";
|
|
6
|
-
import { Ids, Selectors } from "../utils/Ids.sol";
|
|
7
|
-
|
|
8
|
-
/// @notice ABI-encode a remote call from a target remote ID and request block stream.
|
|
9
|
-
/// @dev Derives the function selector from `target` via `Ids.remoteSelector(target)`.
|
|
10
|
-
/// Reverts if `target` is not a valid remote ID.
|
|
11
|
-
/// @param target Destination remote node ID embedding the target selector.
|
|
12
|
-
/// @param request Input block stream for the remote invocation.
|
|
13
|
-
/// @return ABI-encoded calldata for the remote entry point.
|
|
14
|
-
function encodeRemoteCall(uint target, bytes calldata request) pure returns (bytes memory) {
|
|
15
|
-
bytes4 selector = Ids.remoteSelector(target);
|
|
16
|
-
return abi.encodeWithSelector(selector, request);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/// @title RemoteBase
|
|
20
|
-
/// @notice Abstract base for all rootzero remote contracts.
|
|
21
|
-
/// Remotes handle inter-host operations and asset allow/deny management
|
|
22
|
-
/// between cooperating hosts. Access is restricted to trusted callers via `onlyRemote`.
|
|
23
|
-
abstract contract RemoteBase is NodeCalls, RemoteEvent {
|
|
24
|
-
/// @dev Thrown when the commander attempts to call a remote entrypoint directly.
|
|
25
|
-
error CommanderNotAllowed();
|
|
26
|
-
|
|
27
|
-
/// @dev Restrict execution to trusted callers, excluding the commander.
|
|
28
|
-
modifier onlyRemote() {
|
|
29
|
-
if (msg.sender == commander) revert CommanderNotAllowed();
|
|
30
|
-
enforceCaller(msg.sender);
|
|
31
|
-
_;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/// @notice Derive the deterministic node ID for a named remote on this contract.
|
|
35
|
-
/// @param name Remote function name (without argument list).
|
|
36
|
-
/// @return Remote node ID.
|
|
37
|
-
function remoteId(string memory name) internal view returns (uint) {
|
|
38
|
-
return Ids.toRemote(Selectors.remote(name), address(this));
|
|
39
|
-
}
|
|
40
|
-
}
|
package/remote/DenyAssets.sol
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {RemoteBase} from "./Base.sol";
|
|
5
|
-
import {DenyAssetsHook} from "../commands/control/DenyAssets.sol";
|
|
6
|
-
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
7
|
-
|
|
8
|
-
using Cursors for Cur;
|
|
9
|
-
|
|
10
|
-
string constant NAME = "remoteDenyAssets";
|
|
11
|
-
|
|
12
|
-
/// @title RemoteDenyAssets
|
|
13
|
-
/// @notice Remote that blocks a list of (asset, meta) pairs on behalf of a remote host.
|
|
14
|
-
/// Each ASSET block in the request calls `denyAsset`. Restricted to trusted remotes.
|
|
15
|
-
abstract contract RemoteDenyAssets is RemoteBase, DenyAssetsHook {
|
|
16
|
-
uint internal immutable remoteDenyAssetsId = remoteId(NAME);
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
emit Remote(host, remoteDenyAssetsId, NAME, Schemas.Asset, false);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/// @notice Execute the deny-assets remote call.
|
|
23
|
-
function remoteDenyAssets(bytes calldata request) external onlyRemote returns (bytes memory) {
|
|
24
|
-
(Cur memory assets, , ) = cursor(request, 1);
|
|
25
|
-
|
|
26
|
-
while (assets.i < assets.bound) {
|
|
27
|
-
(bytes32 asset, bytes32 meta) = assets.unpackAsset();
|
|
28
|
-
denyAsset(asset, meta);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
assets.complete();
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|