@rootzero/contracts 0.9.0 → 0.9.1
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 +15 -16
- package/Events.sol +2 -1
- package/blocks/Cursors.sol +25 -64
- package/blocks/Keys.sol +0 -2
- package/blocks/Schema.sol +0 -1
- package/commands/Burn.sol +2 -2
- package/commands/Credit.sol +2 -1
- package/commands/Debit.sol +2 -2
- package/commands/Deposit.sol +8 -7
- package/commands/Pipe.sol +2 -2
- package/commands/Provision.sol +8 -7
- package/commands/Transfer.sol +2 -2
- package/commands/Withdraw.sol +2 -2
- package/commands/{admin → control}/AllowAssets.sol +7 -6
- package/commands/{admin → control}/Allowance.sol +11 -11
- package/commands/{admin → control}/Authorize.sol +6 -5
- package/commands/{admin → control}/DenyAssets.sol +7 -6
- package/commands/{admin → control}/Destroy.sol +6 -5
- package/commands/{admin → control}/Execute.sol +6 -5
- package/commands/{admin → control}/Init.sol +6 -5
- package/commands/{admin → control}/Unauthorize.sol +6 -5
- package/core/Access.sol +2 -2
- package/core/Host.sol +5 -6
- package/core/Types.sol +1 -1
- package/events/Control.sol +31 -0
- package/events/Query.sol +4 -4
- package/events/Remote.sol +24 -0
- package/package.json +1 -1
- package/remote/AllowAssets.sol +39 -0
- package/remote/Allowance.sol +36 -0
- package/remote/AssetPull.sol +44 -0
- package/remote/Base.sol +40 -0
- package/remote/DenyAssets.sol +39 -0
- package/remote/Settle.sol +33 -0
- package/utils/Ids.sol +30 -30
- package/utils/Layout.sol +3 -3
- package/commands/admin/Relocate.sol +0 -43
- package/events/Peer.sol +0 -24
- package/peer/AllowAssets.sol +0 -39
- package/peer/Allowance.sol +0 -36
- package/peer/AssetPull.sol +0 -44
- package/peer/Base.sol +0 -40
- package/peer/DenyAssets.sol +0 -39
- package/peer/Settle.sol +0 -33
|
@@ -3,8 +3,7 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { CommandBase, CommandContext, Keys } from "../Base.sol";
|
|
5
5
|
import { Cursors, Cur } from "../../Cursors.sol";
|
|
6
|
-
|
|
7
|
-
string constant NAME = "destroy";
|
|
6
|
+
import { ControlEvent } from "../../events/Control.sol";
|
|
8
7
|
|
|
9
8
|
using Cursors for Cur;
|
|
10
9
|
|
|
@@ -15,13 +14,15 @@ abstract contract DestroyHook {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
/// @title Destroy
|
|
18
|
-
/// @notice
|
|
17
|
+
/// @notice Control command that runs host teardown logic via a virtual hook.
|
|
19
18
|
/// The full request is passed to `destroy` as a cursor. Only callable by the admin account.
|
|
20
|
-
abstract contract Destroy is CommandBase, DestroyHook {
|
|
19
|
+
abstract contract Destroy is CommandBase, ControlEvent, DestroyHook {
|
|
20
|
+
string private constant NAME = "destroy";
|
|
21
|
+
|
|
21
22
|
uint internal immutable destroyId = commandId(NAME);
|
|
22
23
|
|
|
23
24
|
constructor(string memory input) {
|
|
24
|
-
emit
|
|
25
|
+
emit Control(host, destroyId, NAME, input, Keys.Empty, Keys.Empty, false);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function destroy(
|
|
@@ -3,22 +3,23 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {CommandContext, CommandPayable, Keys} from "../Base.sol";
|
|
5
5
|
import {Cursors, Cur, Schemas} from "../../Cursors.sol";
|
|
6
|
+
import {ControlEvent} from "../../events/Control.sol";
|
|
6
7
|
import {Budget, Values} from "../../utils/Value.sol";
|
|
7
8
|
import {Ids} from "../../utils/Ids.sol";
|
|
8
9
|
|
|
9
10
|
using Cursors for Cur;
|
|
10
11
|
|
|
11
|
-
string constant NAME = "executePayable";
|
|
12
|
-
|
|
13
12
|
/// @title ExecutePayable
|
|
14
|
-
/// @notice
|
|
13
|
+
/// @notice Control command that forwards raw calldata to one or more target nodes.
|
|
15
14
|
/// Each CALL block specifies a target node ID, native value, and raw calldata payload.
|
|
16
15
|
/// Only callable by the admin account.
|
|
17
|
-
abstract contract ExecutePayable is CommandPayable {
|
|
16
|
+
abstract contract ExecutePayable is CommandPayable, ControlEvent {
|
|
17
|
+
string private constant NAME = "executePayable";
|
|
18
|
+
|
|
18
19
|
uint internal immutable executePayableId = commandId(NAME);
|
|
19
20
|
|
|
20
21
|
constructor() {
|
|
21
|
-
emit
|
|
22
|
+
emit Control(host, executePayableId, NAME, Schemas.Call, Keys.Empty, Keys.Empty, true);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
|
|
@@ -3,8 +3,7 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { CommandBase, CommandContext, Keys } from "../Base.sol";
|
|
5
5
|
import { Cursors, Cur } from "../../Cursors.sol";
|
|
6
|
-
|
|
7
|
-
string constant NAME = "init";
|
|
6
|
+
import { ControlEvent } from "../../events/Control.sol";
|
|
8
7
|
|
|
9
8
|
using Cursors for Cur;
|
|
10
9
|
|
|
@@ -15,13 +14,15 @@ abstract contract InitHook {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
/// @title Init
|
|
18
|
-
/// @notice
|
|
17
|
+
/// @notice Control command that runs host initialization logic via a virtual hook.
|
|
19
18
|
/// The full request is passed to `init` as a cursor. Only callable by the admin account.
|
|
20
|
-
abstract contract Init is CommandBase, InitHook {
|
|
19
|
+
abstract contract Init is CommandBase, ControlEvent, InitHook {
|
|
20
|
+
string private constant NAME = "init";
|
|
21
|
+
|
|
21
22
|
uint internal immutable initId = commandId(NAME);
|
|
22
23
|
|
|
23
24
|
constructor(string memory input) {
|
|
24
|
-
emit
|
|
25
|
+
emit Control(host, initId, NAME, input, Keys.Empty, Keys.Empty, false);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function init(
|
|
@@ -3,19 +3,20 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { CommandBase, CommandContext, Keys } from "../Base.sol";
|
|
5
5
|
import { Cursors, Cur, Schemas } from "../../Cursors.sol";
|
|
6
|
+
import { ControlEvent } from "../../events/Control.sol";
|
|
6
7
|
using Cursors for Cur;
|
|
7
8
|
|
|
8
|
-
string constant NAME = "unauthorize";
|
|
9
|
-
|
|
10
9
|
/// @title Unauthorize
|
|
11
|
-
/// @notice
|
|
10
|
+
/// @notice Control command that revokes authorization from a list of node IDs.
|
|
12
11
|
/// Each NODE block in the request is deauthorized on the host.
|
|
13
12
|
/// Only callable by the admin account.
|
|
14
|
-
abstract contract Unauthorize is CommandBase {
|
|
13
|
+
abstract contract Unauthorize is CommandBase, ControlEvent {
|
|
14
|
+
string private constant NAME = "unauthorize";
|
|
15
|
+
|
|
15
16
|
uint internal immutable unauthorizeId = commandId(NAME);
|
|
16
17
|
|
|
17
18
|
constructor() {
|
|
18
|
-
emit
|
|
19
|
+
emit Control(host, unauthorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
function unauthorize(
|
package/core/Access.sol
CHANGED
|
@@ -12,7 +12,7 @@ import {addrOr} from "../utils/Utils.sol";
|
|
|
12
12
|
/// Tracks an immutable trusted commander, the host's own node ID, and a
|
|
13
13
|
/// mapping of externally trusted node IDs. Inbound trust is host-based:
|
|
14
14
|
/// trusted hosts, the commander, and this contract itself may interact
|
|
15
|
-
/// with the host through the guarded command and
|
|
15
|
+
/// with the host through the guarded command and remote entrypoints.
|
|
16
16
|
abstract contract AccessControl is RootZeroContext, AccessEvent {
|
|
17
17
|
/// @dev Trusted commander address. All calls from this address are implicitly trusted.
|
|
18
18
|
/// Defaults to `address(this)` when no external commander is provided.
|
|
@@ -69,7 +69,7 @@ abstract contract AccessControl is RootZeroContext, AccessEvent {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/// @notice Assert that `caller` is trusted and return it.
|
|
72
|
-
/// Used by command and
|
|
72
|
+
/// Used by command and remote modifiers to gate execution to authorized senders.
|
|
73
73
|
/// @param caller Address to validate.
|
|
74
74
|
/// @return The same `caller` value if trusted.
|
|
75
75
|
function enforceCaller(address caller) internal view returns (address) {
|
package/core/Host.sol
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {AccessControl} from "./Access.sol";
|
|
5
|
-
import {Authorize} from "../commands/
|
|
6
|
-
import {Unauthorize} from "../commands/
|
|
7
|
-
import {ExecutePayable} from "../commands/
|
|
8
|
-
import {RelocatePayable} from "../commands/admin/Relocate.sol";
|
|
5
|
+
import {Authorize} from "../commands/control/Authorize.sol";
|
|
6
|
+
import {Unauthorize} from "../commands/control/Unauthorize.sol";
|
|
7
|
+
import {ExecutePayable} from "../commands/control/Execute.sol";
|
|
9
8
|
import {HostAnnouncedEvent} from "../events/Host.sol";
|
|
10
9
|
import {IHostDiscovery} from "../interfaces/IHostDiscovery.sol";
|
|
11
10
|
import {Ids} from "../utils/Ids.sol";
|
|
@@ -26,10 +25,10 @@ abstract contract HostDiscovery is HostAnnouncedEvent, IHostDiscovery {
|
|
|
26
25
|
|
|
27
26
|
/// @title Host
|
|
28
27
|
/// @notice Abstract base contract for rootzero host implementations.
|
|
29
|
-
/// Inherits
|
|
28
|
+
/// Inherits control command support (authorize, unauthorize, executePayable) and
|
|
30
29
|
/// optionally announces itself to a discovery contract at deployment.
|
|
31
30
|
/// Accepts native ETH payments via the `receive` function.
|
|
32
|
-
abstract contract Host is Authorize, Unauthorize, ExecutePayable
|
|
31
|
+
abstract contract Host is Authorize, Unauthorize, ExecutePayable {
|
|
33
32
|
/// @param cmdr Commander address; passed to `AccessControl`.
|
|
34
33
|
/// If `cmdr` is a deployed contract, the host calls `announceHost`
|
|
35
34
|
/// on it during construction to register with the discovery registry.
|
package/core/Types.sol
CHANGED
|
@@ -71,7 +71,7 @@ struct HostAccountAmount {
|
|
|
71
71
|
uint amount;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/// @notice Transfer payload used by transaction blocks and
|
|
74
|
+
/// @notice Transfer payload used by transaction blocks and remote settlement.
|
|
75
75
|
struct Tx {
|
|
76
76
|
/// @dev Sender account identifier.
|
|
77
77
|
bytes32 from;
|
|
@@ -0,0 +1,31 @@
|
|
|
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 Control(uint indexed host, uint id, string name, string request, bytes4 state, bytes4 output, bool acceptsValue)";
|
|
8
|
+
|
|
9
|
+
/// @notice Emitted once per control command during host deployment to publish its request schema and state keys.
|
|
10
|
+
abstract contract ControlEvent is EventEmitter {
|
|
11
|
+
/// @param host Host node ID that owns this control command.
|
|
12
|
+
/// @param id Command node ID.
|
|
13
|
+
/// @param name Human-readable command name.
|
|
14
|
+
/// @param request Schema DSL string describing the request shape.
|
|
15
|
+
/// @param state Block key expected for input state, or `Keys.Empty`.
|
|
16
|
+
/// @param output Block key produced for output state, or `Keys.Empty`.
|
|
17
|
+
/// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
|
|
18
|
+
event Control(
|
|
19
|
+
uint indexed host,
|
|
20
|
+
uint id,
|
|
21
|
+
string name,
|
|
22
|
+
string request,
|
|
23
|
+
bytes4 state,
|
|
24
|
+
bytes4 output,
|
|
25
|
+
bool acceptsValue
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
emit EventAbi(ABI);
|
|
30
|
+
}
|
|
31
|
+
}
|
package/events/Query.sol
CHANGED
|
@@ -3,16 +3,16 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {EventEmitter} from "./Emitter.sol";
|
|
5
5
|
|
|
6
|
-
string constant ABI = "event Query(uint indexed host, uint id, string name, string
|
|
6
|
+
string constant ABI = "event Query(uint indexed host, uint id, string name, string request, string response)";
|
|
7
7
|
|
|
8
8
|
/// @notice Emitted once per query during host deployment to publish its request and response schemas.
|
|
9
9
|
abstract contract QueryEvent is EventEmitter {
|
|
10
10
|
/// @param host Host node ID that owns this query.
|
|
11
11
|
/// @param id Query node ID.
|
|
12
12
|
/// @param name Human-readable query name.
|
|
13
|
-
/// @param
|
|
14
|
-
/// @param
|
|
15
|
-
event Query(uint indexed host, uint id, string name, string
|
|
13
|
+
/// @param request Schema DSL string describing the query request shape.
|
|
14
|
+
/// @param response Schema DSL string describing the query response shape.
|
|
15
|
+
event Query(uint indexed host, uint id, string name, string request, string response);
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
18
|
emit EventAbi(ABI);
|
|
@@ -0,0 +1,24 @@
|
|
|
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/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
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 { 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
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import { RemoteBase } 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
|
+
string constant NAME = "remoteSettle";
|
|
11
|
+
|
|
12
|
+
/// @title RemoteSettle
|
|
13
|
+
/// @notice Remote that consumes remote-supplied TRANSACTION blocks through the shared transfer hook.
|
|
14
|
+
/// Each TRANSACTION block in the request calls `transfer(value)`. Restricted to trusted remotes.
|
|
15
|
+
abstract contract RemoteSettle is RemoteBase, TransferHook {
|
|
16
|
+
uint internal immutable remoteSettleId = remoteId(NAME);
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
emit Remote(host, remoteSettleId, NAME, Schemas.Transaction, false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// @notice Execute the remote-settle call.
|
|
23
|
+
function remoteSettle(bytes calldata request) external onlyRemote returns (bytes memory) {
|
|
24
|
+
(Cur memory state, , ) = cursor(request, 1);
|
|
25
|
+
|
|
26
|
+
while (state.i < state.bound) {
|
|
27
|
+
transfer(state.unpackTxValue());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
state.complete();
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
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 `Remote`)
|
|
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 remotes 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 remote nodes.
|
|
26
|
+
uint32 constant Remote = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Remote);
|
|
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 remote node ID.
|
|
41
|
+
function isRemote(uint id) internal pure returns (bool) {
|
|
42
|
+
return uint32(id >> 224) == Remote;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/// @notice Return true if `id` is a query node ID.
|
|
@@ -63,19 +63,19 @@ library Ids {
|
|
|
63
63
|
return bytes4(uint32(id >> 160));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/// @notice Assert that `id` is a
|
|
66
|
+
/// @notice Assert that `id` is a remote ID and return it unchanged.
|
|
67
67
|
/// @param id Node ID to validate.
|
|
68
|
-
/// @return pid The same `id` value if it is a
|
|
69
|
-
function
|
|
70
|
-
if (!
|
|
68
|
+
/// @return pid The same `id` value if it is a remote.
|
|
69
|
+
function remote(uint id) internal pure returns (uint pid) {
|
|
70
|
+
if (!isRemote(id)) revert InvalidId();
|
|
71
71
|
return id;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/// @notice Assert that `id` is a
|
|
74
|
+
/// @notice Assert that `id` is a remote ID and return its embedded ABI selector.
|
|
75
75
|
/// @param id Node ID to validate.
|
|
76
|
-
/// @return selector 4-byte
|
|
77
|
-
function
|
|
78
|
-
if (!
|
|
76
|
+
/// @return selector 4-byte remote selector stored in bits [191:160].
|
|
77
|
+
function remoteSelector(uint id) internal pure returns (bytes4 selector) {
|
|
78
|
+
if (!isRemote(id)) revert InvalidId();
|
|
79
79
|
return bytes4(uint32(id >> 160));
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -121,12 +121,12 @@ library Ids {
|
|
|
121
121
|
return id;
|
|
122
122
|
}
|
|
123
123
|
|
|
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(
|
|
124
|
+
/// @notice Build a chain-local remote ID for the given selector and contract.
|
|
125
|
+
/// @param selector 4-byte ABI selector of the remote entry point.
|
|
126
|
+
/// @param target Remote contract address.
|
|
127
|
+
/// @return Remote node ID embedding both the selector and address.
|
|
128
|
+
function toRemote(bytes4 selector, address target) internal view returns (uint) {
|
|
129
|
+
uint id = toLocalBase(Remote) | uint(uint160(target));
|
|
130
130
|
id |= uint(uint32(selector)) << 160;
|
|
131
131
|
return id;
|
|
132
132
|
}
|
|
@@ -143,7 +143,7 @@ library Ids {
|
|
|
143
143
|
|
|
144
144
|
/// @notice Extract the contract address from any local node ID.
|
|
145
145
|
/// Reverts if `id` does not belong to the local node family.
|
|
146
|
-
/// @param id Node ID (host, command, or
|
|
146
|
+
/// @param id Node ID (host, command, or remote).
|
|
147
147
|
/// @return Contract address in the lower 160 bits of `id`.
|
|
148
148
|
function nodeAddr(uint id) internal view returns (address) {
|
|
149
149
|
if (!isLocalFamily(id, Node)) revert InvalidId();
|
|
@@ -161,12 +161,12 @@ library Ids {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/// @title Selectors
|
|
164
|
-
/// @notice ABI-selector derivation helpers for command,
|
|
164
|
+
/// @notice ABI-selector derivation helpers for command, remote, and query dispatch.
|
|
165
165
|
library Selectors {
|
|
166
166
|
/// @dev ABI argument encoding for command entry points: `((bytes32,bytes,bytes))`.
|
|
167
167
|
string constant CommandArgs = "((bytes32,bytes,bytes))";
|
|
168
|
-
/// @dev ABI argument encoding for
|
|
169
|
-
string constant
|
|
168
|
+
/// @dev ABI argument encoding for remote entry points: `(bytes)`.
|
|
169
|
+
string constant RemoteArgs = "(bytes)";
|
|
170
170
|
/// @dev ABI argument encoding for query entry points: `(bytes)`.
|
|
171
171
|
string constant QueryArgs = "(bytes)";
|
|
172
172
|
|
|
@@ -178,12 +178,12 @@ library Selectors {
|
|
|
178
178
|
return bytes4(keccak256(bytes.concat(bytes(name), bytes(CommandArgs))));
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
/// @notice Derive the 4-byte ABI selector for a named
|
|
182
|
-
/// The selector is `keccak256(name ++
|
|
183
|
-
/// @param name
|
|
181
|
+
/// @notice Derive the 4-byte ABI selector for a named remote.
|
|
182
|
+
/// The selector is `keccak256(name ++ RemoteArgs)[0:4]`.
|
|
183
|
+
/// @param name Remote function name (without arguments).
|
|
184
184
|
/// @return 4-byte selector.
|
|
185
|
-
function
|
|
186
|
-
return bytes4(keccak256(bytes.concat(bytes(name), bytes(
|
|
185
|
+
function remote(string memory name) internal pure returns (bytes4) {
|
|
186
|
+
return bytes4(keccak256(bytes.concat(bytes(name), bytes(RemoteArgs))));
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
/// @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 remote).
|
|
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 remote contract.
|
|
54
|
+
uint8 constant Remote = 0x03;
|
|
55
55
|
/// @dev Node is a query contract.
|
|
56
56
|
uint8 constant Query = 0x04;
|
|
57
57
|
|