@rootzero/contracts 1.9.0 → 1.11.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 +40 -2
- package/Core.sol +3 -1
- package/Endpoints.sol +3 -6
- package/Events.sol +3 -6
- package/README.md +35 -33
- package/Utils.sol +2 -1
- package/blocks/Cursors.sol +39 -88
- package/blocks/Keys.sol +14 -4
- package/blocks/Schema.sol +49 -44
- package/commands/Base.sol +43 -13
- package/commands/Burn.sol +3 -6
- package/commands/Credit.sol +9 -6
- package/commands/Debit.sol +15 -11
- package/commands/Deposit.sol +19 -23
- package/commands/Payout.sol +6 -10
- package/commands/Provision.sol +19 -23
- package/commands/Recover.sol +16 -10
- package/commands/Relay.sol +21 -12
- package/commands/Withdraw.sol +3 -6
- package/commands/admin/AllowAssets.sol +7 -10
- package/commands/admin/Allowance.sol +7 -10
- package/commands/admin/Appoint.sol +7 -10
- package/commands/admin/Authorize.sol +7 -10
- package/commands/admin/Base.sol +1 -2
- package/commands/admin/DenyAssets.sol +7 -10
- package/commands/admin/Dismiss.sol +7 -10
- package/commands/admin/Execute.sol +7 -10
- package/commands/admin/Label.sol +8 -11
- package/commands/admin/Unauthorize.sol +7 -10
- package/core/Endpoint.sol +153 -0
- package/core/Payable.sol +7 -0
- package/core/Pipeline.sol +1 -1
- package/core/Portal.sol +19 -48
- package/docs/Schema.md +114 -82
- package/events/Endpoint.sol +19 -0
- package/events/Recovered.sol +17 -0
- package/events/Schema.sol +23 -0
- package/guards/Base.sol +17 -8
- package/guards/Revoke.sol +4 -6
- package/package.json +1 -1
- package/ports/AllowAssets.sol +6 -9
- package/ports/Allowance.sol +6 -9
- package/ports/Base.sol +21 -8
- package/ports/Credit.sol +6 -9
- package/ports/Debit.sol +6 -9
- package/ports/DenyAssets.sol +6 -9
- package/ports/Dispatch.sol +7 -10
- package/ports/Pipe.sol +4 -7
- package/ports/Redeem.sol +4 -7
- package/ports/Settle.sol +6 -9
- package/queries/Assets.sol +9 -12
- package/queries/Balances.sol +7 -9
- package/queries/Base.sol +19 -11
- package/utils/Selectors.sol +49 -0
- package/commands/admin/Destroy.sol +0 -43
- package/commands/admin/Init.sol +0 -43
- package/events/Admin.sol +0 -32
- package/events/Command.sol +0 -32
- package/events/Guard.sol +0 -18
- package/events/Port.sol +0 -22
- package/events/Query.sol +0 -20
- package/events/Resolved.sol +0 -17
- package/queries/Positions.sol +0 -54
package/core/Payable.sol
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {Budget, Values} from "../utils/Value.sol";
|
|
5
|
+
import {max128} from "../utils/Utils.sol";
|
|
5
6
|
|
|
6
7
|
/// @title Payable
|
|
7
8
|
/// @notice Abstract mixin for entrypoints that accept native value (`msg.value`).
|
|
@@ -16,6 +17,12 @@ abstract contract Payable {
|
|
|
16
17
|
return Budget({remaining: msg.value});
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/// @notice Return the current call's native value as a checked uint128.
|
|
21
|
+
/// @return value Current `msg.value` in wei.
|
|
22
|
+
function msgValue() internal view returns (uint128 value) {
|
|
23
|
+
return uint128(max128(msg.value));
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
/// @notice Deduct the EVM value lane from a packed resource word and return it.
|
|
20
27
|
/// @dev EVM resources use the low 128 bits as native value/endowment.
|
|
21
28
|
/// @param budget Mutable budget to deduct from.
|
package/core/Pipeline.sol
CHANGED
|
@@ -43,7 +43,7 @@ abstract contract Pipeline is Payable {
|
|
|
43
43
|
bytes calldata steps,
|
|
44
44
|
Budget memory budget
|
|
45
45
|
) internal {
|
|
46
|
-
(Cur memory input,
|
|
46
|
+
(Cur memory input, ) = Cursors.init(steps, 1);
|
|
47
47
|
|
|
48
48
|
while (input.i < input.len) {
|
|
49
49
|
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
package/core/Portal.sol
CHANGED
|
@@ -2,67 +2,38 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortCalls} from "./Calls.sol";
|
|
5
|
-
import {ResolvedEvent} from "../events/Resolved.sol";
|
|
6
|
-
import {UndeliveredEvent} from "../events/Undelivered.sol";
|
|
7
|
-
import {Budget} from "../utils/Value.sol";
|
|
8
|
-
|
|
9
|
-
abstract contract RoutePayableHook {
|
|
10
|
-
/// @notice Override to route an encoded payload through `portal`.
|
|
11
|
-
/// @param portal Destination portal identifier, often the destination host ID.
|
|
12
|
-
/// @param resources Chain-specific destination resources. EVM adapters
|
|
13
|
-
/// may interpret this as packed execution gas and destination value.
|
|
14
|
-
/// @param payload Encoded payload ready for the transport layer.
|
|
15
|
-
/// @param budget Source native-value budget available for transport
|
|
16
|
-
/// fees and destination resource funding.
|
|
17
|
-
function route(uint portal, uint resources, bytes memory payload, Budget memory budget) internal virtual;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
abstract contract RecoverHook {
|
|
21
|
-
/// @notice Override to recover a previously undelivered witness through `handler`.
|
|
22
|
-
/// @param handler Port that should attempt recovery.
|
|
23
|
-
/// @param key Recovery lookup key.
|
|
24
|
-
/// @param witness Witness payload used to prove and replay recovery.
|
|
25
|
-
/// @param value Native EVM value assigned to the recovery attempt.
|
|
26
|
-
function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual;
|
|
27
|
-
}
|
|
28
5
|
|
|
29
6
|
/// @title Portal
|
|
30
7
|
/// @notice Base contract for hosts that route payloads through portal adapters.
|
|
31
|
-
abstract contract Portal is PortCalls
|
|
8
|
+
abstract contract Portal is PortCalls {
|
|
32
9
|
error BadWitness();
|
|
33
10
|
|
|
34
|
-
/// @dev Remote port used to handle messages delivered through this portal.
|
|
35
|
-
uint private immutable port;
|
|
36
|
-
|
|
37
11
|
mapping(bytes32 key => bytes32 digest) internal undelivered;
|
|
38
12
|
|
|
39
|
-
/// @
|
|
40
|
-
|
|
41
|
-
|
|
13
|
+
/// @notice Try to forward `message` to `port`.
|
|
14
|
+
/// @dev Records and returns `keccak256(message)` under `key` only when forwarding fails.
|
|
15
|
+
/// @param port Port that should handle the forwarded message.
|
|
16
|
+
/// @param key Forwarding/recovery lookup key.
|
|
17
|
+
/// @param message Encoded port input to forward.
|
|
18
|
+
/// @param value Native EVM value assigned to the forwarding attempt.
|
|
19
|
+
/// @return miss Message digest recorded for recovery when forwarding fails; zero on success.
|
|
20
|
+
function forward(uint port, bytes32 key, bytes calldata message, uint128 value) internal returns (bytes32 miss) {
|
|
21
|
+
if (tryCallPort(port, value, message)) return bytes32(0);
|
|
22
|
+
|
|
23
|
+
miss = keccak256(message);
|
|
24
|
+
undelivered[key] = miss;
|
|
42
25
|
}
|
|
43
26
|
|
|
44
|
-
/// @notice
|
|
45
|
-
/// @dev Records `keccak256(message)` under `key` only when delivery fails.
|
|
46
|
-
/// @param key Delivery/recovery lookup key.
|
|
47
|
-
/// @param message Encoded port input to deliver.
|
|
48
|
-
/// @param value Native EVM value assigned to the delivery attempt.
|
|
49
|
-
function deliver(bytes32 key, bytes calldata message, uint128 value) internal {
|
|
50
|
-
if (tryCallPort(port, value, message)) return;
|
|
51
|
-
bytes32 digest = keccak256(message);
|
|
52
|
-
undelivered[key] = digest;
|
|
53
|
-
emit Undelivered(host, key, digest);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/// @notice Recover a previously undelivered witness through `handler`.
|
|
27
|
+
/// @notice Retry a previously undelivered witness through `port`.
|
|
57
28
|
/// @dev The witness must hash to the digest stored under `key`.
|
|
58
|
-
/// @param
|
|
29
|
+
/// @param port Port that should attempt recovery.
|
|
59
30
|
/// @param key Recovery lookup key.
|
|
60
31
|
/// @param witness Witness payload used to prove and replay recovery.
|
|
61
32
|
/// @param value Native EVM value assigned to the recovery attempt.
|
|
62
|
-
function
|
|
63
|
-
if (
|
|
33
|
+
function retry(uint port, bytes32 key, bytes calldata witness, uint128 value) internal virtual {
|
|
34
|
+
if (undelivered[key] != keccak256(witness)) revert BadWitness();
|
|
35
|
+
|
|
64
36
|
delete undelivered[key];
|
|
65
|
-
callPort(
|
|
66
|
-
emit Resolved(host, key);
|
|
37
|
+
callPort(port, value, witness);
|
|
67
38
|
}
|
|
68
39
|
}
|
package/docs/Schema.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Schema
|
|
2
2
|
|
|
3
3
|
Rootzero request and response data is encoded as a stream of typed blocks. A
|
|
4
|
-
schema string describes payload
|
|
5
|
-
runtime block key is
|
|
4
|
+
schema string describes the payload body for discovery events and tooling; the
|
|
5
|
+
runtime block key is the compact type tag that identifies that payload layout in
|
|
6
|
+
the active schema context. The block alias is published separately from the
|
|
7
|
+
payload schema.
|
|
6
8
|
|
|
7
9
|
## Wire Format
|
|
8
10
|
|
|
@@ -15,39 +17,50 @@ Every block uses the same header:
|
|
|
15
17
|
`payloadLen` is big-endian and counts only payload bytes. Child blocks and list
|
|
16
18
|
items use the same header format.
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
Standard built-in block keys use:
|
|
19
21
|
|
|
20
22
|
```txt
|
|
21
23
|
bytes4(keccak256("#name"))
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
For example,
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
For example, the standard `amount` alias uses the key derived from `#amount`
|
|
27
|
+
and the schema body `{ bytes32 asset, uint amount }`. Custom block keys do not
|
|
28
|
+
have to be keccak-derived. They
|
|
29
|
+
are opaque `bytes4` tags and only need to be unique in the context where they are
|
|
30
|
+
used. A host can publish the meaning of a custom key with:
|
|
31
|
+
|
|
32
|
+
```solidity
|
|
33
|
+
event Schema(uint indexed host, bytes4 key, string schema, bytes32 name);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For example, a host-specific payment block can use `Keys.local(1)`, the command
|
|
37
|
+
selector, or any other chosen `bytes4` value as long as that key is not
|
|
38
|
+
overloaded in the relevant host/schema context.
|
|
27
39
|
|
|
28
40
|
## Block Syntax
|
|
29
41
|
|
|
30
|
-
A block
|
|
42
|
+
A block definition has an event alias and a schema body. Fixed fields are
|
|
43
|
+
written in braces:
|
|
31
44
|
|
|
32
45
|
```txt
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
alias: amount
|
|
47
|
+
schema: { bytes32 asset, uint amount }
|
|
35
48
|
```
|
|
36
49
|
|
|
37
|
-
A block
|
|
50
|
+
A block body can reference another block alias as a child item with `#`:
|
|
38
51
|
|
|
39
52
|
```txt
|
|
40
|
-
#
|
|
41
|
-
#bytes
|
|
53
|
+
{ bytes32 account, #bytes as state, #bytes as request }
|
|
42
54
|
```
|
|
43
55
|
|
|
44
|
-
|
|
56
|
+
The empty schema string `""` means the block has no structured payload. This is
|
|
57
|
+
used for zero-payload blocks such as `#unit` and raw dynamic blocks such as
|
|
58
|
+
`#bytes`.
|
|
45
59
|
|
|
46
|
-
A schema is a comma-separated list of items. Order is significant.
|
|
60
|
+
A schema body is a comma-separated list of items. Order is significant.
|
|
47
61
|
|
|
48
62
|
```txt
|
|
49
|
-
#amount
|
|
50
|
-
maybe #account { bytes32 account }
|
|
63
|
+
{ #amount, maybe #account as recipient }
|
|
51
64
|
```
|
|
52
65
|
|
|
53
66
|
## Payload Layout
|
|
@@ -56,8 +69,8 @@ A block payload has fixed fields first, followed by an optional child-block tail
|
|
|
56
69
|
Once a child block appears, no more fixed fields may follow.
|
|
57
70
|
|
|
58
71
|
```txt
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
{ uint target, uint resources, #bytes as payload }
|
|
73
|
+
{ bytes32 account, #bytes as state, #bytes as request }
|
|
61
74
|
```
|
|
62
75
|
|
|
63
76
|
The tail is embedded directly as child block bytes. There is no wrapper around a
|
|
@@ -75,59 +88,87 @@ alias to give those bytes a presentation name:
|
|
|
75
88
|
Cardinality is expressed with prefix keywords:
|
|
76
89
|
|
|
77
90
|
```txt
|
|
78
|
-
#balance
|
|
79
|
-
maybe #balance
|
|
80
|
-
many #balance
|
|
81
|
-
maybe many #balance
|
|
91
|
+
#balance
|
|
92
|
+
maybe #balance
|
|
93
|
+
many #balance
|
|
94
|
+
maybe many #balance
|
|
82
95
|
```
|
|
83
96
|
|
|
84
97
|
- no prefix: one required item
|
|
85
98
|
- `maybe`: optional item
|
|
86
|
-
- `many`: one `#list` block whose payload contains repeated items
|
|
99
|
+
- `many`: one generic `#list` block whose payload contains repeated items
|
|
87
100
|
- `maybe many`: optional `#list` block
|
|
88
101
|
|
|
89
102
|
`maybe` emits no placeholder when absent. `many` wraps repeated items in one
|
|
90
103
|
generic list block; it does not repeat the item in place.
|
|
91
104
|
|
|
92
|
-
##
|
|
105
|
+
## Endpoint Lanes
|
|
106
|
+
|
|
107
|
+
Endpoint descriptors identify each lane with a block key and group size. In
|
|
108
|
+
Solidity, endpoint definition helpers accept `bytes9` lane values, with plain
|
|
109
|
+
`bytes4` keys and the `bytes8` values returned by `many(item)` widening
|
|
110
|
+
implicitly. A plain key or `many(item)` stores a zero group byte that readers
|
|
111
|
+
interpret as group size 1, while `bytes9(0)` or `Keys.Empty` means the endpoint
|
|
112
|
+
has no blocks in that lane. Use
|
|
113
|
+
`group(lane, size)` when a lane needs an explicit group size other than 1.
|
|
114
|
+
|
|
115
|
+
The packed descriptor stores each lane key as an 8-byte value:
|
|
116
|
+
|
|
117
|
+
```txt
|
|
118
|
+
[key bytes4][item bytes4]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
A plain block key is widened into `[key][0]`, so normal endpoint declarations can
|
|
122
|
+
pass standard `bytes4` keys directly. A lane with a nonzero `item` describes a
|
|
123
|
+
generic container block: `key` is the top-level wire key and `item` is the
|
|
124
|
+
contained item key. The built-in `many(item)` helper creates `[Keys.List][item]`
|
|
125
|
+
with the default group size 1, matching the DSL form `many #item`.
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
Any non-empty lane resolves its key to a block alias and schema body through the
|
|
128
|
+
active schema context. If the item slot is nonzero, tooling also resolves that
|
|
129
|
+
item key in the same context. A bare list lane, `[Keys.List][0]`, is incomplete
|
|
130
|
+
discovery metadata because it does not say what the list contains; indexers
|
|
131
|
+
should reject it for self-describing endpoints.
|
|
95
132
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
133
|
+
The lane key is the prime item. Prime items may repeat at the top level for
|
|
134
|
+
batching. When the lane is `many #item`, the repeated prime item is the generic
|
|
135
|
+
LIST block and each LIST payload contains repeated `item` blocks. Later
|
|
136
|
+
top-level items are globals for the whole batch and are not counted as
|
|
137
|
+
per-operation prime blocks.
|
|
99
138
|
|
|
100
|
-
The prime item cannot be optional. If
|
|
101
|
-
no payload, use a zero-payload block such as `#unit`.
|
|
139
|
+
The prime item cannot be optional. If an endpoint needs a per-operation marker
|
|
140
|
+
with no payload, use a zero-payload block such as `#unit`.
|
|
102
141
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
items. Future protocol surfaces may use the more flexible
|
|
106
|
-
|
|
142
|
+
Endpoint descriptors currently use a narrower convention than the full block
|
|
143
|
+
grammar: each state, input, or output lane is a single run of blocks, without
|
|
144
|
+
additional global items. Future protocol surfaces may use the more flexible
|
|
145
|
+
top-level structure.
|
|
107
146
|
|
|
108
|
-
## Aliases
|
|
147
|
+
## Field Aliases
|
|
109
148
|
|
|
110
|
-
|
|
111
|
-
or runtime keys.
|
|
149
|
+
Block aliases are published in `Schema` events. Field aliases are presentation
|
|
150
|
+
metadata for tooling. They do not change payload layout or runtime keys.
|
|
112
151
|
|
|
113
152
|
```txt
|
|
114
|
-
maybe #account
|
|
115
|
-
|
|
153
|
+
maybe #account as recipient
|
|
154
|
+
{ uint target, uint resources, #bytes as payload }
|
|
116
155
|
```
|
|
117
156
|
|
|
118
|
-
|
|
157
|
+
Field aliases may be used on any block item, including child blocks and prime
|
|
158
|
+
items.
|
|
119
159
|
|
|
120
|
-
|
|
160
|
+
Child blocks are schema references:
|
|
121
161
|
|
|
122
162
|
```txt
|
|
123
|
-
|
|
163
|
+
{ uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
124
164
|
```
|
|
125
165
|
|
|
126
|
-
Alias resolution is context-dependent. A consumer may resolve `#context` from
|
|
127
|
-
standard
|
|
128
|
-
schema context.
|
|
129
|
-
|
|
130
|
-
|
|
166
|
+
Alias resolution is context-dependent. A consumer may resolve `#context` from
|
|
167
|
+
standard block events, from app-specific block events, or from another active
|
|
168
|
+
schema context. Custom parents should define nested custom blocks from the
|
|
169
|
+
bottom up and reference them by alias. Consumers should reject schemas with
|
|
170
|
+
unresolved aliases. The runtime encoding is still an embedded child block with
|
|
171
|
+
the referenced key and layout.
|
|
131
172
|
|
|
132
173
|
## Field Paths
|
|
133
174
|
|
|
@@ -136,13 +177,13 @@ path does not change the block key, payload bytes, payload length, cursor
|
|
|
136
177
|
behavior, or any onchain validation. It is metadata only.
|
|
137
178
|
|
|
138
179
|
```txt
|
|
139
|
-
|
|
180
|
+
{ uint dst.portal, uint dst.resources, #bytes as dst.payload }
|
|
140
181
|
```
|
|
141
182
|
|
|
142
183
|
This has the same runtime layout as:
|
|
143
184
|
|
|
144
185
|
```txt
|
|
145
|
-
|
|
186
|
+
{ uint portal, uint resources, #bytes as payload }
|
|
146
187
|
```
|
|
147
188
|
|
|
148
189
|
Offchain tooling may decode the dotted form into a nested object:
|
|
@@ -169,11 +210,11 @@ uint dst.portal, uint dst.portal // duplicate path
|
|
|
169
210
|
uint dst, uint dst.portal // prefix/value collision
|
|
170
211
|
```
|
|
171
212
|
|
|
172
|
-
The same rule applies to
|
|
213
|
+
The same rule applies to field aliases:
|
|
173
214
|
|
|
174
215
|
```txt
|
|
175
|
-
|
|
176
|
-
maybe #account
|
|
216
|
+
{ uint target, uint resources, #bytes as calldata.payload }
|
|
217
|
+
maybe #account as recipient.account
|
|
177
218
|
```
|
|
178
219
|
|
|
179
220
|
## Field Types
|
|
@@ -226,8 +267,8 @@ the layout of an ID only apply to structured IDs.
|
|
|
226
267
|
|
|
227
268
|
## Identifiers
|
|
228
269
|
|
|
229
|
-
Block
|
|
230
|
-
one or more lower camelCase path segments separated by dots:
|
|
270
|
+
Block aliases use lower camelCase ASCII identifiers. Field names and aliases
|
|
271
|
+
use one or more lower camelCase path segments separated by dots:
|
|
231
272
|
|
|
232
273
|
```txt
|
|
233
274
|
[a-z][a-zA-Z0-9]*
|
|
@@ -246,48 +287,39 @@ asset.
|
|
|
246
287
|
```
|
|
247
288
|
|
|
248
289
|
Reserved words include `maybe`, `many`, `as`, all field type names, and the
|
|
249
|
-
reserved block
|
|
250
|
-
|
|
290
|
+
reserved block aliases `bytes` and `list`. For dotted paths, reserved words are
|
|
291
|
+
invalid in any path segment.
|
|
251
292
|
|
|
252
293
|
## Reserved Blocks
|
|
253
294
|
|
|
254
295
|
- `#bytes`: raw dynamic bytes, written without a body
|
|
255
|
-
- `#data`: generic/custom payload block
|
|
256
296
|
- `#list`: generic list wrapper emitted by `many`
|
|
257
297
|
|
|
258
|
-
|
|
298
|
+
Custom input shapes should define their own context-local block key and publish
|
|
299
|
+
that key with a `Schema` event:
|
|
259
300
|
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
If a schema string starts with a fixed field type, it is shorthand for one
|
|
266
|
-
top-level `#data` block:
|
|
267
|
-
|
|
268
|
-
```txt
|
|
269
|
-
uint foo, bytes32 tag
|
|
301
|
+
```solidity
|
|
302
|
+
bytes4 constant Input = Keys.local(1);
|
|
303
|
+
emit Schema(host, Input, "{ bytes32 asset, uint amount }", bytes32("payment"));
|
|
270
304
|
```
|
|
271
305
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
#data { uint foo, bytes32 tag }
|
|
276
|
-
```
|
|
306
|
+
The key can be a small literal, a selector, or any other `bytes4` value that is
|
|
307
|
+
unique in the context where it is used. The alias names the block; the schema
|
|
308
|
+
string describes only the payload body.
|
|
277
309
|
|
|
278
310
|
## Standard Blocks
|
|
279
311
|
|
|
280
312
|
Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
281
313
|
|
|
282
314
|
```txt
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
315
|
+
amount { bytes32 asset, uint amount }
|
|
316
|
+
balance { bytes32 asset, uint amount }
|
|
317
|
+
custody { uint host, bytes32 asset, uint amount }
|
|
318
|
+
call { uint target, uint resources, #bytes as payload }
|
|
319
|
+
step { uint target, uint resources, #bytes as request }
|
|
320
|
+
context { bytes32 account, #bytes as state, #bytes as request }
|
|
321
|
+
recover { uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
322
|
+
auth { uint cid, uint deadline, #bytes as proof }
|
|
291
323
|
```
|
|
292
324
|
|
|
293
|
-
`Keys.sol` contains the corresponding runtime keys.
|
|
325
|
+
`Keys.sol` contains the corresponding standard runtime keys.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
+
|
|
6
|
+
/// @title EndpointEvent
|
|
7
|
+
/// @notice Emitted during host deployment to publish a callable endpoint descriptor.
|
|
8
|
+
abstract contract EndpointEvent is EventEmitter {
|
|
9
|
+
string private constant ABI = "event Endpoint(uint indexed host, uint id, bytes32 descriptor)";
|
|
10
|
+
|
|
11
|
+
/// @param host Host node ID that exposes the endpoint.
|
|
12
|
+
/// @param id Endpoint node ID.
|
|
13
|
+
/// @param descriptor Packed endpoint lane metadata and flags.
|
|
14
|
+
event Endpoint(uint indexed host, uint id, bytes32 descriptor);
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
emit EventAbi(ABI);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 when a host recovers a previously recorded key.
|
|
7
|
+
abstract contract RecoveredEvent is EventEmitter {
|
|
8
|
+
string private constant ABI = "event Recovered(uint indexed host, bytes32 key)";
|
|
9
|
+
|
|
10
|
+
/// @param host Host node ID that owns the recovered key.
|
|
11
|
+
/// @param key Recovery lookup key.
|
|
12
|
+
event Recovered(uint indexed host, bytes32 key);
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
emit EventAbi(ABI);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
+
|
|
6
|
+
/// @title SchemaEvent
|
|
7
|
+
/// @notice Emitted during host deployment to publish a block key and payload schema.
|
|
8
|
+
/// Block keys are opaque `bytes4` tags. Standard protocol blocks use
|
|
9
|
+
/// keccak-derived keys by convention, but custom block keys only need to be
|
|
10
|
+
/// unique within the publishing host/schema context.
|
|
11
|
+
abstract contract SchemaEvent is EventEmitter {
|
|
12
|
+
string private constant ABI = "event Schema(uint indexed host, bytes4 key, string schema, bytes32 name)";
|
|
13
|
+
|
|
14
|
+
/// @param host Host node ID that publishes this block schema.
|
|
15
|
+
/// @param key Block type key being defined by `host`.
|
|
16
|
+
/// @param schema Schema DSL string describing the block payload body.
|
|
17
|
+
/// @param name Optional block alias used by endpoint descriptors and nested schemas.
|
|
18
|
+
event Schema(uint indexed host, bytes4 key, string schema, bytes32 name);
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
emit EventAbi(ABI);
|
|
22
|
+
}
|
|
23
|
+
}
|
package/guards/Base.sol
CHANGED
|
@@ -2,24 +2,33 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {AccessControl} from "../core/Access.sol";
|
|
5
|
-
import {
|
|
6
|
-
import {LabeledEvent} from "../events/Labeled.sol";
|
|
5
|
+
import {EndpointBase} from "../core/Endpoint.sol";
|
|
7
6
|
import {Nodes} from "../utils/Nodes.sol";
|
|
7
|
+
import {Selectors} from "../utils/Selectors.sol";
|
|
8
8
|
|
|
9
9
|
/// @title GuardBase
|
|
10
10
|
/// @notice Abstract base for guardian-only direct host actions.
|
|
11
11
|
/// Guard actions are non-payable direct calls with no command context, state, or response.
|
|
12
|
-
abstract contract GuardBase is AccessControl,
|
|
12
|
+
abstract contract GuardBase is AccessControl, EndpointBase {
|
|
13
13
|
/// @dev Restrict execution to active guardian addresses.
|
|
14
14
|
modifier onlyGuardian() {
|
|
15
15
|
if (!isGuardian(msg.sender)) revert AccessDenied();
|
|
16
16
|
_;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/// @notice
|
|
20
|
-
/// @param
|
|
21
|
-
/// @
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/// @notice Publish guard metadata and a default label.
|
|
20
|
+
/// @param name Default human-readable guard label and selector name.
|
|
21
|
+
/// @param input Packed input lane plus optional group byte.
|
|
22
|
+
/// @param selector Guard ABI selector, or zero to derive it from `name`.
|
|
23
|
+
/// @return id Guard action node ID.
|
|
24
|
+
/// @return descriptor Packed endpoint lane metadata and flags.
|
|
25
|
+
function guard(
|
|
26
|
+
string memory name,
|
|
27
|
+
bytes9 input,
|
|
28
|
+
bytes4 selector
|
|
29
|
+
) internal returns (uint id, bytes32 descriptor) {
|
|
30
|
+
selector = selector == bytes4(0) ? Selectors.guard(name) : selector;
|
|
31
|
+
id = Nodes.toGuard(selector, address(this));
|
|
32
|
+
descriptor = endpoint(id, name, bytes9(0), input, bytes9(0), false, false);
|
|
24
33
|
}
|
|
25
34
|
}
|
package/guards/Revoke.sol
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {GuardBase} from "./Base.sol";
|
|
5
|
-
import {Cursors, Cur,
|
|
5
|
+
import {Cursors, Cur, Keys} from "../Cursors.sol";
|
|
6
6
|
using Cursors for Cur;
|
|
7
7
|
|
|
8
8
|
/// @title Revoke
|
|
@@ -10,21 +10,19 @@ 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
|
-
|
|
13
|
+
bytes32 private immutable descriptor;
|
|
14
14
|
|
|
15
15
|
constructor() {
|
|
16
|
-
|
|
17
|
-
emit Labeled(revokeId, bytes32(0), "revoke");
|
|
16
|
+
(, descriptor) = guard("revoke", Keys.Node, 0);
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
function revoke(bytes calldata request) external onlyGuardian {
|
|
21
|
-
(Cur memory input,
|
|
20
|
+
(Cur memory input, ) = openInput(request, descriptor);
|
|
22
21
|
|
|
23
22
|
while (input.i < input.len) {
|
|
24
23
|
uint node = input.unpackNode();
|
|
25
24
|
setNode(node, false);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
input.complete();
|
|
29
27
|
}
|
|
30
28
|
}
|
package/package.json
CHANGED
package/ports/AllowAssets.sol
CHANGED
|
@@ -3,7 +3,7 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { PortBase } from "./Base.sol";
|
|
5
5
|
import { AllowAssetsHook } from "../commands/admin/AllowAssets.sol";
|
|
6
|
-
import { Cursors, Cur,
|
|
6
|
+
import { Cursors, Cur, Keys } from "../Cursors.sol";
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
@@ -11,25 +11,22 @@ using Cursors for Cur;
|
|
|
11
11
|
/// @notice Port that permits a list of assets on behalf of a peer host.
|
|
12
12
|
/// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
|
|
13
13
|
abstract contract PortAllowAssets is PortBase, AllowAssetsHook {
|
|
14
|
-
|
|
14
|
+
bytes32 private immutable descriptor;
|
|
15
15
|
|
|
16
16
|
constructor() {
|
|
17
|
-
|
|
18
|
-
emit Labeled(portAllowAssetsId, bytes32(0), "portAllowAssets");
|
|
17
|
+
(, descriptor) = port("portAllowAssets", Keys.Asset, Keys.Empty, 0, false);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
/// @notice Execute the allow-assets peer call.
|
|
22
21
|
/// @param data ASSET block stream supplied by the trusted peer.
|
|
23
22
|
/// @return Empty response bytes.
|
|
24
23
|
function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory
|
|
24
|
+
(Cur memory input, ) = openInput(data, descriptor);
|
|
26
25
|
|
|
27
|
-
while (
|
|
28
|
-
bytes32 asset =
|
|
26
|
+
while (input.i < input.len) {
|
|
27
|
+
bytes32 asset = input.unpackAsset();
|
|
29
28
|
allowAsset(asset);
|
|
30
29
|
}
|
|
31
|
-
|
|
32
|
-
assets.complete();
|
|
33
30
|
return "";
|
|
34
31
|
}
|
|
35
32
|
}
|