@rootzero/contracts 1.11.0 → 1.12.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 +26 -0
- package/Endpoints.sol +1 -0
- package/README.md +3 -3
- package/blocks/Cursors.sol +13 -0
- package/blocks/Keys.sol +4 -0
- package/blocks/Schema.sol +4 -3
- package/commands/Credit.sol +2 -7
- package/commands/Debit.sol +2 -7
- package/commands/admin/Authorize.sol +5 -11
- package/commands/admin/Schemas.sol +31 -0
- package/commands/admin/Unauthorize.sol +5 -11
- package/core/Endpoint.sol +14 -7
- package/core/Host.sol +1 -1
- package/docs/Schema.md +15 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
Until the protocol reaches integration-stable status, minor versions may include
|
|
4
4
|
breaking API changes. Breaking changes are called out explicitly.
|
|
5
5
|
|
|
6
|
+
## 1.12.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Removed the generic `EndpointBase.schema(...)` helper. Use
|
|
11
|
+
`localSchema(...)` for context-local endpoint schemas or emit `Schema`
|
|
12
|
+
directly for custom/named keys.
|
|
13
|
+
- Replaced `isDebitAccount`, `isCreditAccount`, `isAuthorize`, and
|
|
14
|
+
`isUnauthorize` helper predicates with internal command ID fields:
|
|
15
|
+
`debitAccountId`, `creditAccountId`, `authorizeId`, and `unauthorizeId`.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Added `Keys.Local` as the default context-local block key and added
|
|
20
|
+
`EndpointBase.localSchema(...)` helpers for publishing local endpoint
|
|
21
|
+
schemas.
|
|
22
|
+
- Added the standard `#schema` block, `unpackSchema`, and an opt-in
|
|
23
|
+
`publishSchema` admin command for emitting schema claims from `#schema`
|
|
24
|
+
blocks.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated the schema DSL documentation to allow any number of child blocks in
|
|
29
|
+
declaration order, including fixed fields before, after, or between child
|
|
30
|
+
blocks.
|
|
31
|
+
|
|
6
32
|
## 1.11.0
|
|
7
33
|
|
|
8
34
|
### Breaking Changes
|
package/Endpoints.sol
CHANGED
|
@@ -31,6 +31,7 @@ import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
|
|
|
31
31
|
import { Dismiss } from "./commands/admin/Dismiss.sol";
|
|
32
32
|
import { ExecutePayable } from "./commands/admin/Execute.sol";
|
|
33
33
|
import { Label } from "./commands/admin/Label.sol";
|
|
34
|
+
import { PublishSchema } from "./commands/admin/Schemas.sol";
|
|
34
35
|
import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
35
36
|
|
|
36
37
|
// Port endpoints
|
package/README.md
CHANGED
|
@@ -89,8 +89,8 @@ request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
|
|
|
89
89
|
port would parse; what differs per chain is how a host *resolves* the
|
|
90
90
|
identifiers inside, never how the bytes are laid out.
|
|
91
91
|
|
|
92
|
-
Schemas can express more than flat fields: a block may
|
|
93
|
-
blocks (`#bytes as payload` names
|
|
92
|
+
Schemas can express more than flat fields: a block may contain any number of
|
|
93
|
+
nested child blocks (`#bytes as payload` names raw dynamic bytes), items can be
|
|
94
94
|
marked `maybe` (optional) or `many` (a list), and aliases and dotted field
|
|
95
95
|
paths give off-chain tooling presentation names without changing a single byte
|
|
96
96
|
on the wire. The full schema language is specified in
|
|
@@ -332,7 +332,7 @@ bytes and produce the same output bytes for every endpoint.
|
|
|
332
332
|
|
|
333
333
|
Admin commands use the regular command shape but are gated to the host's admin
|
|
334
334
|
account: trust management (`authorize`, `unauthorize`), guardian management
|
|
335
|
-
(`appoint`, `dismiss`),
|
|
335
|
+
(`appoint`, `dismiss`), metadata (`label`, `publishSchema`), asset gating (`allowAssets`,
|
|
336
336
|
`denyAssets`, `allowance`), lifecycle (`init`, `destroy`), and raw calls
|
|
337
337
|
(`executePayable`). Guards go the other way: direct actions guardians can take
|
|
338
338
|
without any command context — the default is `revoke`, which lets a guardian
|
package/blocks/Cursors.sol
CHANGED
|
@@ -726,6 +726,19 @@ library Cursors {
|
|
|
726
726
|
cur.ensureAt(end);
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
/// @notice Consume a SCHEMA block and return its fields.
|
|
730
|
+
/// @param cur Cursor; advanced past the SCHEMA block.
|
|
731
|
+
/// @return key Block key being defined.
|
|
732
|
+
/// @return body Schema DSL string describing the block payload body.
|
|
733
|
+
/// @return name Optional block alias.
|
|
734
|
+
function unpackSchema(Cur memory cur) internal pure returns (bytes4 key, string memory body, bytes32 name) {
|
|
735
|
+
uint end = cur.enter(Keys.Schema, 36 + Sizes.Header, 0);
|
|
736
|
+
key = cur.read4();
|
|
737
|
+
body = cur.unpackString();
|
|
738
|
+
name = cur.read32();
|
|
739
|
+
cur.ensureAt(end);
|
|
740
|
+
}
|
|
741
|
+
|
|
729
742
|
/// @notice Consume a dynamic block with a single bytes32 payload.
|
|
730
743
|
/// @param cur Cursor; advanced past the block.
|
|
731
744
|
/// @param key Expected dynamic block key.
|
package/blocks/Keys.sol
CHANGED
|
@@ -21,6 +21,8 @@ library Keys {
|
|
|
21
21
|
bytes4 constant Empty = bytes4(0);
|
|
22
22
|
/// @dev Wildcard key used in discovery when any block stream is accepted.
|
|
23
23
|
bytes4 constant Any = 0xffffffff;
|
|
24
|
+
/// @dev Default context-local block key for hosts/endpoints that need one custom schema.
|
|
25
|
+
bytes4 constant Local = bytes4(uint32(1));
|
|
24
26
|
/// @dev Input amount - (bytes32 asset, uint amount)
|
|
25
27
|
bytes4 constant Amount = bytes4(keccak256("#amount"));
|
|
26
28
|
/// @dev Ledger balance - (bytes32 asset, uint amount)
|
|
@@ -71,6 +73,8 @@ library Keys {
|
|
|
71
73
|
bytes4 constant Bounty = bytes4(keccak256("#bounty"));
|
|
72
74
|
/// @dev Mutable node label - (uint id, bytes32 namespace, #string as name)
|
|
73
75
|
bytes4 constant Label = bytes4(keccak256("#label"));
|
|
76
|
+
/// @dev Block schema publication - (bytes4 key, #string as body, bytes32 name)
|
|
77
|
+
bytes4 constant Schema = bytes4(keccak256("#schema"));
|
|
74
78
|
|
|
75
79
|
/// @dev Structural status form - (uint code)
|
|
76
80
|
bytes4 constant Status = bytes4(keccak256("#status"));
|
package/blocks/Schema.sol
CHANGED
|
@@ -28,9 +28,9 @@ pragma solidity ^0.8.33;
|
|
|
28
28
|
// are offchain projection metadata only and do not change runtime encoding
|
|
29
29
|
// - child blocks resolve by alias in the active schema context; unresolved aliases are invalid
|
|
30
30
|
// - schema strings describe the payload body only; the `Block` event carries the alias
|
|
31
|
-
// -
|
|
32
|
-
// -
|
|
33
|
-
// - child
|
|
31
|
+
// - items are encoded in declaration order
|
|
32
|
+
// - fixed fields are packed inline and any number of child blocks are embedded directly
|
|
33
|
+
// - child blocks may appear between fixed fields because each block carries its own length
|
|
34
34
|
// - `#bytes` is a reserved child block that stores raw bytes and has no body
|
|
35
35
|
// - `#string` is a reserved child block that stores UTF-8 string bytes and has no body
|
|
36
36
|
// - generic lists use the stable key derived from `#list`
|
|
@@ -87,6 +87,7 @@ library Schemas {
|
|
|
87
87
|
string constant Fee = "{ uint amount }";
|
|
88
88
|
string constant Auth = "{ uint cid, uint deadline, #bytes as proof }";
|
|
89
89
|
string constant Label = "{ uint id, bytes32 namespace, #string as name }";
|
|
90
|
+
string constant Schema = "{ bytes4 key, #string as body, bytes32 name }";
|
|
90
91
|
string constant Bytes = "";
|
|
91
92
|
string constant String = "";
|
|
92
93
|
string constant List = "";
|
package/commands/Credit.sol
CHANGED
|
@@ -20,15 +20,10 @@ abstract contract CreditAccountHook {
|
|
|
20
20
|
/// Use for internally recording credits that have already been settled externally.
|
|
21
21
|
abstract contract CreditAccount is CommandBase, CreditAccountHook {
|
|
22
22
|
bytes32 private immutable descriptor;
|
|
23
|
-
uint
|
|
23
|
+
uint internal immutable creditAccountId;
|
|
24
24
|
|
|
25
25
|
constructor() {
|
|
26
|
-
(
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/// @notice Return true if `candidate` is this command's credit account ID.
|
|
30
|
-
function isCreditAccount(uint candidate) internal view returns (bool) {
|
|
31
|
-
return candidate == id;
|
|
26
|
+
(creditAccountId, descriptor) = command("creditAccount", Keys.Balance, Keys.Empty, Keys.Empty, 0, false, false);
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
/// @notice Credit each BALANCE block from the command state to the command account.
|
package/commands/Debit.sol
CHANGED
|
@@ -22,15 +22,10 @@ abstract contract DebitAccountHook {
|
|
|
22
22
|
/// AMOUNT block; the default batch implementation handles the full request loop.
|
|
23
23
|
abstract contract DebitAccount is CommandBase, DebitAccountHook {
|
|
24
24
|
bytes32 private immutable descriptor;
|
|
25
|
-
uint
|
|
25
|
+
uint internal immutable debitAccountId;
|
|
26
26
|
|
|
27
27
|
constructor() {
|
|
28
|
-
(
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/// @notice Return true if `candidate` is this command's debit account ID.
|
|
32
|
-
function isDebitAccount(uint candidate) internal view returns (bool) {
|
|
33
|
-
return candidate == id;
|
|
28
|
+
(debitAccountId, descriptor) = command("debitAccount", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
/// @notice Override to customize request parsing or batching for debits.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import {AdminBase, CommandContext, Keys} from "./Base.sol";
|
|
5
|
+
import {Cursors, Cur} from "../../Cursors.sol";
|
|
6
6
|
using Cursors for Cur;
|
|
7
7
|
|
|
8
8
|
/// @title Authorize
|
|
@@ -11,17 +11,16 @@ using Cursors for Cur;
|
|
|
11
11
|
/// Only callable by the admin account.
|
|
12
12
|
abstract contract Authorize is AdminBase {
|
|
13
13
|
bytes32 private immutable descriptor;
|
|
14
|
+
uint internal immutable authorizeId;
|
|
14
15
|
|
|
15
16
|
constructor() {
|
|
16
|
-
(, descriptor) = command("authorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
|
|
17
|
+
(authorizeId, descriptor) = command("authorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/// @notice Authorize each NODE block in the admin request.
|
|
20
21
|
/// @param c Admin command context; `c.input` must contain NODE blocks.
|
|
21
22
|
/// @return Empty output state.
|
|
22
|
-
function authorize(
|
|
23
|
-
CommandContext calldata c
|
|
24
|
-
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
23
|
+
function authorize(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
|
|
25
24
|
(Cur memory input, ) = openInput(c.input, descriptor);
|
|
26
25
|
|
|
27
26
|
while (input.i < input.len) {
|
|
@@ -31,8 +30,3 @@ abstract contract Authorize is AdminBase {
|
|
|
31
30
|
return "";
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {AdminBase, CommandContext, Keys} from "./Base.sol";
|
|
5
|
+
import {Cursors, Cur} from "../../Cursors.sol";
|
|
6
|
+
using Cursors for Cur;
|
|
7
|
+
|
|
8
|
+
/// @title PublishSchema
|
|
9
|
+
/// @notice Admin command that publishes block schemas for keys.
|
|
10
|
+
/// Each SCHEMA block in the request emits one `Schema` event. Only callable by
|
|
11
|
+
/// the admin account.
|
|
12
|
+
abstract contract PublishSchema is AdminBase {
|
|
13
|
+
bytes32 private immutable descriptor;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
(, descriptor) = command("publishSchema", Keys.Empty, Keys.Schema, Keys.Empty, 0, false, true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// @notice Publish each SCHEMA block in the admin request.
|
|
20
|
+
/// @param c Admin command context; `c.input` must contain SCHEMA blocks.
|
|
21
|
+
/// @return Empty output state.
|
|
22
|
+
function publishSchema(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
|
|
23
|
+
(Cur memory input, ) = openInput(c.input, descriptor);
|
|
24
|
+
|
|
25
|
+
while (input.i < input.len) {
|
|
26
|
+
(bytes4 key, string memory body, bytes32 name) = input.unpackSchema();
|
|
27
|
+
emit Schema(host, key, body, name);
|
|
28
|
+
}
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import {AdminBase, CommandContext, Keys} from "./Base.sol";
|
|
5
|
+
import {Cursors, Cur} from "../../Cursors.sol";
|
|
6
6
|
using Cursors for Cur;
|
|
7
7
|
|
|
8
8
|
/// @title Unauthorize
|
|
@@ -11,17 +11,16 @@ using Cursors for Cur;
|
|
|
11
11
|
/// Only callable by the admin account.
|
|
12
12
|
abstract contract Unauthorize is AdminBase {
|
|
13
13
|
bytes32 private immutable descriptor;
|
|
14
|
+
uint internal immutable unauthorizeId;
|
|
14
15
|
|
|
15
16
|
constructor() {
|
|
16
|
-
(, descriptor) = command("unauthorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
|
|
17
|
+
(unauthorizeId, descriptor) = command("unauthorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/// @notice Unauthorize each NODE block in the admin request.
|
|
20
21
|
/// @param c Admin command context; `c.input` must contain NODE blocks.
|
|
21
22
|
/// @return Empty output state.
|
|
22
|
-
function unauthorize(
|
|
23
|
-
CommandContext calldata c
|
|
24
|
-
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
23
|
+
function unauthorize(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
|
|
25
24
|
(Cur memory input, ) = openInput(c.input, descriptor);
|
|
26
25
|
|
|
27
26
|
while (input.i < input.len) {
|
|
@@ -31,8 +30,3 @@ abstract contract Unauthorize is AdminBase {
|
|
|
31
30
|
return "";
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
package/core/Endpoint.sol
CHANGED
|
@@ -118,14 +118,21 @@ abstract contract EndpointBase is Runtime, EndpointEvent, LabeledEvent, SchemaEv
|
|
|
118
118
|
(input, , outputs) = openLane(source, descriptor, Lane.Input, 0);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
/// @notice Publish
|
|
122
|
-
/// @param key Block key being defined.
|
|
121
|
+
/// @notice Publish the default local block schema and return `Keys.Local`.
|
|
123
122
|
/// @param body Schema DSL string describing the block payload body.
|
|
124
|
-
/// @
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
/// @return The default context-local block key.
|
|
124
|
+
function localSchema(string memory body) internal returns (bytes4) {
|
|
125
|
+
return localSchema(1, body);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// @notice Publish a context-local block schema and return its key.
|
|
129
|
+
/// @param key Context-local key value.
|
|
130
|
+
/// @param body Schema DSL string describing the block payload body.
|
|
131
|
+
/// @return The context-local block key.
|
|
132
|
+
function localSchema(uint32 key, string memory body) internal returns (bytes4) {
|
|
133
|
+
bytes4 k = Keys.local(key);
|
|
134
|
+
emit Schema(host, k, body, bytes32(0));
|
|
135
|
+
return k;
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
/// @notice Create and publish endpoint metadata with a default label.
|
package/core/Host.sol
CHANGED
|
@@ -23,7 +23,7 @@ interface IHostIntroduction {
|
|
|
23
23
|
|
|
24
24
|
/// @title Host
|
|
25
25
|
/// @notice Abstract base contract for rootzero host implementations.
|
|
26
|
-
/// Inherits admin command support (authorize, unauthorize, executePayable),
|
|
26
|
+
/// Inherits admin command support (authorize, unauthorize, label, executePayable),
|
|
27
27
|
/// guardian management, the default guardian revoke action, and
|
|
28
28
|
/// optionally introduces itself to a commander host at deployment.
|
|
29
29
|
/// Accepts native ETH payments via the `receive` function.
|
package/docs/Schema.md
CHANGED
|
@@ -33,7 +33,7 @@ used. A host can publish the meaning of a custom key with:
|
|
|
33
33
|
event Schema(uint indexed host, bytes4 key, string schema, bytes32 name);
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
For example, a host-specific payment block can use `Keys.
|
|
36
|
+
For example, a host-specific payment block can use `Keys.Local`, the command
|
|
37
37
|
selector, or any other chosen `bytes4` value as long as that key is not
|
|
38
38
|
overloaded in the relevant host/schema context.
|
|
39
39
|
|
|
@@ -65,16 +65,19 @@ A schema body is a comma-separated list of items. Order is significant.
|
|
|
65
65
|
|
|
66
66
|
## Payload Layout
|
|
67
67
|
|
|
68
|
-
A block payload
|
|
69
|
-
|
|
68
|
+
A block payload encodes schema items in declaration order. Fixed fields are
|
|
69
|
+
packed inline, and zero or more child blocks are embedded directly at their
|
|
70
|
+
declared positions. Because every child block carries its own header and payload
|
|
71
|
+
length, fixed fields may appear before, after, or between child blocks.
|
|
70
72
|
|
|
71
73
|
```txt
|
|
72
74
|
{ uint target, uint resources, #bytes as payload }
|
|
73
75
|
{ bytes32 account, #bytes as state, #bytes as request }
|
|
76
|
+
{ bytes4 key, #string as body, bytes32 name }
|
|
77
|
+
{ #bytes as left, uint op, #bytes as right }
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
child-block tail.
|
|
80
|
+
There is no wrapper around embedded child blocks.
|
|
78
81
|
|
|
79
82
|
Raw dynamic bytes are represented with the reserved `#bytes` child block. Use an
|
|
80
83
|
alias to give those bytes a presentation name:
|
|
@@ -293,19 +296,21 @@ invalid in any path segment.
|
|
|
293
296
|
## Reserved Blocks
|
|
294
297
|
|
|
295
298
|
- `#bytes`: raw dynamic bytes, written without a body
|
|
299
|
+
- `#string`: UTF-8 string bytes, written without a body
|
|
296
300
|
- `#list`: generic list wrapper emitted by `many`
|
|
297
301
|
|
|
298
302
|
Custom input shapes should define their own context-local block key and publish
|
|
299
303
|
that key with a `Schema` event:
|
|
300
304
|
|
|
301
305
|
```solidity
|
|
302
|
-
bytes4 constant Input = Keys.
|
|
306
|
+
bytes4 constant Input = Keys.Local;
|
|
303
307
|
emit Schema(host, Input, "{ bytes32 asset, uint amount }", bytes32("payment"));
|
|
304
308
|
```
|
|
305
309
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
310
|
+
Use `Keys.local(n)` when a host needs more than one local block key. The key can
|
|
311
|
+
also be a small literal, a selector, or any other `bytes4` value that is unique
|
|
312
|
+
in the context where it is used. The alias names the block; the schema string
|
|
313
|
+
describes only the payload body.
|
|
309
314
|
|
|
310
315
|
## Standard Blocks
|
|
311
316
|
|
|
@@ -320,6 +325,7 @@ step { uint target, uint resources, #bytes as request }
|
|
|
320
325
|
context { bytes32 account, #bytes as state, #bytes as request }
|
|
321
326
|
recover { uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
322
327
|
auth { uint cid, uint deadline, #bytes as proof }
|
|
328
|
+
schema { bytes4 key, #string as body, bytes32 name }
|
|
323
329
|
```
|
|
324
330
|
|
|
325
331
|
`Keys.sol` contains the corresponding standard runtime keys.
|