@rootzero/contracts 1.0.1 → 1.2.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 +2 -1
- package/Events.sol +1 -0
- package/blocks/Cursors.sol +176 -116
- package/blocks/Keys.sol +11 -7
- package/blocks/Schema.sol +11 -5
- package/blocks/Writers.sol +25 -9
- package/commands/Base.sol +10 -9
- package/commands/Burn.sol +3 -4
- package/commands/Credit.sol +3 -4
- package/commands/Debit.sol +3 -4
- package/commands/Deposit.sol +6 -8
- package/commands/Payout.sol +3 -4
- package/commands/Provision.sol +6 -8
- package/commands/Relay.sol +24 -32
- package/commands/Withdraw.sol +3 -4
- package/commands/admin/AllowAssets.sol +3 -4
- package/commands/admin/Allowance.sol +3 -3
- package/commands/admin/Appoint.sol +3 -4
- package/commands/admin/Authorize.sol +3 -4
- package/commands/admin/DenyAssets.sol +3 -4
- package/commands/admin/Destroy.sol +3 -4
- package/commands/admin/Dismiss.sol +3 -4
- package/commands/admin/Execute.sol +6 -7
- package/commands/admin/Init.sol +3 -4
- package/commands/admin/Label.sol +35 -0
- package/commands/admin/Unauthorize.sol +3 -4
- package/core/Calls.sol +3 -3
- package/core/Host.sol +7 -11
- package/core/Payable.sol +12 -10
- package/core/Pipeline.sol +4 -4
- package/docs/Schema.md +13 -6
- package/events/Admin.sol +2 -4
- package/events/Chain.sol +2 -3
- package/events/Command.sol +2 -4
- package/events/Guard.sol +2 -3
- package/events/Introduction.sol +2 -4
- package/events/Labeled.sol +18 -0
- package/events/Peer.sol +2 -11
- package/events/Query.sol +2 -3
- package/guards/Base.sol +7 -6
- package/guards/Revoke.sol +3 -4
- package/package.json +2 -1
- package/peer/AllowAssets.sol +3 -3
- package/peer/Allowance.sol +3 -3
- package/peer/BalancePull.sol +3 -3
- package/peer/Base.sol +7 -6
- package/peer/DenyAssets.sol +3 -3
- package/peer/Dispatch.sol +13 -23
- package/peer/Pipe.sol +7 -7
- package/peer/Settle.sol +3 -3
- package/queries/Assets.sol +3 -3
- package/queries/Balances.sol +3 -3
- package/queries/Base.sol +9 -8
- package/queries/Positions.sol +3 -4
- package/utils/Value.sol +5 -4
package/core/Pipeline.sol
CHANGED
|
@@ -20,14 +20,14 @@ abstract contract Pipeline is Payable {
|
|
|
20
20
|
/// @param account Account identifier for the piped context.
|
|
21
21
|
/// @param state Current threaded state block stream.
|
|
22
22
|
/// @param request Step request block stream.
|
|
23
|
-
/// @param value Native value assigned to this step.
|
|
23
|
+
/// @param value Native EVM value assigned to this step.
|
|
24
24
|
/// @return Updated state block stream for the next step.
|
|
25
25
|
function dispatch(
|
|
26
26
|
uint target,
|
|
27
27
|
bytes32 account,
|
|
28
28
|
bytes memory state,
|
|
29
29
|
bytes calldata request,
|
|
30
|
-
|
|
30
|
+
uint128 value
|
|
31
31
|
) internal virtual returns (bytes memory);
|
|
32
32
|
|
|
33
33
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
@@ -45,8 +45,8 @@ abstract contract Pipeline is Payable {
|
|
|
45
45
|
(Cur memory input, , ) = Cursors.init(steps, 0, 1);
|
|
46
46
|
|
|
47
47
|
while (input.i < input.len) {
|
|
48
|
-
(uint target, uint
|
|
49
|
-
state = dispatch(target, account, state, request, useValue(budget,
|
|
48
|
+
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
|
49
|
+
state = dispatch(target, account, state, request, useValue(budget, resources));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (state.length != 0) revert UnexpectedState();
|
package/docs/Schema.md
CHANGED
|
@@ -56,9 +56,9 @@ A block payload has fixed fields first, followed by an optional child-block tail
|
|
|
56
56
|
Once a child block appears, no more fixed fields may follow.
|
|
57
57
|
|
|
58
58
|
```txt
|
|
59
|
-
#call { uint target, uint
|
|
59
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
60
60
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
|
61
|
-
#pipe { uint
|
|
61
|
+
#pipe { uint resources, #context { bytes32 account, #bytes as state, #bytes as steps } }
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The tail is embedded directly as child block bytes. There is no wrapper around a
|
|
@@ -108,7 +108,7 @@ or runtime keys.
|
|
|
108
108
|
|
|
109
109
|
```txt
|
|
110
110
|
maybe #account { bytes32 account } as recipient
|
|
111
|
-
#call { uint target, uint
|
|
111
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
Aliases may be used on any block item, including child blocks and prime items.
|
|
@@ -131,6 +131,13 @@ Integers are encoded big-endian. Signed integers use two's-complement encoding
|
|
|
131
131
|
for their declared width. `bool` is one byte: `0x00` for false and `0x01` for
|
|
132
132
|
true. `bytesN` values are encoded as exactly `N` bytes with no padding.
|
|
133
133
|
|
|
134
|
+
## Chain Resources
|
|
135
|
+
|
|
136
|
+
Fields named `resources` are chain-specific resource words. Different chain
|
|
137
|
+
types may pack these words differently, but a given chain type must use one
|
|
138
|
+
stable format everywhere. For EVM chains, the low 128 bits are native value /
|
|
139
|
+
endowment in wei; higher bits are reserved for execution resources such as gas.
|
|
140
|
+
|
|
134
141
|
## Identifiers
|
|
135
142
|
|
|
136
143
|
Block names, field names, and aliases use lower camelCase ASCII identifiers:
|
|
@@ -185,10 +192,10 @@ Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
|
185
192
|
#amount { bytes32 asset, bytes32 meta, uint amount }
|
|
186
193
|
#balance { bytes32 asset, bytes32 meta, uint amount }
|
|
187
194
|
#custody { uint host, bytes32 asset, bytes32 meta, uint amount }
|
|
188
|
-
#call { uint target, uint
|
|
189
|
-
#step { uint target, uint
|
|
195
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
196
|
+
#step { uint target, uint resources, #bytes as request }
|
|
190
197
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
|
191
|
-
#pipe { uint
|
|
198
|
+
#pipe { uint resources, #context { bytes32 account, #bytes as state, #bytes as steps } }
|
|
192
199
|
#auth { uint cid, uint deadline, #bytes as proof }
|
|
193
200
|
```
|
|
194
201
|
|
package/events/Admin.sol
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted once per admin command during host deployment to publish its request schema and state keys.
|
|
7
7
|
abstract contract AdminEvent is EventEmitter {
|
|
8
8
|
string private constant ABI =
|
|
9
|
-
"event Admin(uint indexed host, uint id,
|
|
9
|
+
"event Admin(uint indexed host, uint id, bytes32 shape, string request, bytes4 state, bytes4 output, bool postcheck, bool funded)";
|
|
10
10
|
|
|
11
11
|
/// @param host Host node ID that owns this admin command.
|
|
12
12
|
/// @param id Command node ID.
|
|
13
|
-
/// @param name Human-readable command name.
|
|
14
13
|
/// @param shape Per-operation block counts encoded as `request:state:output`.
|
|
15
14
|
/// The request count covers only the input request run. If `postcheck` is true,
|
|
16
15
|
/// a constraint run follows the input run, or starts the request when `request`
|
|
@@ -23,7 +22,6 @@ abstract contract AdminEvent is EventEmitter {
|
|
|
23
22
|
event Admin(
|
|
24
23
|
uint indexed host,
|
|
25
24
|
uint id,
|
|
26
|
-
string name,
|
|
27
25
|
bytes32 shape,
|
|
28
26
|
string request,
|
|
29
27
|
bytes4 state,
|
package/events/Chain.sol
CHANGED
|
@@ -5,14 +5,13 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when a chain/domain node is announced.
|
|
7
7
|
abstract contract ChainEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin
|
|
8
|
+
string private constant ABI = "event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin)";
|
|
9
9
|
|
|
10
10
|
/// @param chain Chain node ID.
|
|
11
11
|
/// @param native Native asset ID for the chain.
|
|
12
12
|
/// @param commander Commander host node ID for the chain.
|
|
13
13
|
/// @param admin Admin account for the commander host on the chain.
|
|
14
|
-
|
|
15
|
-
event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin, string name);
|
|
14
|
+
event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin);
|
|
16
15
|
|
|
17
16
|
constructor() {
|
|
18
17
|
emit EventAbi(ABI);
|
package/events/Command.sol
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted once per command during host deployment to publish its request schema and state keys.
|
|
7
7
|
abstract contract CommandEvent is EventEmitter {
|
|
8
8
|
string private constant ABI =
|
|
9
|
-
"event Command(uint indexed host, uint id,
|
|
9
|
+
"event Command(uint indexed host, uint id, bytes32 shape, string request, bytes4 state, bytes4 output, bool postcheck, bool funded)";
|
|
10
10
|
|
|
11
11
|
/// @param host Host node ID that owns this command.
|
|
12
12
|
/// @param id Command node ID.
|
|
13
|
-
/// @param name Human-readable command name.
|
|
14
13
|
/// @param shape Per-operation block counts encoded as `request:state:output`.
|
|
15
14
|
/// The request count covers only the input request run. If `postcheck` is true,
|
|
16
15
|
/// a constraint run follows the input run, or starts the request when `request`
|
|
@@ -23,7 +22,6 @@ abstract contract CommandEvent is EventEmitter {
|
|
|
23
22
|
event Command(
|
|
24
23
|
uint indexed host,
|
|
25
24
|
uint id,
|
|
26
|
-
string name,
|
|
27
25
|
bytes32 shape,
|
|
28
26
|
string request,
|
|
29
27
|
bytes4 state,
|
package/events/Guard.sol
CHANGED
|
@@ -5,13 +5,12 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted once per guard action during host deployment to publish its request schema.
|
|
7
7
|
abstract contract GuardEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Guard(uint indexed host, uint id, string
|
|
8
|
+
string private constant ABI = "event Guard(uint indexed host, uint id, string request)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID that owns this guard action.
|
|
11
11
|
/// @param id Guard action node ID.
|
|
12
|
-
/// @param name Human-readable guard action name.
|
|
13
12
|
/// @param request Schema DSL string describing the guard action request shape.
|
|
14
|
-
event Guard(uint indexed host, uint id, string
|
|
13
|
+
event Guard(uint indexed host, uint id, string request);
|
|
15
14
|
|
|
16
15
|
constructor() {
|
|
17
16
|
emit EventAbi(ABI);
|
package/events/Introduction.sol
CHANGED
|
@@ -5,13 +5,11 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when a host introduces itself to another host.
|
|
7
7
|
abstract contract IntroductionEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Introduction(uint indexed host, uint blocknum
|
|
8
|
+
string private constant ABI = "event Introduction(uint indexed host, uint blocknum)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID of the introducing contract.
|
|
11
11
|
/// @param blocknum Block number at which the host was deployed.
|
|
12
|
-
|
|
13
|
-
/// @param namespace Human-readable namespace string for the host.
|
|
14
|
-
event Introduction(uint indexed host, uint blocknum, uint16 version, string namespace);
|
|
12
|
+
event Introduction(uint indexed host, uint blocknum);
|
|
15
13
|
|
|
16
14
|
constructor() {
|
|
17
15
|
emit EventAbi(ABI);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Emitted to attach a human-readable namespaced label to a node ID.
|
|
7
|
+
abstract contract LabeledEvent is EventEmitter {
|
|
8
|
+
string private constant ABI = "event Labeled(uint indexed id, bytes32 namespace, string name)";
|
|
9
|
+
|
|
10
|
+
/// @param id Node or capability ID being labeled.
|
|
11
|
+
/// @param namespace Label namespace.
|
|
12
|
+
/// @param name Human-readable name within the namespace.
|
|
13
|
+
event Labeled(uint indexed id, bytes32 namespace, string name);
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
emit EventAbi(ABI);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/events/Peer.sol
CHANGED
|
@@ -6,24 +6,15 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
6
6
|
/// @notice Emitted once per peer during host deployment to publish its request and response schemas.
|
|
7
7
|
abstract contract PeerEvent is EventEmitter {
|
|
8
8
|
string private constant ABI =
|
|
9
|
-
"event Peer(uint indexed host, uint id,
|
|
9
|
+
"event Peer(uint indexed host, uint id, bytes32 shape, string request, string response, bool funded)";
|
|
10
10
|
|
|
11
11
|
/// @param host Host node ID that owns this peer.
|
|
12
12
|
/// @param id Peer node ID.
|
|
13
|
-
/// @param name Human-readable peer name.
|
|
14
13
|
/// @param shape Per-operation block counts encoded as `request:response`.
|
|
15
14
|
/// @param request Schema DSL string describing the input request run, or empty if none.
|
|
16
15
|
/// @param response Schema DSL string describing the peer response shape.
|
|
17
16
|
/// @param funded Whether the peer entrypoint accepts nonzero `msg.value`.
|
|
18
|
-
event Peer(
|
|
19
|
-
uint indexed host,
|
|
20
|
-
uint id,
|
|
21
|
-
string name,
|
|
22
|
-
bytes32 shape,
|
|
23
|
-
string request,
|
|
24
|
-
string response,
|
|
25
|
-
bool funded
|
|
26
|
-
);
|
|
17
|
+
event Peer(uint indexed host, uint id, bytes32 shape, string request, string response, bool funded);
|
|
27
18
|
|
|
28
19
|
constructor() {
|
|
29
20
|
emit EventAbi(ABI);
|
package/events/Query.sol
CHANGED
|
@@ -5,15 +5,14 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted once per query during host deployment to publish its request and response schemas.
|
|
7
7
|
abstract contract QueryEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Query(uint indexed host, uint id,
|
|
8
|
+
string private constant ABI = "event Query(uint indexed host, uint id, bytes32 shape, string request, string response)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID that owns this query.
|
|
11
11
|
/// @param id Query node ID.
|
|
12
|
-
/// @param name Human-readable query name.
|
|
13
12
|
/// @param shape Per-operation block counts encoded as `request:response`.
|
|
14
13
|
/// @param request Schema DSL string describing the input request run, or empty if none.
|
|
15
14
|
/// @param response Schema DSL string describing the query response shape.
|
|
16
|
-
event Query(uint indexed host, uint id,
|
|
15
|
+
event Query(uint indexed host, uint id, bytes32 shape, string request, string response);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/guards/Base.sol
CHANGED
|
@@ -3,7 +3,8 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {AccessControl} from "../core/Access.sol";
|
|
5
5
|
import {GuardEvent} from "../events/Guard.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {LabeledEvent} from "../events/Labeled.sol";
|
|
7
|
+
import {Ids} from "../utils/Ids.sol";
|
|
7
8
|
|
|
8
9
|
/// @notice ABI-encode a guard action call from a target guard ID and request block stream.
|
|
9
10
|
/// @dev Derives the function selector from `target` via `Ids.guardSelector(target)`.
|
|
@@ -19,17 +20,17 @@ function encodeGuardCall(uint target, bytes calldata request) pure returns (byte
|
|
|
19
20
|
/// @title GuardBase
|
|
20
21
|
/// @notice Abstract base for guardian-only direct host actions.
|
|
21
22
|
/// Guard actions are non-payable direct calls with no command context, state, or response.
|
|
22
|
-
abstract contract GuardBase is AccessControl, GuardEvent {
|
|
23
|
+
abstract contract GuardBase is AccessControl, GuardEvent, LabeledEvent {
|
|
23
24
|
/// @dev Restrict execution to active guardian addresses.
|
|
24
25
|
modifier onlyGuardian() {
|
|
25
26
|
if (!isGuardian(msg.sender)) revert AccessDenied();
|
|
26
27
|
_;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/// @notice Derive the deterministic node ID for a
|
|
30
|
-
/// @param
|
|
30
|
+
/// @notice Derive the deterministic node ID for a guard action selector on this contract.
|
|
31
|
+
/// @param selector Guard action entrypoint selector.
|
|
31
32
|
/// @return Guard action node ID.
|
|
32
|
-
function guardId(
|
|
33
|
-
return Ids.toGuard(
|
|
33
|
+
function guardId(bytes4 selector) internal view returns (uint) {
|
|
34
|
+
return Ids.toGuard(selector, address(this));
|
|
34
35
|
}
|
|
35
36
|
}
|
package/guards/Revoke.sol
CHANGED
|
@@ -10,12 +10,11 @@ using Cursors for Cur;
|
|
|
10
10
|
/// Each NODE block in the request is deauthorized on the host.
|
|
11
11
|
/// Only callable by active guardian addresses.
|
|
12
12
|
abstract contract Revoke is GuardBase {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
uint internal immutable revokeId = guardId(NAME);
|
|
13
|
+
uint internal immutable revokeId = guardId(this.revoke.selector);
|
|
16
14
|
|
|
17
15
|
constructor() {
|
|
18
|
-
emit Guard(host, revokeId,
|
|
16
|
+
emit Guard(host, revokeId, Schemas.Node);
|
|
17
|
+
emit Labeled(revokeId, bytes32(0), "revoke");
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
function revoke(bytes calldata request) external onlyGuardian {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootzero/contracts",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"**/*.sol",
|
|
10
10
|
"README.md",
|
|
11
|
+
"CHANGELOG.md",
|
|
11
12
|
"LICENSE",
|
|
12
13
|
"docs/Schema.md"
|
|
13
14
|
],
|
package/peer/AllowAssets.sol
CHANGED
|
@@ -11,11 +11,11 @@ using Cursors for Cur;
|
|
|
11
11
|
/// @notice Peer that permits a list of (asset, meta) pairs on behalf of a peer host.
|
|
12
12
|
/// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
|
|
13
13
|
abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
|
|
14
|
-
|
|
15
|
-
uint internal immutable peerAllowAssetsId = peerId(NAME);
|
|
14
|
+
uint internal immutable peerAllowAssetsId = peerId(this.peerAllowAssets.selector);
|
|
16
15
|
|
|
17
16
|
constructor() {
|
|
18
|
-
emit Peer(host, peerAllowAssetsId,
|
|
17
|
+
emit Peer(host, peerAllowAssetsId, "1:0", Schemas.Asset, "", false);
|
|
18
|
+
emit Labeled(peerAllowAssetsId, bytes32(0), "peerAllowAssets");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the allow-assets peer call.
|
package/peer/Allowance.sol
CHANGED
|
@@ -12,11 +12,11 @@ using Cursors for Cur;
|
|
|
12
12
|
/// Each AMOUNT block in the request is scoped to the peer host and passed to the
|
|
13
13
|
/// shared allowance hook as a host-scoped allowance. Restricted to trusted peers.
|
|
14
14
|
abstract contract PeerAllowance is PeerBase, AllowanceHook {
|
|
15
|
-
|
|
16
|
-
uint internal immutable peerAllowanceId = peerId(NAME);
|
|
15
|
+
uint internal immutable peerAllowanceId = peerId(this.peerAllowance.selector);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
|
-
emit Peer(host, peerAllowanceId,
|
|
18
|
+
emit Peer(host, peerAllowanceId, "1:0", Schemas.Amount, "", false);
|
|
19
|
+
emit Labeled(peerAllowanceId, bytes32(0), "peerAllowance");
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/// @notice Execute the allowance peer call.
|
package/peer/BalancePull.sol
CHANGED
|
@@ -20,11 +20,11 @@ abstract contract BalancePullHook {
|
|
|
20
20
|
/// Each BALANCE block in the request calls `balancePull(peer, asset, meta, amount)`.
|
|
21
21
|
/// Restricted to trusted peers.
|
|
22
22
|
abstract contract PeerBalancePull is PeerBase, BalancePullHook {
|
|
23
|
-
|
|
24
|
-
uint internal immutable peerBalancePullId = peerId(NAME);
|
|
23
|
+
uint internal immutable peerBalancePullId = peerId(this.peerBalancePull.selector);
|
|
25
24
|
|
|
26
25
|
constructor() {
|
|
27
|
-
emit Peer(host, peerBalancePullId,
|
|
26
|
+
emit Peer(host, peerBalancePullId, "1:0", Schemas.Balance, "", false);
|
|
27
|
+
emit Labeled(peerBalancePullId, bytes32(0), "peerBalancePull");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/// @notice Execute the balance-pull peer call.
|
package/peer/Base.sol
CHANGED
|
@@ -3,7 +3,8 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { NodeCalls } from "../core/Calls.sol";
|
|
5
5
|
import { PeerEvent } from "../events/Peer.sol";
|
|
6
|
-
import {
|
|
6
|
+
import { LabeledEvent } from "../events/Labeled.sol";
|
|
7
|
+
import { Ids } from "../utils/Ids.sol";
|
|
7
8
|
|
|
8
9
|
/// @notice ABI-encode a peer call from a target peer ID and request block stream.
|
|
9
10
|
/// @dev Derives the function selector from `target` via `Ids.peerSelector(target)`.
|
|
@@ -20,7 +21,7 @@ function encodePeerCall(uint target, bytes calldata request) pure returns (bytes
|
|
|
20
21
|
/// @notice Abstract base for all rootzero peer contracts.
|
|
21
22
|
/// Peers handle inter-host operations and asset allow/deny management
|
|
22
23
|
/// between cooperating hosts. Access is restricted to trusted callers via `onlyPeer`.
|
|
23
|
-
abstract contract PeerBase is NodeCalls, PeerEvent {
|
|
24
|
+
abstract contract PeerBase is NodeCalls, PeerEvent, LabeledEvent {
|
|
24
25
|
/// @dev Thrown when the commander attempts to call a peer entrypoint directly.
|
|
25
26
|
error CommanderNotAllowed();
|
|
26
27
|
|
|
@@ -31,10 +32,10 @@ abstract contract PeerBase is NodeCalls, PeerEvent {
|
|
|
31
32
|
_;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
/// @notice Derive the deterministic node ID for a
|
|
35
|
-
/// @param
|
|
35
|
+
/// @notice Derive the deterministic node ID for a peer selector on this contract.
|
|
36
|
+
/// @param selector Peer entrypoint selector.
|
|
36
37
|
/// @return Peer node ID.
|
|
37
|
-
function peerId(
|
|
38
|
-
return Ids.toPeer(
|
|
38
|
+
function peerId(bytes4 selector) internal view returns (uint) {
|
|
39
|
+
return Ids.toPeer(selector, address(this));
|
|
39
40
|
}
|
|
40
41
|
}
|
package/peer/DenyAssets.sol
CHANGED
|
@@ -11,11 +11,11 @@ using Cursors for Cur;
|
|
|
11
11
|
/// @notice Peer that blocks a list of (asset, meta) pairs on behalf of a peer host.
|
|
12
12
|
/// Each ASSET block in the request calls `denyAsset`. Restricted to trusted peers.
|
|
13
13
|
abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook {
|
|
14
|
-
|
|
15
|
-
uint internal immutable peerDenyAssetsId = peerId(NAME);
|
|
14
|
+
uint internal immutable peerDenyAssetsId = peerId(this.peerDenyAssets.selector);
|
|
16
15
|
|
|
17
16
|
constructor() {
|
|
18
|
-
emit Peer(host, peerDenyAssetsId,
|
|
17
|
+
emit Peer(host, peerDenyAssetsId, "1:0", Schemas.Asset, "", false);
|
|
18
|
+
emit Labeled(peerDenyAssetsId, bytes32(0), "peerDenyAssets");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the deny-assets peer call.
|
package/peer/Dispatch.sol
CHANGED
|
@@ -4,29 +4,19 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
import { PeerBase } from "./Base.sol";
|
|
5
5
|
import { Payable } from "../core/Payable.sol";
|
|
6
6
|
import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
7
|
+
import { DispatchPayableHook } from "../commands/Relay.sol";
|
|
7
8
|
import { Budget } from "../utils/Value.sol";
|
|
8
9
|
|
|
9
10
|
using Cursors for Cur;
|
|
10
11
|
|
|
11
|
-
abstract contract PeerDispatchPayableHook {
|
|
12
|
-
/// @notice Override to dispatch an already encoded payload to `chain`.
|
|
13
|
-
/// @param chain Destination chain node ID.
|
|
14
|
-
/// @param resources Chain-adapter-specific destination resources. EVM adapters
|
|
15
|
-
/// may interpret this as packed execution gas and destination value.
|
|
16
|
-
/// @param payload Encoded payload ready for the transport layer.
|
|
17
|
-
/// @param budget Source-chain native-value budget available for transport
|
|
18
|
-
/// fees and destination resource funding.
|
|
19
|
-
function dispatch(uint chain, uint resources, bytes calldata payload, Budget memory budget) internal virtual;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
12
|
/// @title PeerDispatchPayable
|
|
23
13
|
/// @notice Peer endpoint that forwards DISPATCH blocks to a host-defined dispatch hook.
|
|
24
|
-
abstract contract PeerDispatchPayable is PeerBase, Payable,
|
|
25
|
-
|
|
26
|
-
uint internal immutable peerDispatchPayableId = peerId(NAME);
|
|
14
|
+
abstract contract PeerDispatchPayable is PeerBase, Payable, DispatchPayableHook {
|
|
15
|
+
uint internal immutable peerDispatchPayableId = peerId(this.peerDispatchPayable.selector);
|
|
27
16
|
|
|
28
17
|
constructor() {
|
|
29
|
-
emit Peer(host, peerDispatchPayableId,
|
|
18
|
+
emit Peer(host, peerDispatchPayableId, "1:0", Schemas.Dispatch, "", true);
|
|
19
|
+
emit Labeled(peerDispatchPayableId, bytes32(0), "peerDispatchPayable");
|
|
30
20
|
}
|
|
31
21
|
|
|
32
22
|
/// @notice Forward peer-supplied dispatches to the host-defined dispatch hook.
|
|
@@ -36,14 +26,14 @@ abstract contract PeerDispatchPayable is PeerBase, Payable, PeerDispatchPayableH
|
|
|
36
26
|
/// @return output Empty response bytes.
|
|
37
27
|
function peerDispatchPayable(bytes calldata request) external payable onlyPeer returns (bytes memory output) {
|
|
38
28
|
(Cur memory input, , ) = Cursors.init(request, 0, 1);
|
|
39
|
-
Budget memory budget = valueBudget();
|
|
40
|
-
|
|
41
|
-
while (input.i < input.len) {
|
|
42
|
-
(uint chain, uint resources, bytes calldata payload) = input.unpackDispatch();
|
|
43
|
-
dispatch(chain, resources, payload, budget);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
input.complete();
|
|
29
|
+
Budget memory budget = valueBudget();
|
|
30
|
+
|
|
31
|
+
while (input.i < input.len) {
|
|
32
|
+
(uint chain, uint resources, bytes calldata payload) = input.unpackDispatch();
|
|
33
|
+
dispatch(chain, resources, bytes(payload), budget);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
input.complete();
|
|
47
37
|
return "";
|
|
48
38
|
}
|
|
49
39
|
|
package/peer/Pipe.sol
CHANGED
|
@@ -10,18 +10,18 @@ using Cursors for Cur;
|
|
|
10
10
|
|
|
11
11
|
/// @title PeerPipePayable
|
|
12
12
|
/// @notice Peer that consumes PIPE blocks and executes each context step stream.
|
|
13
|
-
/// Each PIPE block carries
|
|
13
|
+
/// Each PIPE block carries chain resources plus a CONTEXT block; the nested
|
|
14
14
|
/// context steps are passed to the shared pipeline as the step stream.
|
|
15
15
|
abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
16
|
-
|
|
17
|
-
uint internal immutable peerPipePayableId = peerId(NAME);
|
|
16
|
+
uint internal immutable peerPipePayableId = peerId(this.peerPipePayable.selector);
|
|
18
17
|
|
|
19
18
|
constructor() {
|
|
20
|
-
emit Peer(host, peerPipePayableId,
|
|
19
|
+
emit Peer(host, peerPipePayableId, "1:0", Schemas.Pipe, "", true);
|
|
20
|
+
emit Labeled(peerPipePayableId, bytes32(0), "peerPipePayable");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/// @notice Execute peer-supplied pipes through the shared payable pipe.
|
|
24
|
-
/// @dev Each pipe receives its own explicit value sub-budget. Any top-level
|
|
24
|
+
/// @dev Each pipe receives its own explicit EVM value sub-budget. Any top-level
|
|
25
25
|
/// `msg.value` not assigned to a pipe remains on this host.
|
|
26
26
|
/// @param request PIPE block stream supplied by the trusted peer.
|
|
27
27
|
/// @return Empty response bytes.
|
|
@@ -30,8 +30,8 @@ abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
|
30
30
|
Budget memory budget = valueBudget();
|
|
31
31
|
|
|
32
32
|
while (input.i < input.len) {
|
|
33
|
-
(uint
|
|
34
|
-
pipe(account, state, steps, allocateValue(budget,
|
|
33
|
+
(uint resources, bytes32 account, bytes calldata state, bytes calldata steps) = input.unpackPipe();
|
|
34
|
+
pipe(account, state, steps, allocateValue(budget, resources));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
input.complete();
|
package/peer/Settle.sol
CHANGED
|
@@ -12,11 +12,11 @@ using Cursors for Cur;
|
|
|
12
12
|
/// @notice Peer that consumes peer-supplied TRANSACTION blocks through debit and credit hooks.
|
|
13
13
|
/// Each TRANSACTION block calls `debitAccount` for `from` and `creditAccount` for `to`.
|
|
14
14
|
abstract contract PeerSettle is PeerBase, DebitAccountHook, CreditAccountHook {
|
|
15
|
-
|
|
16
|
-
uint internal immutable peerSettleId = peerId(NAME);
|
|
15
|
+
uint internal immutable peerSettleId = peerId(this.peerSettle.selector);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
|
-
emit Peer(host, peerSettleId,
|
|
18
|
+
emit Peer(host, peerSettleId, "1:0", Schemas.Transaction, "", false);
|
|
19
|
+
emit Labeled(peerSettleId, bytes32(0), "peerSettle");
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/// @notice Execute the peer-settle call.
|
package/queries/Assets.sol
CHANGED
|
@@ -22,11 +22,11 @@ abstract contract AssetStatusHook {
|
|
|
22
22
|
/// The request is a run of `ASSET` blocks.
|
|
23
23
|
/// The response returns one `STATUS` form block per query entry, preserving request order.
|
|
24
24
|
abstract contract AssetStatus is QueryBase, AssetStatusHook {
|
|
25
|
-
|
|
26
|
-
uint public immutable assetStatusId = queryId(NAME);
|
|
25
|
+
uint public immutable assetStatusId = queryId(this.assetStatus.selector);
|
|
27
26
|
|
|
28
27
|
constructor() {
|
|
29
|
-
emit Query(host, assetStatusId,
|
|
28
|
+
emit Query(host, assetStatusId, "1:1", Schemas.Asset, Forms.Status);
|
|
29
|
+
emit Labeled(assetStatusId, bytes32(0), "assetStatus");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/// @notice Resolve asset support status for a run of requested `(asset, meta)` tuples.
|
package/queries/Balances.sol
CHANGED
|
@@ -22,11 +22,11 @@ abstract contract GetBalancesHook {
|
|
|
22
22
|
/// The request is a run of `ACCOUNT_ASSET` form blocks.
|
|
23
23
|
/// The response returns one `ACCOUNT_AMOUNT` form block per requested position, preserving request order.
|
|
24
24
|
abstract contract GetBalances is QueryBase, GetBalancesHook {
|
|
25
|
-
|
|
26
|
-
uint public immutable getBalancesId = queryId(NAME);
|
|
25
|
+
uint public immutable getBalancesId = queryId(this.getBalances.selector);
|
|
27
26
|
|
|
28
27
|
constructor() {
|
|
29
|
-
emit Query(host, getBalancesId,
|
|
28
|
+
emit Query(host, getBalancesId, "1:1", Forms.AccountAsset, Forms.AccountAmount);
|
|
29
|
+
emit Labeled(getBalancesId, bytes32(0), "getBalances");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/// @notice Resolve balances for a run of requested `(account, asset, meta)` tuples.
|
package/queries/Base.sol
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import { Runtime } from "../core/Runtime.sol";
|
|
5
|
+
import { LabeledEvent } from "../events/Labeled.sol";
|
|
5
6
|
import { QueryEvent } from "../events/Query.sol";
|
|
6
|
-
import { Ids
|
|
7
|
+
import { Ids } from "../utils/Ids.sol";
|
|
7
8
|
|
|
8
9
|
/// @notice ABI-encode a query call from a target query ID and request block stream.
|
|
9
10
|
/// @dev Derives the function selector from `target` via `Ids.querySelector(target)`.
|
|
@@ -20,14 +21,14 @@ function encodeQueryCall(uint target, bytes calldata request) pure returns (byte
|
|
|
20
21
|
/// @notice Abstract base for rootzero query contracts.
|
|
21
22
|
/// Queries are view-only entry points that consume a block-stream request and
|
|
22
23
|
/// return a block-stream response.
|
|
23
|
-
abstract contract QueryBase is Runtime, QueryEvent {
|
|
24
|
+
abstract contract QueryBase is Runtime, QueryEvent, LabeledEvent {
|
|
24
25
|
|
|
25
|
-
/// @notice Derive the deterministic node ID for a
|
|
26
|
-
/// The ID encodes the ABI selector
|
|
27
|
-
///
|
|
28
|
-
/// @param
|
|
26
|
+
/// @notice Derive the deterministic node ID for a query selector on this contract.
|
|
27
|
+
/// The ID encodes the ABI selector and `address(this)`, making it unique
|
|
28
|
+
/// per (function selector, contract address) pair.
|
|
29
|
+
/// @param selector Query entrypoint selector.
|
|
29
30
|
/// @return Query node ID.
|
|
30
|
-
function queryId(
|
|
31
|
-
return Ids.toQuery(
|
|
31
|
+
function queryId(bytes4 selector) internal view returns (uint) {
|
|
32
|
+
return Ids.toQuery(selector, address(this));
|
|
32
33
|
}
|
|
33
34
|
}
|
package/queries/Positions.sol
CHANGED
|
@@ -29,12 +29,11 @@ abstract contract GetPositionHook {
|
|
|
29
29
|
/// The request is a run of `ACCOUNT_ASSET` form blocks.
|
|
30
30
|
/// The response returns one output-schema block per position entry, preserving request order.
|
|
31
31
|
abstract contract GetPosition is QueryBase, GetPositionHook {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
uint public immutable getPositionId = queryId(NAME);
|
|
32
|
+
uint public immutable getPositionId = queryId(this.getPosition.selector);
|
|
35
33
|
|
|
36
34
|
constructor(string memory output) {
|
|
37
|
-
emit Query(host, getPositionId,
|
|
35
|
+
emit Query(host, getPositionId, "1:1", Forms.AccountAsset, output);
|
|
36
|
+
emit Labeled(getPositionId, bytes32(0), "getPosition");
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/// @notice Resolve positions for a run of requested `(account, asset, meta)` tuples.
|
package/utils/Value.sol
CHANGED
|
@@ -18,9 +18,10 @@ library Values {
|
|
|
18
18
|
/// @param budget Mutable budget to deduct from.
|
|
19
19
|
/// @param amount Native value to spend in wei.
|
|
20
20
|
/// @return The same `amount`, ready to forward to a callee.
|
|
21
|
-
function use(Budget memory budget,
|
|
22
|
-
|
|
23
|
-
budget.remaining
|
|
21
|
+
function use(Budget memory budget, uint128 amount) internal pure returns (uint128) {
|
|
22
|
+
uint value = uint(amount);
|
|
23
|
+
if (value > budget.remaining) revert InsufficientValue();
|
|
24
|
+
budget.remaining -= value;
|
|
24
25
|
return amount;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -29,7 +30,7 @@ library Values {
|
|
|
29
30
|
/// @param budget Mutable parent budget to deduct from.
|
|
30
31
|
/// @param amount Native value to assign to the sub-budget, in wei.
|
|
31
32
|
/// @return A new budget with `amount` remaining.
|
|
32
|
-
function allocate(Budget memory budget,
|
|
33
|
+
function allocate(Budget memory budget, uint128 amount) internal pure returns (Budget memory) {
|
|
33
34
|
return Budget({remaining: use(budget, amount)});
|
|
34
35
|
}
|
|
35
36
|
}
|