@rootzero/contracts 1.13.0 → 1.15.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 +87 -3
- package/Codec.sol +21 -0
- package/Commands.sol +14 -0
- package/Core.sol +8 -6
- package/Endpoints.sol +2 -7
- package/Events.sol +1 -2
- package/README.md +70 -37
- package/Utils.sol +2 -4
- package/annotations/Action.sol +17 -0
- package/annotations/Label.sol +23 -0
- package/annotations/Schema.sol +53 -0
- package/codec/Blocks.sol +1643 -0
- package/codec/Buffers.sol +165 -0
- package/codec/Decoders.sol +570 -0
- package/codec/Descriptors.sol +124 -0
- package/{blocks → codec}/Keys.sol +10 -18
- package/codec/Readers.sol +114 -0
- package/{blocks → codec}/Schema.sol +43 -80
- package/codec/Specs.sol +240 -0
- package/codec/Writers.sol +485 -0
- package/commands/Allocate.sol +21 -20
- package/commands/Base.sol +83 -45
- package/commands/Burn.sol +23 -14
- package/commands/Credit.sol +21 -20
- package/commands/Debit.sol +25 -28
- package/commands/Deposit.sol +41 -36
- package/commands/Payout.sol +26 -16
- package/commands/Provision.sol +37 -38
- package/commands/Recover.sol +20 -19
- package/commands/Relay.sol +24 -22
- package/commands/Withdraw.sol +20 -19
- package/commands/admin/AllowAssets.sol +19 -16
- package/commands/admin/Allowance.sol +18 -13
- package/commands/admin/Annotate.sol +36 -0
- package/commands/admin/Appoint.sol +19 -16
- package/commands/admin/Authorize.sol +23 -14
- package/commands/admin/Base.sol +9 -2
- package/commands/admin/DenyAssets.sol +19 -16
- package/commands/admin/Dismiss.sol +19 -16
- package/commands/admin/Execute.sol +21 -19
- package/commands/admin/Unauthorize.sol +23 -14
- package/core/Access.sol +91 -49
- package/core/Calls.sol +30 -33
- package/core/Endpoint.sol +46 -143
- package/core/Host.sol +63 -21
- package/core/Pipeline.sol +16 -15
- package/core/Types.sol +1 -1
- package/docs/Schema.md +48 -31
- package/events/Annotation.sol +24 -0
- package/events/Endpoint.sol +2 -2
- package/events/Guardian.sol +2 -2
- package/events/Introduction.sol +3 -2
- package/execution/Budget.sol +40 -0
- package/execution/Execution.sol +1104 -0
- package/guards/Base.sol +11 -12
- package/guards/Revoke.sol +11 -9
- package/package.json +1 -1
- package/ports/AllowAssets.sol +12 -15
- package/ports/Allowance.sol +11 -9
- package/ports/Base.sol +35 -16
- package/ports/Credit.sol +11 -9
- package/ports/Debit.sol +11 -9
- package/ports/DenyAssets.sol +10 -13
- package/ports/Dispatch.sol +13 -15
- package/ports/Pipe.sol +14 -12
- package/ports/Redeem.sol +12 -9
- package/ports/Settle.sol +16 -10
- package/queries/Assets.sol +15 -15
- package/queries/Balances.sol +17 -17
- package/queries/Base.sol +26 -12
- package/utils/Accounts.sol +0 -23
- package/utils/Actions.sol +1 -0
- package/utils/Cursors.sol +367 -0
- package/utils/Lanes.sol +14 -0
- package/utils/Layout.sol +0 -2
- package/utils/Selectors.sol +2 -2
- package/utils/Utils.sol +46 -0
- package/Cursors.sol +0 -16
- package/blocks/Cursors.sol +0 -1529
- package/blocks/Writers.sol +0 -1036
- package/commands/admin/Label.sol +0 -32
- package/commands/admin/Schemas.sol +0 -32
- package/core/Payable.sol +0 -53
- package/events/Labeled.sol +0 -21
- package/events/Schema.sol +0 -23
- package/utils/Value.sol +0 -43
package/core/Endpoint.sol
CHANGED
|
@@ -1,162 +1,65 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {Keys} from "../blocks/Keys.sol";
|
|
4
|
+
import {Execution, Executions} from "../execution/Execution.sol";
|
|
6
5
|
import {EndpointEvent} from "../events/Endpoint.sol";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
/// @title Lane
|
|
12
|
-
/// @notice Bit offsets for endpoint descriptor lanes.
|
|
13
|
-
library Lane {
|
|
14
|
-
/// @dev Descriptor shift for the state lane.
|
|
15
|
-
uint internal constant State = 184;
|
|
16
|
-
/// @dev Descriptor shift for the input lane.
|
|
17
|
-
uint internal constant Input = 112;
|
|
18
|
-
/// @dev Descriptor shift for the output lane.
|
|
19
|
-
uint internal constant Output = 40;
|
|
20
|
-
}
|
|
6
|
+
import {Label} from "../annotations/Label.sol";
|
|
7
|
+
import {Schema} from "../annotations/Schema.sol";
|
|
8
|
+
import {Descriptors} from "../codec/Descriptors.sol";
|
|
21
9
|
|
|
22
10
|
/// @title EndpointBase
|
|
23
11
|
/// @notice Shared endpoint metadata helpers.
|
|
24
|
-
abstract contract EndpointBase is
|
|
25
|
-
/// @
|
|
26
|
-
///
|
|
27
|
-
///
|
|
28
|
-
///
|
|
29
|
-
/// @param
|
|
30
|
-
/// @param
|
|
31
|
-
/// @param
|
|
32
|
-
/// @param
|
|
33
|
-
/// @
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
value |= uint(funded ? 1 : 0) << 32;
|
|
46
|
-
value |= uint(admin ? 1 : 0) << 33;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/// @dev Return a lane's effective group size, defaulting non-empty lanes to one.
|
|
50
|
-
/// @param descriptor Packed endpoint descriptor.
|
|
51
|
-
/// @param shift Bit offset of the lane to inspect.
|
|
52
|
-
/// @return size Effective group size, or zero when the lane is absent.
|
|
53
|
-
function laneGroup(bytes32 descriptor, uint shift) private pure returns (uint8 size) {
|
|
54
|
-
uint72 lane = uint72(uint(descriptor) >> shift);
|
|
55
|
-
if (lane == 0) return 0;
|
|
56
|
-
|
|
57
|
-
size = uint8(lane);
|
|
58
|
-
if (size == 0) size = 1;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/// @dev Open a descriptor lane and return its effective group and output counts.
|
|
62
|
-
/// An absent lane inherits `expected`; a present lane must match it when nonzero.
|
|
63
|
-
/// @param source Block stream to open for the requested lane.
|
|
64
|
-
/// @param descriptor Packed endpoint descriptor.
|
|
65
|
-
/// @param shift Bit offset of the lane to open.
|
|
66
|
-
/// @param expected Required group count, or zero to accept the lane's count.
|
|
67
|
-
/// @return cur Cursor scoped to the lane's first block run.
|
|
68
|
-
/// @return groups Number of lane groups in `cur`, or `expected` for an absent lane.
|
|
69
|
-
/// @return outputs Number of output blocks implied by `groups` and the descriptor output lane.
|
|
70
|
-
function openLane(
|
|
71
|
-
bytes calldata source,
|
|
72
|
-
bytes32 descriptor,
|
|
73
|
-
uint shift,
|
|
74
|
-
uint expected
|
|
75
|
-
) internal pure returns (Cur memory cur, uint groups, uint outputs) {
|
|
76
|
-
(cur, groups) = Cursors.init(source, laneGroup(descriptor, shift));
|
|
77
|
-
if (groups == 0) groups = expected;
|
|
78
|
-
else if (expected != 0 && groups != expected) revert Cursors.BadRatio();
|
|
79
|
-
outputs = groups * laneGroup(descriptor, Lane.Output);
|
|
12
|
+
abstract contract EndpointBase is EndpointEvent, Label, Schema {
|
|
13
|
+
/// @notice Create and publish endpoint metadata with a default label.
|
|
14
|
+
/// @param id Endpoint node ID.
|
|
15
|
+
/// @param name Default human-readable endpoint label.
|
|
16
|
+
/// @param state State block specification.
|
|
17
|
+
/// @param input Input block specification.
|
|
18
|
+
/// @param output Output block specification.
|
|
19
|
+
/// @param transactions Number of transaction blocks produced per batch, or zero for none.
|
|
20
|
+
/// @param flags Packed endpoint behavior flags.
|
|
21
|
+
/// @return descriptor Packed endpoint lane metadata and flags.
|
|
22
|
+
function endpoint(
|
|
23
|
+
uint id,
|
|
24
|
+
string memory name,
|
|
25
|
+
uint state,
|
|
26
|
+
uint input,
|
|
27
|
+
uint output,
|
|
28
|
+
uint8 transactions,
|
|
29
|
+
uint8 flags
|
|
30
|
+
) internal returns (uint descriptor) {
|
|
31
|
+
descriptor = Descriptors.create(state, input, output, transactions, flags);
|
|
32
|
+
return endpoint(id, name, descriptor);
|
|
80
33
|
}
|
|
81
34
|
|
|
82
|
-
/// @notice
|
|
83
|
-
/// @param
|
|
84
|
-
/// @param
|
|
85
|
-
/// @
|
|
86
|
-
/// @return
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
bytes32
|
|
90
|
-
|
|
91
|
-
(state, , outputs) = openLane(source, descriptor, Lane.State, 0);
|
|
35
|
+
/// @notice Publish already constructed endpoint metadata with a default label.
|
|
36
|
+
/// @param id Endpoint node ID.
|
|
37
|
+
/// @param name Default human-readable endpoint label.
|
|
38
|
+
/// @param descriptor Packed endpoint lane metadata and flags.
|
|
39
|
+
/// @return The published endpoint descriptor.
|
|
40
|
+
function endpoint(uint id, string memory name, uint descriptor) internal returns (uint) {
|
|
41
|
+
emit Endpoint(host, id, descriptor);
|
|
42
|
+
label(id, bytes32(0), name);
|
|
43
|
+
return descriptor;
|
|
92
44
|
}
|
|
93
45
|
|
|
94
|
-
/// @notice Open an endpoint input stream
|
|
46
|
+
/// @notice Open an endpoint input stream with an expected batch count.
|
|
95
47
|
/// @param source Input block stream to open.
|
|
96
48
|
/// @param descriptor Packed endpoint descriptor.
|
|
97
|
-
/// @
|
|
98
|
-
/// @return
|
|
49
|
+
/// @param batches Required batch count, or zero to accept the input count.
|
|
50
|
+
/// @return exec Execution with its output buffer metadata initialized for the input batch count.
|
|
99
51
|
function openInput(
|
|
100
52
|
bytes calldata source,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/// @notice Return an 8-byte lane value for a generic LIST containing `item`.
|
|
107
|
-
/// @param item Block key expected inside each LIST payload.
|
|
108
|
-
/// @return Packed lane key `[Keys.List][item]`.
|
|
109
|
-
function many(bytes4 item) internal pure returns (bytes8) {
|
|
110
|
-
return bytes8(bytes.concat(Keys.List, item));
|
|
53
|
+
uint descriptor,
|
|
54
|
+
uint batches
|
|
55
|
+
) internal view returns (Execution memory exec) {
|
|
56
|
+
return Executions.openInput(source, descriptor, batches);
|
|
111
57
|
}
|
|
112
58
|
|
|
113
|
-
/// @notice
|
|
114
|
-
/// @param
|
|
115
|
-
/// @
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return bytes9(bytes.concat(value, bytes1(size)));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/// @notice Publish a context-local block schema and return its key.
|
|
122
|
-
/// @param key Context-local key value.
|
|
123
|
-
/// @param body Schema DSL string describing the block payload body.
|
|
124
|
-
/// @return The context-local block key.
|
|
125
|
-
function schema(uint32 key, string memory body) internal returns (bytes4) {
|
|
126
|
-
return schema(key, body, bytes32(0));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/// @notice Publish a named context-local block schema and return its key.
|
|
130
|
-
/// @param key Context-local key value.
|
|
131
|
-
/// @param body Schema DSL string describing the block payload body.
|
|
132
|
-
/// @param name Schema alias name, or zero for unnamed schemas.
|
|
133
|
-
/// @return The context-local block key.
|
|
134
|
-
function schema(uint32 key, string memory body, bytes32 name) internal returns (bytes4) {
|
|
135
|
-
bytes4 k = bytes4(key);
|
|
136
|
-
emit Schema(host, k, body, name);
|
|
137
|
-
return k;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/// @notice Create and publish endpoint metadata with a default label.
|
|
141
|
-
/// @param id Endpoint node ID.
|
|
142
|
-
/// @param name Default human-readable endpoint label.
|
|
143
|
-
/// @param state Packed state lane plus optional group byte.
|
|
144
|
-
/// @param input Packed input lane plus optional group byte.
|
|
145
|
-
/// @param output Packed output lane plus optional group byte.
|
|
146
|
-
/// @param funded Whether the endpoint accepts nonzero native value.
|
|
147
|
-
/// @param admin Whether the endpoint is restricted to the admin account.
|
|
148
|
-
/// @return descriptor Packed endpoint lane metadata and flags.
|
|
149
|
-
function endpoint(
|
|
150
|
-
uint id,
|
|
151
|
-
string memory name,
|
|
152
|
-
bytes9 state,
|
|
153
|
-
bytes9 input,
|
|
154
|
-
bytes9 output,
|
|
155
|
-
bool funded,
|
|
156
|
-
bool admin
|
|
157
|
-
) internal returns (bytes32 descriptor) {
|
|
158
|
-
descriptor = bytes32(pack(state, input, output, funded, admin));
|
|
159
|
-
emit Endpoint(host, id, descriptor);
|
|
160
|
-
emit Labeled(id, bytes32(0), name);
|
|
59
|
+
/// @notice Finalize an execution output and return its encoded block stream.
|
|
60
|
+
/// @param exec Completed endpoint execution.
|
|
61
|
+
/// @return Encoded output block stream.
|
|
62
|
+
function close(Execution memory exec) internal pure returns (bytes memory) {
|
|
63
|
+
return Executions.finish(exec);
|
|
161
64
|
}
|
|
162
65
|
}
|
package/core/Host.sol
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {AccessDenied, CallerAccess, CommanderAccess} from "./Access.sol";
|
|
5
|
+
import {Runtime} from "./Runtime.sol";
|
|
6
|
+
import {Annotate} from "../commands/admin/Annotate.sol";
|
|
5
7
|
import {Appoint} from "../commands/admin/Appoint.sol";
|
|
6
8
|
import {Authorize} from "../commands/admin/Authorize.sol";
|
|
7
9
|
import {Dismiss} from "../commands/admin/Dismiss.sol";
|
|
8
10
|
import {Unauthorize} from "../commands/admin/Unauthorize.sol";
|
|
9
11
|
import {ExecutePayable} from "../commands/admin/Execute.sol";
|
|
10
|
-
import {Label} from "../commands/admin/Label.sol";
|
|
11
12
|
import {Revoke} from "../guards/Revoke.sol";
|
|
12
13
|
import {IntroductionEvent} from "../events/Introduction.sol";
|
|
14
|
+
import {Accounts} from "../utils/Accounts.sol";
|
|
13
15
|
import {Nodes} from "../utils/Nodes.sol";
|
|
14
16
|
|
|
15
17
|
/// @title IHostIntroduction
|
|
@@ -21,6 +23,55 @@ interface IHostIntroduction {
|
|
|
21
23
|
function introduce(uint peer, uint blocknum) external;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
/// @title HostIntroduction
|
|
27
|
+
/// @notice Shared deployment-time introduction behavior for rootzero hosts.
|
|
28
|
+
/// Calls a deployed commander during construction without adding an inbound
|
|
29
|
+
/// introduction endpoint to the inheriting host.
|
|
30
|
+
abstract contract HostIntroduction is Runtime {
|
|
31
|
+
/// @param cmdr Commander address to introduce this host to when it is a deployed contract.
|
|
32
|
+
/// @dev Deployment reverts if a contract commander does not accept `introduce(uint,uint)`.
|
|
33
|
+
constructor(address cmdr) {
|
|
34
|
+
if (cmdr == address(0) || cmdr == address(this) || cmdr.code.length == 0) return;
|
|
35
|
+
introduceTo(Nodes.toHost(cmdr));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// @notice Introduce this host to the contract address embedded in a local EVM node ID.
|
|
39
|
+
/// @dev Accepts host and endpoint IDs such as commands, ports, queries, and guards.
|
|
40
|
+
/// Reverts when `node` is not local or embeds the zero address.
|
|
41
|
+
/// @param node Local EVM node ID whose underlying contract receives the introduction.
|
|
42
|
+
function introduceTo(uint node) internal {
|
|
43
|
+
IHostIntroduction(Nodes.addr(node)).introduce(host, block.number);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// @title CommandHost
|
|
48
|
+
/// @notice Minimal host base for commander-only command execution.
|
|
49
|
+
/// Does not include admin commands, peer authorization, guardians, inbound
|
|
50
|
+
/// introductions, generic execution, or a native-token receive function.
|
|
51
|
+
/// Commands using trusted `NodeCalls` must separately compose a `TrustAccess` policy.
|
|
52
|
+
abstract contract CommandHost is CommanderAccess, CallerAccess, HostIntroduction {
|
|
53
|
+
/// @dev Thrown when a commander-only host is deployed without an external commander.
|
|
54
|
+
error InvalidCommander();
|
|
55
|
+
|
|
56
|
+
/// @param cmdr Nonzero address allowed to invoke hosted commands.
|
|
57
|
+
constructor(address cmdr) CommanderAccess(cmdr) HostIntroduction(cmdr) {
|
|
58
|
+
if (cmdr == address(0)) revert InvalidCommander();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function enforceCaller(address caller) internal view virtual override returns (address) {
|
|
62
|
+
return enforceCommander(caller);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// @title Admins
|
|
68
|
+
/// @notice Optional bundle of the default host administration commands.
|
|
69
|
+
abstract contract Admins is Annotate, ExecutePayable, Authorize, Unauthorize {}
|
|
70
|
+
|
|
71
|
+
/// @title Guardians
|
|
72
|
+
/// @notice Optional bundle for guardian management and the default revoke guard.
|
|
73
|
+
abstract contract Guardians is Appoint, Dismiss, Revoke {}
|
|
74
|
+
|
|
24
75
|
/// @title Host
|
|
25
76
|
/// @notice Abstract base contract for rootzero host implementations.
|
|
26
77
|
/// Inherits admin command support (authorize, unauthorize, label, executePayable),
|
|
@@ -28,30 +79,21 @@ interface IHostIntroduction {
|
|
|
28
79
|
/// optionally introduces itself to a commander host at deployment.
|
|
29
80
|
/// Accepts native ETH payments via the `receive` function.
|
|
30
81
|
abstract contract Host is
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Appoint,
|
|
35
|
-
Dismiss,
|
|
36
|
-
Label,
|
|
37
|
-
ExecutePayable,
|
|
82
|
+
Admins,
|
|
83
|
+
Guardians,
|
|
84
|
+
HostIntroduction,
|
|
38
85
|
IntroductionEvent,
|
|
39
86
|
IHostIntroduction
|
|
40
87
|
{
|
|
41
|
-
/// @param cmdr Commander address;
|
|
88
|
+
/// @param cmdr Commander address; used by the composed access capabilities.
|
|
42
89
|
/// If `cmdr` is a deployed contract, the host calls `introduce`
|
|
43
90
|
/// on it during construction.
|
|
44
|
-
constructor(address cmdr)
|
|
45
|
-
if (cmdr == address(0) || cmdr == address(this) || cmdr.code.length == 0) return;
|
|
46
|
-
introduceTo(Nodes.toHost(cmdr));
|
|
47
|
-
}
|
|
91
|
+
constructor(address cmdr) CommanderAccess(cmdr) HostIntroduction(cmdr) {}
|
|
48
92
|
|
|
49
|
-
/// @notice
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function introduceTo(uint node) internal {
|
|
54
|
-
IHostIntroduction(Nodes.addr(node)).introduce(host, block.number);
|
|
93
|
+
/// @notice Assert that `caller` may invoke commands on a peer-aware host.
|
|
94
|
+
function enforceCaller(address caller) internal view virtual override returns (address) {
|
|
95
|
+
if (caller == address(0) || !isTrustedCaller(caller)) revert AccessDenied();
|
|
96
|
+
return caller;
|
|
55
97
|
}
|
|
56
98
|
|
|
57
99
|
/// @notice Record a host introduction claim.
|
|
@@ -59,7 +101,7 @@ abstract contract Host is
|
|
|
59
101
|
/// @param peer Host node ID being introduced.
|
|
60
102
|
/// @param blocknum Block number at which the host was deployed.
|
|
61
103
|
function introduce(uint peer, uint blocknum) external {
|
|
62
|
-
emit Introduction(host, Nodes.matchHost(peer, msg.sender), blocknum);
|
|
104
|
+
emit Introduction(host, Nodes.matchHost(peer, msg.sender), Accounts.toUser(tx.origin), blocknum);
|
|
63
105
|
}
|
|
64
106
|
|
|
65
107
|
/// @notice Accept native ETH transfers (e.g. from command value flows).
|
package/core/Pipeline.sol
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {Payable} from "./Payable.sol";
|
|
4
|
+
import {Decoders, Cur, Readers, Reader} from "../Codec.sol";
|
|
6
5
|
import {Settlement} from "./Settlement.sol";
|
|
7
|
-
import {Budget} from "../
|
|
6
|
+
import {Budget, Budgets} from "../execution/Budget.sol";
|
|
8
7
|
|
|
9
|
-
using
|
|
8
|
+
using Decoders for Cur;
|
|
10
9
|
using Readers for Reader;
|
|
10
|
+
using Budgets for Budget;
|
|
11
11
|
|
|
12
12
|
/// @title Pipeline
|
|
13
13
|
/// @notice Core pipeline functionality shared by higher-level surfaces.
|
|
14
|
-
abstract contract Pipeline is
|
|
14
|
+
abstract contract Pipeline is Settlement {
|
|
15
15
|
/// @dev Thrown when the pipeline finishes with non-empty threaded state.
|
|
16
16
|
error UnexpectedState();
|
|
17
17
|
|
|
@@ -19,20 +19,20 @@ abstract contract Pipeline is Payable, Settlement {
|
|
|
19
19
|
/// Called once per STEP block. The returned state becomes the state passed to
|
|
20
20
|
/// the next step, and the final returned state must be empty. Returned
|
|
21
21
|
/// transactions are decoded and passed individually to `settle` before the next step runs.
|
|
22
|
-
/// @param
|
|
22
|
+
/// @param cmd Command node ID to invoke or handle.
|
|
23
23
|
/// @param account Account identifier for the piped context.
|
|
24
24
|
/// @param state Current threaded state block stream.
|
|
25
|
-
/// @param
|
|
25
|
+
/// @param input Step input block stream.
|
|
26
26
|
/// @param value Native EVM value assigned to this step.
|
|
27
|
-
/// @return
|
|
27
|
+
/// @return output Updated state block stream for the next step.
|
|
28
28
|
/// @return transactions Transaction block stream produced by the command.
|
|
29
29
|
function dispatch(
|
|
30
|
-
uint
|
|
30
|
+
uint cmd,
|
|
31
31
|
bytes32 account,
|
|
32
32
|
bytes memory state,
|
|
33
|
-
bytes calldata
|
|
33
|
+
bytes calldata input,
|
|
34
34
|
uint128 value
|
|
35
|
-
) internal virtual returns (bytes memory
|
|
35
|
+
) internal virtual returns (bytes memory output, bytes memory transactions);
|
|
36
36
|
|
|
37
37
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
38
38
|
/// @dev Reverts with `UnexpectedState` if the final threaded state is non-empty.
|
|
@@ -42,12 +42,13 @@ abstract contract Pipeline is Payable, Settlement {
|
|
|
42
42
|
/// @param steps STEP block stream to execute.
|
|
43
43
|
/// @param budget Mutable native-value budget shared across all steps.
|
|
44
44
|
function pipe(bytes32 account, bytes memory state, bytes calldata steps, Budget memory budget) internal {
|
|
45
|
-
|
|
45
|
+
Cur memory cur = Decoders.open(steps, 1);
|
|
46
46
|
|
|
47
|
-
while (
|
|
48
|
-
(uint
|
|
47
|
+
while (cur.more()) {
|
|
48
|
+
(uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
|
|
49
49
|
Reader memory txs;
|
|
50
|
-
(state, txs.source) = dispatch(
|
|
50
|
+
(state, txs.source) = dispatch(cmd, account, state, input, budget.use(resources));
|
|
51
|
+
|
|
51
52
|
while (txs.more()) {
|
|
52
53
|
(bytes32 from, bytes32 to, bytes32 asset, uint amount) = txs.unpackTransaction();
|
|
53
54
|
settle(from, to, asset, amount);
|
package/core/Types.sol
CHANGED
|
@@ -17,7 +17,7 @@ struct AccountAsset {
|
|
|
17
17
|
bytes32 asset;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/// @notice Account-scoped amount shape for
|
|
20
|
+
/// @notice Account-scoped amount shape for inputs, responses, and reporting.
|
|
21
21
|
struct AccountAmount {
|
|
22
22
|
/// @dev Account identifier.
|
|
23
23
|
bytes32 account;
|
package/docs/Schema.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Schema
|
|
2
2
|
|
|
3
|
-
Rootzero
|
|
3
|
+
Rootzero input and response data is encoded as a stream of typed blocks. A
|
|
4
4
|
schema string describes the payload body for discovery events and tooling; the
|
|
5
5
|
runtime block key is the compact type tag that identifies that payload layout in
|
|
6
6
|
the active schema context. The block alias is published separately from the
|
|
@@ -27,12 +27,23 @@ For example, the standard `amount` alias uses the key derived from `#amount`
|
|
|
27
27
|
and the schema body `{ bytes32 asset, uint amount }`. Custom block keys do not
|
|
28
28
|
have to be keccak-derived. They
|
|
29
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
|
|
30
|
+
used. A host can publish the meaning of a custom key as an annotation:
|
|
31
31
|
|
|
32
32
|
```solidity
|
|
33
|
-
event
|
|
33
|
+
event Annotation(uint indexed entity, bytes data);
|
|
34
|
+
#schema { uint spec, #string as body, bytes32 name }
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
Annotation merge behavior is defined by the annotation block type rather than
|
|
38
|
+
by the `Annotation` event. A `#schema` annotation is identified by its entity
|
|
39
|
+
and the block key encoded in `spec`: distinct keys accumulate, while the latest
|
|
40
|
+
trusted claim for the same key replaces the earlier one. Other annotation types
|
|
41
|
+
may define additive, historical, or explicitly revocable behavior instead.
|
|
42
|
+
|
|
43
|
+
The standard `#action { uint action }` annotation assigns one primary semantic
|
|
44
|
+
action to an entity. The latest trusted value replaces the previous value, and
|
|
45
|
+
`Actions.None` clears the classification.
|
|
46
|
+
|
|
36
47
|
For example, a host-specific payment block can use a small literal, the command
|
|
37
48
|
selector, or any other chosen `bytes4` value as long as that key is not
|
|
38
49
|
overloaded in the relevant host/schema context.
|
|
@@ -50,7 +61,7 @@ schema: { bytes32 asset, uint amount }
|
|
|
50
61
|
A block body can reference another block alias as a child item with `#`:
|
|
51
62
|
|
|
52
63
|
```txt
|
|
53
|
-
{ bytes32 account, #bytes as state, #bytes as
|
|
64
|
+
{ bytes32 account, #bytes as state, #bytes as input }
|
|
54
65
|
```
|
|
55
66
|
|
|
56
67
|
The empty schema string `""` means the block has no structured payload. This is
|
|
@@ -72,8 +83,8 @@ length, fixed fields may appear before, after, or between child blocks.
|
|
|
72
83
|
|
|
73
84
|
```txt
|
|
74
85
|
{ uint target, uint resources, #bytes as payload }
|
|
75
|
-
{ bytes32 account, #bytes as state, #bytes as
|
|
76
|
-
{
|
|
86
|
+
{ bytes32 account, #bytes as state, #bytes as input }
|
|
87
|
+
{ uint spec, #string as body, bytes32 name }
|
|
77
88
|
{ #bytes as left, uint op, #bytes as right }
|
|
78
89
|
```
|
|
79
90
|
|
|
@@ -108,24 +119,30 @@ generic list block; it does not repeat the item in place.
|
|
|
108
119
|
## Endpoint Lanes
|
|
109
120
|
|
|
110
121
|
Endpoint descriptors identify each lane with a block key and group size. In
|
|
111
|
-
Solidity, endpoint definition helpers accept
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
has no blocks in that lane. Use
|
|
116
|
-
`group(lane, size)` when a lane needs an explicit group size other than 1.
|
|
122
|
+
Solidity, endpoint definition helpers accept block specs such as `Specs.Amount`.
|
|
123
|
+
A zero group is interpreted as group size 1, while `Specs.Empty` means the
|
|
124
|
+
endpoint has no blocks in that lane. Use `group(spec, size)` when a lane needs
|
|
125
|
+
an explicit group size other than 1.
|
|
117
126
|
|
|
118
|
-
The packed descriptor
|
|
127
|
+
The packed descriptor uses these lane layouts:
|
|
119
128
|
|
|
120
129
|
```txt
|
|
121
|
-
[key
|
|
130
|
+
state [key:4][group:1]
|
|
131
|
+
input [key:4][item:4][group:1]
|
|
132
|
+
output [key:4][min:4][max:4][hint:4][group:1]
|
|
122
133
|
```
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
contained
|
|
128
|
-
with
|
|
135
|
+
Containers are exclusive to input. A plain input spec is compressed into
|
|
136
|
+
`[spec.key][0]`. A spec with a nonzero container is compressed into
|
|
137
|
+
`[spec.container][spec.key]`: the container is the top-level wire key and the
|
|
138
|
+
item is its contained block key. The built-in `many(spec)` helper annotates the
|
|
139
|
+
spec with `Specs.List` as its container, matching the DSL form `many #item`.
|
|
140
|
+
Output lanes retain their size bounds and allocation hint so execution can
|
|
141
|
+
reconstruct the output spec and initialize its writer directly. The Solidity
|
|
142
|
+
output decoder returns this as a left-aligned, writer-ready spec that retains
|
|
143
|
+
its encoded group. Its container and reserved fields are cleared. `Specs.group`
|
|
144
|
+
returns the effective group, interpreting an encoded zero as one for a
|
|
145
|
+
non-empty spec.
|
|
129
146
|
|
|
130
147
|
Any non-empty lane resolves its key to a block alias and schema body through the
|
|
131
148
|
active schema context. If the item slot is nonzero, tooling also resolves that
|
|
@@ -149,7 +166,7 @@ top-level structure.
|
|
|
149
166
|
|
|
150
167
|
## Field Aliases
|
|
151
168
|
|
|
152
|
-
Block aliases are published in `
|
|
169
|
+
Block aliases are published in `#schema` annotations. Field aliases are presentation
|
|
153
170
|
metadata for tooling. They do not change payload layout or runtime keys.
|
|
154
171
|
|
|
155
172
|
```txt
|
|
@@ -299,33 +316,33 @@ invalid in any path segment.
|
|
|
299
316
|
- `#string`: UTF-8 string bytes, written without a body
|
|
300
317
|
- `#list`: generic list wrapper emitted by `many`
|
|
301
318
|
|
|
302
|
-
Custom input shapes should define their own context-local block
|
|
303
|
-
|
|
304
|
-
that
|
|
319
|
+
Custom input shapes should define their own context-local block spec and publish
|
|
320
|
+
it with a `#schema` annotation. Endpoint contracts can use `schema(...)` to
|
|
321
|
+
construct and publish that spec:
|
|
305
322
|
|
|
306
323
|
```solidity
|
|
307
|
-
|
|
324
|
+
uint input = schema(1, 64, 64, 64, "{ bytes32 asset, uint amount }", bytes32(0));
|
|
308
325
|
```
|
|
309
326
|
|
|
310
327
|
Use different numeric keys when a host needs more than one local block key. The
|
|
311
328
|
key can also be a selector or any other `bytes4` value that is unique in the
|
|
312
|
-
context where it is used. The
|
|
313
|
-
|
|
329
|
+
context where it is used. The numeric arguments after the key are the minimum,
|
|
330
|
+
maximum, and allocation hint payload sizes. The alias names the block; the
|
|
331
|
+
schema string describes only the payload body.
|
|
314
332
|
|
|
315
333
|
## Standard Blocks
|
|
316
334
|
|
|
317
|
-
Common protocol schemas live in `contracts/
|
|
335
|
+
Common protocol schemas live in `contracts/codec/Schema.sol`:
|
|
318
336
|
|
|
319
337
|
```txt
|
|
320
338
|
amount { bytes32 asset, uint amount }
|
|
321
339
|
balance { bytes32 asset, uint amount }
|
|
322
340
|
custody { uint host, bytes32 asset, uint amount }
|
|
323
341
|
call { uint target, uint resources, #bytes as payload }
|
|
324
|
-
step { uint
|
|
325
|
-
context { bytes32 account, #bytes as state, #bytes as
|
|
342
|
+
step { uint cmd, uint resources, #bytes as input }
|
|
343
|
+
context { bytes32 account, #bytes as state, #bytes as input }
|
|
326
344
|
recover { uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
327
|
-
|
|
328
|
-
schema { bytes4 key, #string as body, bytes32 name }
|
|
345
|
+
schema { uint spec, #string as body, bytes32 name }
|
|
329
346
|
```
|
|
330
347
|
|
|
331
348
|
`Keys.sol` contains the corresponding standard runtime keys.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Emitted to attach encoded annotation blocks to an entity.
|
|
7
|
+
/// @dev `data` is a protocol block stream. A single block is the conventional
|
|
8
|
+
/// case, but related annotations may be emitted together. Annotations are claims
|
|
9
|
+
/// by the emitting contract, and each block key defines its annotation type.
|
|
10
|
+
/// Consumers process events in log order and blocks in stream order, then apply
|
|
11
|
+
/// the identity and merge rules defined by each annotation type. The event does
|
|
12
|
+
/// not impose a universal replacement policy: a type may replace, accumulate,
|
|
13
|
+
/// preserve history, or define its own revocation convention.
|
|
14
|
+
abstract contract AnnotationEvent is EventEmitter {
|
|
15
|
+
string private constant ABI = "event Annotation(uint indexed entity, bytes data)";
|
|
16
|
+
|
|
17
|
+
/// @param entity Entity being annotated.
|
|
18
|
+
/// @param data Encoded annotation block stream, conventionally containing one block.
|
|
19
|
+
event Annotation(uint indexed entity, bytes data);
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
emit EventAbi(ABI);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/events/Endpoint.sol
CHANGED
|
@@ -6,12 +6,12 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
6
6
|
/// @title EndpointEvent
|
|
7
7
|
/// @notice Emitted during host deployment to publish a callable endpoint descriptor.
|
|
8
8
|
abstract contract EndpointEvent is EventEmitter {
|
|
9
|
-
string private constant ABI = "event Endpoint(uint indexed host, uint id,
|
|
9
|
+
string private constant ABI = "event Endpoint(uint indexed host, uint id, uint descriptor)";
|
|
10
10
|
|
|
11
11
|
/// @param host Host node ID that exposes the endpoint.
|
|
12
12
|
/// @param id Endpoint node ID.
|
|
13
13
|
/// @param descriptor Packed endpoint lane metadata and flags.
|
|
14
|
-
event Endpoint(uint indexed host, uint id,
|
|
14
|
+
event Endpoint(uint indexed host, uint id, uint descriptor);
|
|
15
15
|
|
|
16
16
|
constructor() {
|
|
17
17
|
emit EventAbi(ABI);
|
package/events/Guardian.sol
CHANGED
|
@@ -3,12 +3,12 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { EventEmitter } from "./Emitter.sol";
|
|
5
5
|
|
|
6
|
-
/// @notice Emitted when a
|
|
6
|
+
/// @notice Emitted when a user account's guardian role changes on a host.
|
|
7
7
|
abstract contract GuardianEvent is EventEmitter {
|
|
8
8
|
string private constant ABI = "event Guardian(uint indexed host, bytes32 account, bool active)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID where the guardian change occurred.
|
|
11
|
-
/// @param account
|
|
11
|
+
/// @param account User account ID assigned or removed as a guardian.
|
|
12
12
|
/// @param active True if the guardian is enabled, false if revoked.
|
|
13
13
|
event Guardian(uint indexed host, bytes32 account, bool active);
|
|
14
14
|
|
package/events/Introduction.sol
CHANGED
|
@@ -5,12 +5,13 @@ 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 peer, uint blocknum)";
|
|
8
|
+
string private constant ABI = "event Introduction(uint indexed host, uint peer, bytes32 origin, uint blocknum)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID receiving the introduction.
|
|
11
11
|
/// @param peer Host node ID of the introducing contract.
|
|
12
|
+
/// @param origin Transaction-origin address encoded as a chain-agnostic user account.
|
|
12
13
|
/// @param blocknum Block number at which the host was deployed.
|
|
13
|
-
event Introduction(uint indexed host, uint peer, uint blocknum);
|
|
14
|
+
event Introduction(uint indexed host, uint peer, bytes32 origin, uint blocknum);
|
|
14
15
|
|
|
15
16
|
constructor() {
|
|
16
17
|
emit EventAbi(ABI);
|