@rootzero/contracts 1.1.0 → 1.3.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 +23 -0
- package/Endpoints.sol +10 -7
- package/Events.sol +1 -0
- package/README.md +300 -79
- package/blocks/Cursors.sol +49 -14
- package/blocks/Keys.sol +4 -0
- package/blocks/Schema.sol +10 -4
- package/blocks/Writers.sol +16 -0
- package/commands/Base.sol +10 -9
- package/commands/Burn.sol +4 -5
- package/commands/Credit.sol +4 -5
- package/commands/Debit.sol +4 -5
- package/commands/Deposit.sol +8 -10
- package/commands/Payout.sol +5 -6
- package/commands/Provision.sol +8 -10
- package/commands/Relay.sol +4 -5
- package/commands/Withdraw.sol +4 -5
- package/commands/admin/AllowAssets.sol +4 -5
- package/commands/admin/Allowance.sol +4 -4
- package/commands/admin/Appoint.sol +4 -5
- package/commands/admin/Authorize.sol +4 -5
- package/commands/admin/DenyAssets.sol +4 -5
- package/commands/admin/Destroy.sol +3 -4
- package/commands/admin/Dismiss.sol +4 -5
- package/commands/admin/Execute.sol +4 -5
- package/commands/admin/Init.sol +3 -4
- package/commands/admin/Label.sol +35 -0
- package/commands/admin/Unauthorize.sol +4 -5
- package/core/Host.sol +7 -11
- package/core/Pipeline.sol +1 -1
- package/docs/Schema.md +59 -2
- package/events/Admin.sol +3 -9
- package/events/Chain.sol +2 -3
- package/events/Command.sol +3 -9
- package/events/Guard.sol +2 -3
- package/events/Introduction.sol +2 -4
- package/events/Labeled.sol +21 -0
- package/events/Peer.sol +2 -11
- package/events/Query.sol +2 -3
- package/events/Transfer.sol +1 -1
- package/guards/Base.sol +7 -6
- package/guards/Revoke.sol +4 -5
- package/package.json +1 -1
- package/peer/AllowAssets.sol +9 -5
- package/peer/Allowance.sol +9 -5
- package/peer/BalancePull.sol +9 -5
- package/peer/Base.sol +7 -6
- package/peer/Credit.sol +39 -0
- package/peer/Debit.sol +39 -0
- package/peer/DenyAssets.sol +9 -5
- package/peer/Dispatch.sol +9 -5
- package/peer/Pipe.sol +9 -5
- package/peer/Settle.sol +9 -5
- package/queries/Assets.sol +4 -4
- package/queries/Balances.sol +4 -4
- package/queries/Base.sol +9 -8
- package/queries/Positions.sol +4 -5
|
@@ -11,12 +11,11 @@ using Cursors for Cur;
|
|
|
11
11
|
/// Each NODE block in the request is deauthorized on the host.
|
|
12
12
|
/// Only callable by the admin account.
|
|
13
13
|
abstract contract Unauthorize is CommandBase, AdminEvent {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
uint internal immutable unauthorizeId = commandId(NAME);
|
|
14
|
+
uint internal immutable unauthorizeId = commandId(this.unauthorize.selector);
|
|
17
15
|
|
|
18
16
|
constructor() {
|
|
19
|
-
emit Admin(host, unauthorizeId,
|
|
17
|
+
emit Admin(host, unauthorizeId, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
|
|
18
|
+
emit Labeled(unauthorizeId, bytes32(0), "unauthorize");
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/// @notice Unauthorize each NODE block in the admin request.
|
|
@@ -25,7 +24,7 @@ abstract contract Unauthorize is CommandBase, AdminEvent {
|
|
|
25
24
|
function unauthorize(
|
|
26
25
|
CommandContext calldata c
|
|
27
26
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
28
|
-
(Cur memory request, , ) = Cursors.init(c.request,
|
|
27
|
+
(Cur memory request, , ) = Cursors.init(c.request, 1);
|
|
29
28
|
|
|
30
29
|
while (request.i < request.len) {
|
|
31
30
|
uint node = request.unpackNode();
|
package/core/Host.sol
CHANGED
|
@@ -7,6 +7,7 @@ import {Authorize} from "../commands/admin/Authorize.sol";
|
|
|
7
7
|
import {Dismiss} from "../commands/admin/Dismiss.sol";
|
|
8
8
|
import {Unauthorize} from "../commands/admin/Unauthorize.sol";
|
|
9
9
|
import {ExecutePayable} from "../commands/admin/Execute.sol";
|
|
10
|
+
import {Label} from "../commands/admin/Label.sol";
|
|
10
11
|
import {Revoke} from "../guards/Revoke.sol";
|
|
11
12
|
import {IntroductionEvent} from "../events/Introduction.sol";
|
|
12
13
|
import {Ids} from "../utils/Ids.sol";
|
|
@@ -17,9 +18,7 @@ interface IHostIntroduction {
|
|
|
17
18
|
/// @notice Record a host introduction claim.
|
|
18
19
|
/// @param peer Host node ID being introduced.
|
|
19
20
|
/// @param blocknum Block number at which the introduction was made.
|
|
20
|
-
|
|
21
|
-
/// @param namespace Human-readable namespace or label for the host.
|
|
22
|
-
function introduce(uint peer, uint blocknum, uint16 version, string calldata namespace) external;
|
|
21
|
+
function introduce(uint peer, uint blocknum) external;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/// @title Host
|
|
@@ -34,6 +33,7 @@ abstract contract Host is
|
|
|
34
33
|
Revoke,
|
|
35
34
|
Appoint,
|
|
36
35
|
Dismiss,
|
|
36
|
+
Label,
|
|
37
37
|
ExecutePayable,
|
|
38
38
|
IntroductionEvent,
|
|
39
39
|
IHostIntroduction
|
|
@@ -41,21 +41,17 @@ abstract contract Host is
|
|
|
41
41
|
/// @param cmdr Commander address; passed to `AccessControl`.
|
|
42
42
|
/// If `cmdr` is a deployed contract, the host calls `introduce`
|
|
43
43
|
/// on it during construction.
|
|
44
|
-
|
|
45
|
-
/// @param namespace Human-readable namespace string for the host.
|
|
46
|
-
constructor(address cmdr, uint16 version, string memory namespace) AccessControl(cmdr) {
|
|
44
|
+
constructor(address cmdr) AccessControl(cmdr) {
|
|
47
45
|
if (cmdr == address(0) || cmdr == address(this) || cmdr.code.length == 0) return;
|
|
48
|
-
IHostIntroduction(cmdr).introduce(host, block.number
|
|
46
|
+
IHostIntroduction(cmdr).introduce(host, block.number);
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
/// @notice Record a host introduction claim.
|
|
52
50
|
/// @dev Validates that `peer` matches `msg.sender`; it does not authorize or trust the introduced host.
|
|
53
51
|
/// @param peer Host node ID being introduced.
|
|
54
52
|
/// @param blocknum Block number at which the host was deployed.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
function introduce(uint peer, uint blocknum, uint16 version, string calldata namespace) external {
|
|
58
|
-
emit Introduction(Ids.matchHost(peer, msg.sender), blocknum, version, namespace);
|
|
53
|
+
function introduce(uint peer, uint blocknum) external {
|
|
54
|
+
emit Introduction(Ids.matchHost(peer, msg.sender), blocknum);
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
/// @notice Accept native ETH transfers (e.g. from command value flows).
|
package/core/Pipeline.sol
CHANGED
|
@@ -42,7 +42,7 @@ abstract contract Pipeline is Payable {
|
|
|
42
42
|
bytes calldata steps,
|
|
43
43
|
Budget memory budget
|
|
44
44
|
) internal {
|
|
45
|
-
(Cur memory input, , ) = Cursors.init(steps,
|
|
45
|
+
(Cur memory input, , ) = Cursors.init(steps, 1);
|
|
46
46
|
|
|
47
47
|
while (input.i < input.len) {
|
|
48
48
|
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
package/docs/Schema.md
CHANGED
|
@@ -101,6 +101,11 @@ the whole batch and are not counted as per-operation prime blocks.
|
|
|
101
101
|
The prime item cannot be optional. If a command needs a per-operation marker with
|
|
102
102
|
no payload, use a zero-payload block such as `#unit`.
|
|
103
103
|
|
|
104
|
+
Command request and state streams currently use a narrower convention than the
|
|
105
|
+
full block grammar: each is a single run of blocks, without additional global
|
|
106
|
+
items. Future protocol surfaces may use the more flexible top-level structure,
|
|
107
|
+
but command discovery metadata should describe only that one-run shape.
|
|
108
|
+
|
|
104
109
|
## Aliases
|
|
105
110
|
|
|
106
111
|
Aliases are presentation metadata for tooling. They do not change payload layout
|
|
@@ -113,6 +118,53 @@ maybe #account { bytes32 account } as recipient
|
|
|
113
118
|
|
|
114
119
|
Aliases may be used on any block item, including child blocks and prime items.
|
|
115
120
|
|
|
121
|
+
## Field Paths
|
|
122
|
+
|
|
123
|
+
Field names and aliases may use dotted paths for offchain projection. A dotted
|
|
124
|
+
path does not change the block key, payload bytes, payload length, cursor
|
|
125
|
+
behavior, or any onchain validation. It is metadata only.
|
|
126
|
+
|
|
127
|
+
```txt
|
|
128
|
+
#dispatch { uint dst.chain, uint dst.resources, #bytes as dst.payload }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This has the same runtime layout as:
|
|
132
|
+
|
|
133
|
+
```txt
|
|
134
|
+
#dispatch { uint chain, uint resources, #bytes as payload }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Offchain tooling may decode the dotted form into a nested object:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
{
|
|
141
|
+
dst: {
|
|
142
|
+
chain,
|
|
143
|
+
resources,
|
|
144
|
+
payload
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Encoding and decoding must still follow schema declaration order, not object
|
|
150
|
+
property order. Fields with the same path prefix do not need to be contiguous,
|
|
151
|
+
although contiguous fields are easier to read when they represent one logical
|
|
152
|
+
object.
|
|
153
|
+
|
|
154
|
+
Tooling should reject duplicate full paths and prefix/value collisions:
|
|
155
|
+
|
|
156
|
+
```txt
|
|
157
|
+
uint dst.chain, uint dst.chain // duplicate path
|
|
158
|
+
uint dst, uint dst.chain // prefix/value collision
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The same rule applies to block aliases:
|
|
162
|
+
|
|
163
|
+
```txt
|
|
164
|
+
#call { uint target, uint resources, #bytes as calldata.payload }
|
|
165
|
+
maybe #account { bytes32 account } as recipient.account
|
|
166
|
+
```
|
|
167
|
+
|
|
116
168
|
## Field Types
|
|
117
169
|
|
|
118
170
|
Supported field types are chain-neutral:
|
|
@@ -140,10 +192,12 @@ endowment in wei; higher bits are reserved for execution resources such as gas.
|
|
|
140
192
|
|
|
141
193
|
## Identifiers
|
|
142
194
|
|
|
143
|
-
Block names
|
|
195
|
+
Block names use lower camelCase ASCII identifiers. Field names and aliases use
|
|
196
|
+
one or more lower camelCase path segments separated by dots:
|
|
144
197
|
|
|
145
198
|
```txt
|
|
146
199
|
[a-z][a-zA-Z0-9]*
|
|
200
|
+
[a-z][a-zA-Z0-9]*(\.[a-z][a-zA-Z0-9]*)*
|
|
147
201
|
```
|
|
148
202
|
|
|
149
203
|
Invalid examples:
|
|
@@ -153,10 +207,13 @@ Amount
|
|
|
153
207
|
asset_meta
|
|
154
208
|
asset-meta
|
|
155
209
|
0account
|
|
210
|
+
asset.
|
|
211
|
+
.asset
|
|
156
212
|
```
|
|
157
213
|
|
|
158
214
|
Reserved words include `maybe`, `many`, `as`, all field type names, and the
|
|
159
|
-
reserved block names `bytes`, `data`, and `list`.
|
|
215
|
+
reserved block names `bytes`, `data`, and `list`. For dotted paths, reserved
|
|
216
|
+
words are invalid in any path segment.
|
|
160
217
|
|
|
161
218
|
## Reserved Blocks
|
|
162
219
|
|
package/events/Admin.sol
CHANGED
|
@@ -1,34 +1,28 @@
|
|
|
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 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
|
-
///
|
|
16
|
-
/// a constraint run follows the input run, or starts the request when `request`
|
|
17
|
-
/// is empty. State globals may follow the state run and are excluded.
|
|
14
|
+
/// Request and state are each a single run of blocks under the current command convention.
|
|
18
15
|
/// @param request Schema DSL string describing the input request run, or empty if none.
|
|
19
16
|
/// @param state Block key expected for input state, `Keys.Empty`, or `Keys.Any`.
|
|
20
17
|
/// @param output Block key produced for output state, or `Keys.Empty`.
|
|
21
|
-
/// @param postcheck Whether command output is validated after execution.
|
|
22
18
|
/// @param funded Whether the command entrypoint accepts nonzero `msg.value`.
|
|
23
19
|
event Admin(
|
|
24
20
|
uint indexed host,
|
|
25
21
|
uint id,
|
|
26
|
-
string name,
|
|
27
22
|
bytes32 shape,
|
|
28
23
|
string request,
|
|
29
24
|
bytes4 state,
|
|
30
25
|
bytes4 output,
|
|
31
|
-
bool postcheck,
|
|
32
26
|
bool funded
|
|
33
27
|
);
|
|
34
28
|
|
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,34 +1,28 @@
|
|
|
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 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
|
-
///
|
|
16
|
-
/// a constraint run follows the input run, or starts the request when `request`
|
|
17
|
-
/// is empty. State globals may follow the state run and are excluded.
|
|
14
|
+
/// Request and state are each a single run of blocks under the current command convention.
|
|
18
15
|
/// @param request Schema string describing the input request run, or empty if none.
|
|
19
16
|
/// @param state Block key expected for input state, `Keys.Empty`, or `Keys.Any`.
|
|
20
17
|
/// @param output Block key produced for output state, or `Keys.Empty`.
|
|
21
|
-
/// @param postcheck Whether command output is validated after execution.
|
|
22
18
|
/// @param funded Whether the command entrypoint accepts nonzero `msg.value`.
|
|
23
19
|
event Command(
|
|
24
20
|
uint indexed host,
|
|
25
21
|
uint id,
|
|
26
|
-
string name,
|
|
27
22
|
bytes32 shape,
|
|
28
23
|
string request,
|
|
29
24
|
bytes4 state,
|
|
30
25
|
bytes4 output,
|
|
31
|
-
bool postcheck,
|
|
32
26
|
bool funded
|
|
33
27
|
);
|
|
34
28
|
|
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,21 @@
|
|
|
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
|
+
/// @dev Labels are claims by the emitting contract. Any contract may emit a
|
|
8
|
+
/// label for any ID, so off-chain indexers must decide which emitters are
|
|
9
|
+
/// trusted for each labeled ID or namespace.
|
|
10
|
+
abstract contract LabeledEvent is EventEmitter {
|
|
11
|
+
string private constant ABI = "event Labeled(uint indexed id, bytes32 namespace, string name)";
|
|
12
|
+
|
|
13
|
+
/// @param id Node or capability ID being labeled.
|
|
14
|
+
/// @param namespace Label namespace.
|
|
15
|
+
/// @param name Human-readable name within the namespace.
|
|
16
|
+
event Labeled(uint indexed id, bytes32 namespace, string name);
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
emit EventAbi(ABI);
|
|
20
|
+
}
|
|
21
|
+
}
|
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/events/Transfer.sol
CHANGED
|
@@ -5,7 +5,7 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an asset moves from one account to another.
|
|
7
7
|
abstract contract TransferEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Transfer(bytes32 indexed
|
|
8
|
+
string private constant ABI = "event Transfer(bytes32 indexed account, bytes32 to, bytes32 asset, bytes32 meta, uint amount, uint32 action, uint context)";
|
|
9
9
|
|
|
10
10
|
/// @param account Source account identifier.
|
|
11
11
|
/// @param to Destination account identifier.
|
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,16 +10,15 @@ 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 {
|
|
22
|
-
(Cur memory input, , ) = Cursors.init(request,
|
|
21
|
+
(Cur memory input, , ) = Cursors.init(request, 1);
|
|
23
22
|
|
|
24
23
|
while (input.i < input.len) {
|
|
25
24
|
uint node = input.unpackNode();
|
package/package.json
CHANGED
package/peer/AllowAssets.sol
CHANGED
|
@@ -7,22 +7,26 @@ import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
10
|
+
interface IPeerAllowAssets {
|
|
11
|
+
function peerAllowAssets(bytes calldata request) external returns (bytes memory);
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
/// @title PeerAllowAssets
|
|
11
15
|
/// @notice Peer that permits a list of (asset, meta) pairs on behalf of a peer host.
|
|
12
16
|
/// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
|
|
13
|
-
abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
|
|
14
|
-
|
|
15
|
-
uint internal immutable peerAllowAssetsId = peerId(NAME);
|
|
17
|
+
abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook, IPeerAllowAssets {
|
|
18
|
+
uint internal immutable peerAllowAssetsId = peerId(this.peerAllowAssets.selector);
|
|
16
19
|
|
|
17
20
|
constructor() {
|
|
18
|
-
emit Peer(host, peerAllowAssetsId,
|
|
21
|
+
emit Peer(host, peerAllowAssetsId, "1:0", Schemas.Asset, "", false);
|
|
22
|
+
emit Labeled(peerAllowAssetsId, bytes32(0), "peerAllowAssets");
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
/// @notice Execute the allow-assets peer call.
|
|
22
26
|
/// @param request ASSET block stream supplied by the trusted peer.
|
|
23
27
|
/// @return Empty response bytes.
|
|
24
28
|
function peerAllowAssets(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory assets, , ) = Cursors.init(request,
|
|
29
|
+
(Cur memory assets, , ) = Cursors.init(request, 1);
|
|
26
30
|
|
|
27
31
|
while (assets.i < assets.len) {
|
|
28
32
|
(bytes32 asset, bytes32 meta) = assets.unpackAsset();
|
package/peer/Allowance.sol
CHANGED
|
@@ -7,23 +7,27 @@ import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
10
|
+
interface IPeerAllowance {
|
|
11
|
+
function peerAllowance(bytes calldata request) external returns (bytes memory);
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
/// @title PeerAllowance
|
|
11
15
|
/// @notice Peer that lets a trusted peer host request or refresh its own allowance.
|
|
12
16
|
/// Each AMOUNT block in the request is scoped to the peer host and passed to the
|
|
13
17
|
/// shared allowance hook as a host-scoped allowance. Restricted to trusted peers.
|
|
14
|
-
abstract contract PeerAllowance is PeerBase, AllowanceHook {
|
|
15
|
-
|
|
16
|
-
uint internal immutable peerAllowanceId = peerId(NAME);
|
|
18
|
+
abstract contract PeerAllowance is PeerBase, AllowanceHook, IPeerAllowance {
|
|
19
|
+
uint internal immutable peerAllowanceId = peerId(this.peerAllowance.selector);
|
|
17
20
|
|
|
18
21
|
constructor() {
|
|
19
|
-
emit Peer(host, peerAllowanceId,
|
|
22
|
+
emit Peer(host, peerAllowanceId, "1:0", Schemas.Amount, "", false);
|
|
23
|
+
emit Labeled(peerAllowanceId, bytes32(0), "peerAllowance");
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
/// @notice Execute the allowance peer call.
|
|
23
27
|
/// @param request AMOUNT block stream requested by the trusted peer.
|
|
24
28
|
/// @return Empty response bytes.
|
|
25
29
|
function peerAllowance(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
26
|
-
(Cur memory amounts, , ) = Cursors.init(request,
|
|
30
|
+
(Cur memory amounts, , ) = Cursors.init(request, 1);
|
|
27
31
|
uint peer = caller();
|
|
28
32
|
|
|
29
33
|
while (amounts.i < amounts.len) {
|
package/peer/BalancePull.sol
CHANGED
|
@@ -6,6 +6,10 @@ import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
|
6
6
|
|
|
7
7
|
using Cursors for Cur;
|
|
8
8
|
|
|
9
|
+
interface IPeerBalancePull {
|
|
10
|
+
function peerBalancePull(bytes calldata request) external returns (bytes memory);
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
abstract contract BalancePullHook {
|
|
10
14
|
/// @notice Override to process one incoming balance-based pull request from a peer host.
|
|
11
15
|
/// @param peer Peer host node ID for this request.
|
|
@@ -19,19 +23,19 @@ abstract contract BalancePullHook {
|
|
|
19
23
|
/// @notice Peer that pulls requested balances from a peer host into this one.
|
|
20
24
|
/// Each BALANCE block in the request calls `balancePull(peer, asset, meta, amount)`.
|
|
21
25
|
/// Restricted to trusted peers.
|
|
22
|
-
abstract contract PeerBalancePull is PeerBase, BalancePullHook {
|
|
23
|
-
|
|
24
|
-
uint internal immutable peerBalancePullId = peerId(NAME);
|
|
26
|
+
abstract contract PeerBalancePull is PeerBase, BalancePullHook, IPeerBalancePull {
|
|
27
|
+
uint internal immutable peerBalancePullId = peerId(this.peerBalancePull.selector);
|
|
25
28
|
|
|
26
29
|
constructor() {
|
|
27
|
-
emit Peer(host, peerBalancePullId,
|
|
30
|
+
emit Peer(host, peerBalancePullId, "1:0", Schemas.Balance, "", false);
|
|
31
|
+
emit Labeled(peerBalancePullId, bytes32(0), "peerBalancePull");
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/// @notice Execute the balance-pull peer call.
|
|
31
35
|
/// @param request BALANCE block stream requested by the trusted peer.
|
|
32
36
|
/// @return Empty response bytes.
|
|
33
37
|
function peerBalancePull(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
34
|
-
(Cur memory input, , ) = Cursors.init(request,
|
|
38
|
+
(Cur memory input, , ) = Cursors.init(request, 1);
|
|
35
39
|
uint peer = caller();
|
|
36
40
|
|
|
37
41
|
while (input.i < input.len) {
|
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/Credit.sol
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import { PeerBase } from "./Base.sol";
|
|
5
|
+
import { CreditAccountHook } from "../commands/Credit.sol";
|
|
6
|
+
import { Cursors, Cur, Forms } from "../Cursors.sol";
|
|
7
|
+
|
|
8
|
+
using Cursors for Cur;
|
|
9
|
+
|
|
10
|
+
interface IPeerCreditTo {
|
|
11
|
+
function peerCreditTo(bytes calldata request) external returns (bytes memory);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// @title PeerCreditTo
|
|
15
|
+
/// @notice Peer that lets a trusted peer credit supplied accounts directly.
|
|
16
|
+
/// Each ACCOUNT_AMOUNT block calls `creditAccount` for its account.
|
|
17
|
+
abstract contract PeerCreditTo is PeerBase, CreditAccountHook, IPeerCreditTo {
|
|
18
|
+
uint internal immutable peerCreditToId = peerId(this.peerCreditTo.selector);
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
emit Peer(host, peerCreditToId, "1:0", Forms.AccountAmount, "", false);
|
|
22
|
+
emit Labeled(peerCreditToId, bytes32(0), "peerCreditTo");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// @notice Execute the peer-credit call.
|
|
26
|
+
/// @param request ACCOUNT_AMOUNT block stream supplied by the trusted peer.
|
|
27
|
+
/// @return Empty response bytes.
|
|
28
|
+
function peerCreditTo(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
29
|
+
(Cur memory amounts, , ) = Cursors.init(request, 1);
|
|
30
|
+
|
|
31
|
+
while (amounts.i < amounts.len) {
|
|
32
|
+
(bytes32 account, bytes32 asset, bytes32 meta, uint amount) = amounts.unpackAccountAmount();
|
|
33
|
+
creditAccount(account, asset, meta, amount);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
amounts.complete();
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
}
|