@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.
Files changed (86) hide show
  1. package/CHANGELOG.md +87 -3
  2. package/Codec.sol +21 -0
  3. package/Commands.sol +14 -0
  4. package/Core.sol +8 -6
  5. package/Endpoints.sol +2 -7
  6. package/Events.sol +1 -2
  7. package/README.md +70 -37
  8. package/Utils.sol +2 -4
  9. package/annotations/Action.sol +17 -0
  10. package/annotations/Label.sol +23 -0
  11. package/annotations/Schema.sol +53 -0
  12. package/codec/Blocks.sol +1643 -0
  13. package/codec/Buffers.sol +165 -0
  14. package/codec/Decoders.sol +570 -0
  15. package/codec/Descriptors.sol +124 -0
  16. package/{blocks → codec}/Keys.sol +10 -18
  17. package/codec/Readers.sol +114 -0
  18. package/{blocks → codec}/Schema.sol +43 -80
  19. package/codec/Specs.sol +240 -0
  20. package/codec/Writers.sol +485 -0
  21. package/commands/Allocate.sol +21 -20
  22. package/commands/Base.sol +83 -45
  23. package/commands/Burn.sol +23 -14
  24. package/commands/Credit.sol +21 -20
  25. package/commands/Debit.sol +25 -28
  26. package/commands/Deposit.sol +41 -36
  27. package/commands/Payout.sol +26 -16
  28. package/commands/Provision.sol +37 -38
  29. package/commands/Recover.sol +20 -19
  30. package/commands/Relay.sol +24 -22
  31. package/commands/Withdraw.sol +20 -19
  32. package/commands/admin/AllowAssets.sol +19 -16
  33. package/commands/admin/Allowance.sol +18 -13
  34. package/commands/admin/Annotate.sol +36 -0
  35. package/commands/admin/Appoint.sol +19 -16
  36. package/commands/admin/Authorize.sol +23 -14
  37. package/commands/admin/Base.sol +9 -2
  38. package/commands/admin/DenyAssets.sol +19 -16
  39. package/commands/admin/Dismiss.sol +19 -16
  40. package/commands/admin/Execute.sol +21 -19
  41. package/commands/admin/Unauthorize.sol +23 -14
  42. package/core/Access.sol +91 -49
  43. package/core/Calls.sol +30 -33
  44. package/core/Endpoint.sol +46 -143
  45. package/core/Host.sol +63 -21
  46. package/core/Pipeline.sol +16 -15
  47. package/core/Types.sol +1 -1
  48. package/docs/Schema.md +48 -31
  49. package/events/Annotation.sol +24 -0
  50. package/events/Endpoint.sol +2 -2
  51. package/events/Guardian.sol +2 -2
  52. package/events/Introduction.sol +3 -2
  53. package/execution/Budget.sol +40 -0
  54. package/execution/Execution.sol +1104 -0
  55. package/guards/Base.sol +11 -12
  56. package/guards/Revoke.sol +11 -9
  57. package/package.json +1 -1
  58. package/ports/AllowAssets.sol +12 -15
  59. package/ports/Allowance.sol +11 -9
  60. package/ports/Base.sol +35 -16
  61. package/ports/Credit.sol +11 -9
  62. package/ports/Debit.sol +11 -9
  63. package/ports/DenyAssets.sol +10 -13
  64. package/ports/Dispatch.sol +13 -15
  65. package/ports/Pipe.sol +14 -12
  66. package/ports/Redeem.sol +12 -9
  67. package/ports/Settle.sol +16 -10
  68. package/queries/Assets.sol +15 -15
  69. package/queries/Balances.sol +17 -17
  70. package/queries/Base.sol +26 -12
  71. package/utils/Accounts.sol +0 -23
  72. package/utils/Actions.sol +1 -0
  73. package/utils/Cursors.sol +367 -0
  74. package/utils/Lanes.sol +14 -0
  75. package/utils/Layout.sol +0 -2
  76. package/utils/Selectors.sol +2 -2
  77. package/utils/Utils.sol +46 -0
  78. package/Cursors.sol +0 -16
  79. package/blocks/Cursors.sol +0 -1529
  80. package/blocks/Writers.sol +0 -1036
  81. package/commands/admin/Label.sol +0 -32
  82. package/commands/admin/Schemas.sol +0 -32
  83. package/core/Payable.sol +0 -53
  84. package/events/Labeled.sol +0 -21
  85. package/events/Schema.sol +0 -23
  86. 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 {Cursors, Cur} from "../Cursors.sol";
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 {LabeledEvent} from "../events/Labeled.sol";
8
- import {SchemaEvent} from "../events/Schema.sol";
9
- import {Runtime} from "./Runtime.sol";
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 Runtime, EndpointEvent, LabeledEvent, SchemaEvent {
25
- /// @dev Pack endpoint lanes and flags into a descriptor.
26
- /// A non-empty lane with group 0 defaults to group 1; a zero lane is absent.
27
- /// Layout: `[state:8][group:1][input:8][group:1][output:8][group:1]`
28
- /// `[flags:1][reserved:4]`. Flag bits: funded = 0, admin = 1.
29
- /// @param state Packed state lane plus optional group byte.
30
- /// @param input Packed input lane plus optional group byte.
31
- /// @param output Packed output lane plus optional group byte.
32
- /// @param funded Whether the endpoint accepts nonzero native value.
33
- /// @param admin Whether the endpoint is restricted to the admin account.
34
- /// @return value Packed endpoint descriptor as an integer.
35
- function pack(
36
- bytes9 state,
37
- bytes9 input,
38
- bytes9 output,
39
- bool funded,
40
- bool admin
41
- ) private pure returns (uint value) {
42
- value |= uint(uint72(state)) << Lane.State;
43
- value |= uint(uint72(input)) << Lane.Input;
44
- value |= uint(uint72(output)) << Lane.Output;
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 Open an endpoint state stream and return the expected output block count.
83
- /// @param source State block stream to open.
84
- /// @param descriptor Packed endpoint descriptor.
85
- /// @return state Cursor scoped to the state lane's first block run.
86
- /// @return outputs Number of output blocks implied by the state group count.
87
- function openState(
88
- bytes calldata source,
89
- bytes32 descriptor
90
- ) internal pure returns (Cur memory state, uint outputs) {
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 and return the expected output block count.
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
- /// @return input Cursor scoped to the input lane's first block run.
98
- /// @return outputs Number of output blocks implied by the input group count.
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
- bytes32 descriptor
102
- ) internal pure returns (Cur memory input, uint outputs) {
103
- (input, , outputs) = openLane(source, descriptor, Lane.Input, 0);
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 Append an explicit group size to an 8-byte lane value.
114
- /// @param value Packed lane key `[key][item]`.
115
- /// @param size Explicit per-operation group size for the lane.
116
- /// @return Packed lane key plus group byte.
117
- function group(bytes8 value, uint8 size) internal pure returns (bytes9) {
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 {AccessControl} from "./Access.sol";
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
- Authorize,
32
- Unauthorize,
33
- Revoke,
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; passed to `AccessControl`.
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) AccessControl(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 Introduce this host to the contract address embedded in a local EVM node ID.
50
- /// @dev Accepts host and endpoint IDs such as commands, ports, queries, and guards.
51
- /// Reverts when `node` is not local or embeds the zero address.
52
- /// @param node Local EVM node ID whose underlying contract receives the introduction.
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 {Cursors, Cur, Readers, Reader} from "../Cursors.sol";
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 "../utils/Value.sol";
6
+ import {Budget, Budgets} from "../execution/Budget.sol";
8
7
 
9
- using Cursors for Cur;
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 Payable, Settlement {
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 target Node ID to invoke or handle.
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 request Step request block stream.
25
+ /// @param input Step input block stream.
26
26
  /// @param value Native EVM value assigned to this step.
27
- /// @return nextState Updated state block stream for the next step.
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 target,
30
+ uint cmd,
31
31
  bytes32 account,
32
32
  bytes memory state,
33
- bytes calldata request,
33
+ bytes calldata input,
34
34
  uint128 value
35
- ) internal virtual returns (bytes memory nextState, bytes memory transactions);
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
- (Cur memory input, ) = Cursors.init(steps, 1);
45
+ Cur memory cur = Decoders.open(steps, 1);
46
46
 
47
- while (input.i < input.len) {
48
- (uint target, uint resources, bytes calldata request) = input.unpackStep();
47
+ while (cur.more()) {
48
+ (uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
49
49
  Reader memory txs;
50
- (state, txs.source) = dispatch(target, account, state, request, useValue(budget, resources));
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 requests, responses, and reporting.
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 request and response data is encoded as a stream of typed blocks. A
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 with:
30
+ used. A host can publish the meaning of a custom key as an annotation:
31
31
 
32
32
  ```solidity
33
- event Schema(uint indexed host, bytes4 key, string schema, bytes32 name);
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 request }
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 request }
76
- { bytes4 key, #string as body, bytes32 name }
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 `bytes9` lane values, with plain
112
- `bytes4` keys and the `bytes8` values returned by `many(item)` widening
113
- implicitly. A plain key or `many(item)` stores a zero group byte that readers
114
- interpret as group size 1, while `bytes9(0)` or `Keys.Empty` means the endpoint
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 stores each lane key as an 8-byte value:
127
+ The packed descriptor uses these lane layouts:
119
128
 
120
129
  ```txt
121
- [key bytes4][item bytes4]
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
- A plain block key is widened into `[key][0]`, so normal endpoint declarations can
125
- pass standard `bytes4` keys directly. A lane with a nonzero `item` describes a
126
- generic container block: `key` is the top-level wire key and `item` is the
127
- contained item key. The built-in `many(item)` helper creates `[Keys.List][item]`
128
- with the default group size 1, matching the DSL form `many #item`.
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 `Schema` events. Field aliases are presentation
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 key and publish
303
- that key with a `Schema` event. Endpoint contracts can use `schema(...)` for
304
- that publication:
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
- bytes4 input = schema(1, "{ bytes32 asset, uint amount }");
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 alias names the block; the schema string describes
313
- only the payload body.
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/blocks/Schema.sol`:
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 target, uint resources, #bytes as request }
325
- context { bytes32 account, #bytes as state, #bytes as request }
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
- auth { uint cid, uint deadline, #bytes as proof }
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
+ }
@@ -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, bytes32 descriptor)";
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, bytes32 descriptor);
14
+ event Endpoint(uint indexed host, uint id, uint descriptor);
15
15
 
16
16
  constructor() {
17
17
  emit EventAbi(ABI);
@@ -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 guardian account status changes on a host.
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 Guardian account ID.
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
 
@@ -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);