@rootzero/contracts 0.9.5 → 0.9.7

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 (47) hide show
  1. package/{Commands.sol → Endpoints.sol} +24 -9
  2. package/Events.sol +8 -7
  3. package/README.md +2 -3
  4. package/blocks/Cursors.sol +10 -14
  5. package/blocks/Keys.sol +3 -3
  6. package/blocks/Schema.sol +4 -4
  7. package/blocks/Writers.sol +17 -20
  8. package/commands/Base.sol +1 -9
  9. package/commands/admin/Appoint.sol +35 -0
  10. package/commands/admin/Authorize.sol +1 -1
  11. package/commands/admin/Dismiss.sol +35 -0
  12. package/commands/admin/Unauthorize.sol +1 -1
  13. package/core/Access.sol +35 -27
  14. package/core/Host.sol +6 -3
  15. package/docs/Schema.md +2 -2
  16. package/events/Admin.sol +3 -3
  17. package/events/Asset.sol +8 -7
  18. package/events/Balance.sol +2 -2
  19. package/events/Collateral.sol +2 -2
  20. package/events/Command.sol +3 -3
  21. package/events/Contexts.sol +12 -0
  22. package/events/Debt.sol +2 -2
  23. package/events/Guard.sol +19 -0
  24. package/events/Guardian.sol +18 -0
  25. package/events/Introduction.sol +2 -2
  26. package/events/Node.sol +18 -0
  27. package/events/Peer.sol +13 -8
  28. package/events/Position.sol +4 -4
  29. package/events/Query.sol +2 -2
  30. package/events/Received.sol +20 -0
  31. package/events/Rooted.sol +2 -2
  32. package/events/Spent.sol +20 -0
  33. package/guards/Base.sol +35 -0
  34. package/guards/Revoke.sol +31 -0
  35. package/package.json +1 -1
  36. package/peer/Pipe.sol +8 -8
  37. package/queries/Assets.sol +16 -16
  38. package/utils/Accounts.sol +49 -2
  39. package/utils/Assets.sol +2 -0
  40. package/utils/Ids.sol +57 -18
  41. package/utils/Layout.sol +6 -2
  42. package/Queries.sol +0 -10
  43. package/events/Access.sol +0 -21
  44. package/events/Deposit.sol +0 -22
  45. package/events/Listing.sol +0 -22
  46. package/events/Swap.sol +0 -20
  47. package/events/Withdraw.sol +0 -22
@@ -1,12 +1,15 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- // Aggregator: re-exports command, admin, and peer abstractions.
5
- // Import this file to inherit from the full rootzero command surface without managing individual paths.
4
+ // Aggregator: re-exports command, admin, peer, guard, and query endpoint abstractions.
5
+ // Import this file to inherit from the full rootzero callable host surface without managing individual paths.
6
6
 
7
+ // Shared helpers
8
+ import { Keys } from "./blocks/Keys.sol";
7
9
  import { CommandBase, CommandContext } from "./commands/Base.sol";
8
10
  import { Payable } from "./core/Payable.sol";
9
- import { Keys } from "./blocks/Keys.sol";
11
+
12
+ // Commands
10
13
  import { Burn, BurnHook } from "./commands/Burn.sol";
11
14
  import { CreditAccount, CreditAccountHook } from "./commands/Credit.sol";
12
15
  import { DebitAccount, DebitAccountHook } from "./commands/Debit.sol";
@@ -14,22 +17,34 @@ import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./comm
14
17
  import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
15
18
  import { Transfer, TransferHook } from "./commands/Transfer.sol";
16
19
  import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
20
+
21
+ // Admin commands
17
22
  import { AllowAssets, AllowAssetsHook } from "./commands/admin/AllowAssets.sol";
18
- import { Destroy, DestroyHook } from "./commands/admin/Destroy.sol";
19
- import { ExecutePayable } from "./commands/admin/Execute.sol";
23
+ import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
24
+ import { Appoint } from "./commands/admin/Appoint.sol";
20
25
  import { Authorize } from "./commands/admin/Authorize.sol";
26
+ import { Destroy, DestroyHook } from "./commands/admin/Destroy.sol";
21
27
  import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
28
+ import { Dismiss } from "./commands/admin/Dismiss.sol";
29
+ import { ExecutePayable } from "./commands/admin/Execute.sol";
22
30
  import { Init, InitHook } from "./commands/admin/Init.sol";
23
- import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
24
31
  import { Unauthorize } from "./commands/admin/Unauthorize.sol";
32
+
33
+ // Peer endpoints
25
34
  import { PeerBase, encodePeerCall } from "./peer/Base.sol";
35
+ import { PeerAllowAssets } from "./peer/AllowAssets.sol";
26
36
  import { PeerAllowance } from "./peer/Allowance.sol";
27
37
  import { PeerBalancePull, BalancePullHook } from "./peer/BalancePull.sol";
28
- import { PeerAllowAssets } from "./peer/AllowAssets.sol";
29
38
  import { PeerDenyAssets } from "./peer/DenyAssets.sol";
30
39
  import { PeerPipePayable } from "./peer/Pipe.sol";
31
40
  import { PeerSettle } from "./peer/Settle.sol";
32
41
 
42
+ // Guard endpoints
43
+ import { GuardBase, encodeGuardCall } from "./guards/Base.sol";
44
+ import { Revoke } from "./guards/Revoke.sol";
33
45
 
34
-
35
-
46
+ // Query endpoints
47
+ import { QueryBase, encodeQueryCall } from "./queries/Base.sol";
48
+ import { AssetStatus, AssetStatusHook } from "./queries/Assets.sol";
49
+ import { GetBalances, GetBalancesHook } from "./queries/Balances.sol";
50
+ import { GetPosition, GetPositionHook } from "./queries/Positions.sol";
package/Events.sol CHANGED
@@ -4,23 +4,24 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports all event contracts.
5
5
  // Import this file to get access to every event emitter in one import.
6
6
 
7
- import { AccessEvent } from "./events/Access.sol";
8
7
  import { AdminEvent } from "./events/Admin.sol";
9
- import { AssetEvent } from "./events/Asset.sol";
8
+ import { AssetStatusEvent } from "./events/Asset.sol";
10
9
  import { BalanceEvent } from "./events/Balance.sol";
11
10
  import { CollateralEvent } from "./events/Collateral.sol";
12
11
  import { CommandEvent } from "./events/Command.sol";
12
+ import { Contexts } from "./events/Contexts.sol";
13
13
  import { DebtEvent } from "./events/Debt.sol";
14
- import { DepositEvent } from "./events/Deposit.sol";
15
- import { AssetPositionEvent } from "./events/Position.sol";
14
+ import { PositionEvent } from "./events/Position.sol";
15
+ import { ReceivedEvent } from "./events/Received.sol";
16
16
  import { EventEmitter } from "./events/Emitter.sol";
17
+ import { GuardEvent } from "./events/Guard.sol";
18
+ import { GuardianEvent } from "./events/Guardian.sol";
17
19
  import { IntroductionEvent } from "./events/Introduction.sol";
18
- import { ListingEvent } from "./events/Listing.sol";
20
+ import { NodeEvent } from "./events/Node.sol";
19
21
  import { PeerEvent } from "./events/Peer.sol";
20
22
  import { QueryEvent } from "./events/Query.sol";
21
23
  import { RootedEvent } from "./events/Rooted.sol";
22
- import { SwapEvent } from "./events/Swap.sol";
23
- import { WithdrawalEvent } from "./events/Withdraw.sol";
24
+ import { SpentEvent } from "./events/Spent.sol";
24
25
 
25
26
 
26
27
 
package/README.md CHANGED
@@ -9,8 +9,7 @@ It contains the reusable contracts, utilities, cursor parsers, and encoding help
9
9
  Most consumers should start from the package root entry points:
10
10
 
11
11
  - `@rootzero/contracts/Core.sol` — host, access control, balances, and validator building blocks
12
- - `@rootzero/contracts/Commands.sol` — command and peer base contracts plus all standard command mixins
13
- - `@rootzero/contracts/Queries.sol` — query base contracts plus standard query mixins
12
+ - `@rootzero/contracts/Endpoints.sol` — command, peer, guard, and query base contracts plus standard endpoint mixins
14
13
  - `@rootzero/contracts/Cursors.sol` — cursor reader (`Cur`), block schemas, key constants, typed block helpers, and writers
15
14
  - `@rootzero/contracts/Utils.sol` — IDs, assets, accounts, layout, and value helpers
16
15
  - `@rootzero/contracts/Events.sol` — reusable event emitters and event contracts
@@ -66,7 +65,7 @@ Extend `CommandBase` to define a command mixin that runs inside the protocol's t
66
65
  // SPDX-License-Identifier: GPL-3.0-only
67
66
  pragma solidity ^0.8.33;
68
67
 
69
- import { CommandBase, CommandContext, Keys } from "@rootzero/contracts/Commands.sol";
68
+ import { CommandBase, CommandContext, Keys } from "@rootzero/contracts/Endpoints.sol";
70
69
  import { Cursors, Cur, Schemas, Writer, Writers } from "@rootzero/contracts/Cursors.sol";
71
70
 
72
71
  using Cursors for Cur;
@@ -531,21 +531,19 @@ library Cursors {
531
531
  return createBlock(Keys.Context, bytes.concat(account, toBytesBlock(state), toBytesBlock(request)));
532
532
  }
533
533
 
534
- /// @notice Encode a RELAY block.
535
- /// @param target Command target identifier.
536
- /// @param value Native value assigned to the relay.
534
+ /// @notice Encode a PIPE block.
535
+ /// @param value Native value assigned to the pipe.
537
536
  /// @param account Command account identifier.
538
537
  /// @param state Embedded state block stream.
539
538
  /// @param request Embedded request block stream.
540
- /// @return Encoded RELAY block bytes.
541
- function toRelayBlock(
542
- uint target,
539
+ /// @return Encoded PIPE block bytes.
540
+ function toPipeBlock(
543
541
  uint value,
544
542
  bytes32 account,
545
543
  bytes memory state,
546
544
  bytes memory request
547
545
  ) internal pure returns (bytes memory) {
548
- return createBlock(Keys.Relay, bytes.concat(bytes32(target), bytes32(value), toContextBlock(account, state, request)));
546
+ return createBlock(Keys.Pipe, bytes.concat(bytes32(value), toContextBlock(account, state, request)));
549
547
  }
550
548
 
551
549
  // -------------------------------------------------------------------------
@@ -1112,18 +1110,16 @@ library Cursors {
1112
1110
  cur.exit(end);
1113
1111
  }
1114
1112
 
1115
- /// @notice Consume a RELAY block and return its target, value, and context fields.
1113
+ /// @notice Consume a PIPE block and return its value and context fields.
1116
1114
  /// @param cur Cursor; advanced past the block.
1117
- /// @return target Destination command/node ID.
1118
- /// @return value Native value assigned to the relay.
1115
+ /// @return value Native value assigned to the pipe.
1119
1116
  /// @return account Command account identifier.
1120
1117
  /// @return state Embedded state block stream.
1121
1118
  /// @return request Embedded request block stream.
1122
- function unpackRelay(
1119
+ function unpackPipe(
1123
1120
  Cur memory cur
1124
- ) internal pure returns (uint target, uint value, bytes32 account, bytes calldata state, bytes calldata request) {
1125
- uint end = cur.enter(Keys.Relay, 64 + Sizes.Header + 32 + 2 * Sizes.Header, 0);
1126
- target = uint(cur.read32());
1121
+ ) internal pure returns (uint value, bytes32 account, bytes calldata state, bytes calldata request) {
1122
+ uint end = cur.enter(Keys.Pipe, 32 + Sizes.Header + 32 + 2 * Sizes.Header, 0);
1127
1123
  value = uint(cur.read32());
1128
1124
  (account, state, request) = cur.unpackContext();
1129
1125
  cur.exit(end);
package/blocks/Keys.sol CHANGED
@@ -43,8 +43,8 @@ library Keys {
43
43
  bytes4 constant Call = bytes4(keccak256("#call"));
44
44
  /// @dev Command context transport - (bytes32 account, #bytes as state, #bytes as request)
45
45
  bytes4 constant Context = bytes4(keccak256("#context"));
46
- /// @dev Pipeline relay - (uint target, uint value, #context)
47
- bytes4 constant Relay = bytes4(keccak256("#relay"));
46
+ /// @dev Pipeline invocation - (uint value, #context)
47
+ bytes4 constant Pipe = bytes4(keccak256("#pipe"));
48
48
  /// @dev Authentication proof - (uint cid, uint deadline, #bytes as proof); must appear last in its segment
49
49
  bytes4 constant Auth = bytes4(keccak256("#auth"));
50
50
  /// @dev Asset descriptor without amount - (bytes32 asset, bytes32 meta)
@@ -54,7 +54,7 @@ library Keys {
54
54
  /// @dev Relayer bounty - (uint amount, bytes32 relayer)
55
55
  bytes4 constant Bounty = bytes4(keccak256("#bounty"));
56
56
 
57
- /// @dev Structural status form - (bool ok)
57
+ /// @dev Structural status form - (uint code)
58
58
  bytes4 constant Status = bytes4(keccak256("#status"));
59
59
  /// @dev Structural asset amount form - (bytes32 asset, bytes32 meta, uint amount)
60
60
  bytes4 constant AssetAmount = bytes4(keccak256("#assetAmount"));
package/blocks/Schema.sol CHANGED
@@ -60,7 +60,7 @@ library Schemas {
60
60
  string constant Allowance = "#allowance { uint host, bytes32 asset, bytes32 meta, uint amount }";
61
61
  string constant Transaction = "#transaction { bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount }";
62
62
  string constant Context = "#context { bytes32 account, #bytes as state, #bytes as request }";
63
- string constant Relay = "#relay { uint target, uint value, #context { bytes32 account, #bytes as state, #bytes as request } }";
63
+ string constant Pipe = "#pipe { uint value, #context { bytes32 account, #bytes as state, #bytes as request } }";
64
64
  string constant Call = "#call { uint target, uint value, #bytes as payload }";
65
65
  string constant Step = "#step { uint target, uint value, #bytes as request }";
66
66
  string constant Bounty = "#bounty { uint amount, bytes32 relayer }";
@@ -76,7 +76,7 @@ library Schemas {
76
76
  /// @notice Reusable structural block schemas for core tuple shapes.
77
77
  /// These describe payload form without assigning command or query semantics.
78
78
  library Forms {
79
- string constant Status = "#status { bool ok }";
79
+ string constant Status = "#status { uint code }";
80
80
  string constant AssetAmount = "#assetAmount { bytes32 asset, bytes32 meta, uint amount }";
81
81
  string constant AccountAsset = "#accountAsset { bytes32 account, bytes32 asset, bytes32 meta }";
82
82
  string constant AccountAmount = "#accountAmount { bytes32 account, bytes32 asset, bytes32 meta, uint amount }";
@@ -107,8 +107,8 @@ library Sizes {
107
107
  uint constant Proof = 85;
108
108
  /// @dev AUTH block: 8 header + 32 cid + 32 deadline + nested BYTES block with 85-byte proof = 165 bytes
109
109
  uint constant Auth = B64 + Header + Proof;
110
- /// @dev STATUS block: 8 header + 1 bool byte = 9 bytes
111
- uint constant Status = Header + 1;
110
+ /// @dev STATUS block: 8 header + 32 status code = 40 bytes
111
+ uint constant Status = B32;
112
112
  /// @dev AMOUNT block: 8 header + 32 asset + 32 meta + 32 amount = 104 bytes
113
113
  uint constant Amount = B96;
114
114
  /// @dev BALANCE block: 8 header + 32 asset + 32 meta + 32 amount = 104 bytes
@@ -27,7 +27,7 @@ library Hints {
27
27
  uint constant Step = 256;
28
28
  uint constant Call = 256;
29
29
  uint constant Context = 512;
30
- uint constant Relay = 640;
30
+ uint constant Pipe = 608;
31
31
  }
32
32
 
33
33
  /// @title Writers
@@ -224,12 +224,12 @@ library Writers {
224
224
  return allocFromHint(count, Hints.Context);
225
225
  }
226
226
 
227
- /// @notice Allocate a writer for `count` RELAY blocks using a per-block capacity hint.
228
- /// @dev The backing buffer expands automatically if encoded relays exceed the initial hint.
229
- /// @param count Number of relay blocks to allocate space for.
227
+ /// @notice Allocate a writer for `count` PIPE blocks using a per-block capacity hint.
228
+ /// @dev The backing buffer expands automatically if encoded pipes exceed the initial hint.
229
+ /// @param count Number of pipe blocks to allocate space for.
230
230
  /// @return writer Allocated growable writer.
231
- function allocRelays(uint count) internal pure returns (Writer memory writer) {
232
- return allocFromHint(count, Hints.Relay);
231
+ function allocPipes(uint count) internal pure returns (Writer memory writer) {
232
+ return allocFromHint(count, Hints.Pipe);
233
233
  }
234
234
 
235
235
  // -------------------------------------------------------------------------
@@ -889,40 +889,37 @@ library Writers {
889
889
  appendBlock32BytesBytes(writer, Keys.Context, account, state, request);
890
890
  }
891
891
 
892
- /// @notice Append a RELAY block with a nested CONTEXT block.
893
- /// @param writer Destination writer; `i` is advanced by the encoded RELAY block length.
894
- /// @param target Command target identifier.
895
- /// @param value Native value assigned to the relay.
892
+ /// @notice Append a PIPE block with a nested CONTEXT block.
893
+ /// @param writer Destination writer; `i` is advanced by the encoded PIPE block length.
894
+ /// @param value Native value assigned to the pipe.
896
895
  /// @param account Command account identifier.
897
896
  /// @param state Raw nested state payload.
898
897
  /// @param request Raw nested request payload.
899
- function appendRelay(
898
+ function appendPipe(
900
899
  Writer memory writer,
901
- uint target,
902
900
  uint value,
903
901
  bytes32 account,
904
902
  bytes memory state,
905
903
  bytes memory request
906
904
  ) internal pure {
907
905
  uint i = writer.i;
908
- uint len = 96 + 3 * Sizes.Header + state.length + request.length;
906
+ uint len = 64 + 3 * Sizes.Header + state.length + request.length;
909
907
  uint next = i + Sizes.Header + len;
910
908
  i = reserve(writer, next, next);
911
909
 
912
- uint p = writeHeader(writer.dst, i, Keys.Relay, uint32(max32(len)));
910
+ uint p = writeHeader(writer.dst, i, Keys.Pipe, uint32(max32(len)));
913
911
  assembly ("memory-safe") {
914
- mstore(add(p, 0x08), target)
915
- mstore(add(p, 0x28), value)
912
+ mstore(add(p, 0x08), value)
916
913
  }
917
914
 
918
- writeBlock32BytesBytes(writer.dst, i + Sizes.Header + 64, Keys.Context, account, state, request);
915
+ writeBlock32BytesBytes(writer.dst, i + Sizes.Header + 32, Keys.Context, account, state, request);
919
916
  }
920
917
 
921
918
  /// @notice Append a STATUS form block.
922
919
  /// @param writer Destination writer; `i` is advanced by `Sizes.Status`.
923
- /// @param ok Status value to encode.
924
- function appendStatus(Writer memory writer, bool ok) internal pure {
925
- appendBlock32(writer, Keys.Status, ok ? bytes32(uint(1) << 248) : bytes32(0), 1);
920
+ /// @param code Status code to encode.
921
+ function appendStatus(Writer memory writer, uint code) internal pure {
922
+ appendBlock32(writer, Keys.Status, bytes32(code), 32);
926
923
  }
927
924
 
928
925
  /// @notice Append a BALANCE block using separate field values.
package/commands/Base.sol CHANGED
@@ -23,12 +23,10 @@ struct CommandContext {
23
23
  abstract contract CommandBase is NodeCalls, CommandEvent {
24
24
  /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
25
25
  error Expired();
26
- /// @dev Thrown when `onlyAdmin` finds that `account` is not the admin account.
27
- error NotAdmin();
28
26
 
29
27
  /// @dev Restrict execution to the commander using the host's admin account.
30
28
  modifier onlyAdmin(bytes32 account) {
31
- if (account != adminAccount) revert NotAdmin();
29
+ if (account != admin) revert AccessDenied();
32
30
  enforceCommander(msg.sender);
33
31
  _;
34
32
  }
@@ -39,12 +37,6 @@ abstract contract CommandBase is NodeCalls, CommandEvent {
39
37
  _;
40
38
  }
41
39
 
42
- /// @dev Restrict execution to callers whose host node is trusted.
43
- modifier onlyTrusted() {
44
- enforceCaller(msg.sender);
45
- _;
46
- }
47
-
48
40
  /// @dev Restrict execution to invocations where `deadline` is in the future.
49
41
  /// @param deadline Unix timestamp after which the invocation is considered expired.
50
42
  modifier onlyActive(uint deadline) {
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
+ import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
7
+ using Cursors for Cur;
8
+
9
+ /// @title Appoint
10
+ /// @notice Admin command that grants guardian status to a list of account IDs.
11
+ /// Each ACCOUNT block in the request is enabled as a guardian on the host.
12
+ /// Only callable by the admin account.
13
+ abstract contract Appoint is CommandBase, AdminEvent {
14
+ string private constant NAME = "appoint";
15
+
16
+ uint internal immutable appointId = commandId(NAME);
17
+
18
+ constructor() {
19
+ emit Admin(host, appointId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
20
+ }
21
+
22
+ function appoint(
23
+ CommandContext calldata c
24
+ ) external onlyAdmin(c.account) returns (bytes memory) {
25
+ (Cur memory request, ) = cursor(c.request, 1);
26
+
27
+ while (request.i < request.bound) {
28
+ bytes32 account = request.unpackAccount();
29
+ setGuardian(account, true);
30
+ }
31
+
32
+ request.close();
33
+ return "";
34
+ }
35
+ }
@@ -26,7 +26,7 @@ abstract contract Authorize is CommandBase, AdminEvent {
26
26
 
27
27
  while (request.i < request.bound) {
28
28
  uint node = request.unpackNode();
29
- authorize(node);
29
+ setNode(node, true);
30
30
  }
31
31
 
32
32
  request.close();
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
+ import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
7
+ using Cursors for Cur;
8
+
9
+ /// @title Dismiss
10
+ /// @notice Admin command that revokes guardian status from a list of account IDs.
11
+ /// Each ACCOUNT block in the request is disabled as a guardian on the host.
12
+ /// Only callable by the admin account.
13
+ abstract contract Dismiss is CommandBase, AdminEvent {
14
+ string private constant NAME = "dismiss";
15
+
16
+ uint internal immutable dismissId = commandId(NAME);
17
+
18
+ constructor() {
19
+ emit Admin(host, dismissId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
20
+ }
21
+
22
+ function dismiss(
23
+ CommandContext calldata c
24
+ ) external onlyAdmin(c.account) returns (bytes memory) {
25
+ (Cur memory request, ) = cursor(c.request, 1);
26
+
27
+ while (request.i < request.bound) {
28
+ bytes32 account = request.unpackAccount();
29
+ setGuardian(account, false);
30
+ }
31
+
32
+ request.close();
33
+ return "";
34
+ }
35
+ }
@@ -26,7 +26,7 @@ abstract contract Unauthorize is CommandBase, AdminEvent {
26
26
 
27
27
  while (request.i < request.bound) {
28
28
  uint node = request.unpackNode();
29
- unauthorize(node);
29
+ setNode(node, false);
30
30
  }
31
31
 
32
32
  request.close();
package/core/Access.sol CHANGED
@@ -1,7 +1,8 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AccessEvent} from "../events/Access.sol";
4
+ import {NodeEvent} from "../events/Node.sol";
5
+ import {GuardianEvent} from "../events/Guardian.sol";
5
6
  import {RootZeroContext} from "./Context.sol";
6
7
  import {Accounts} from "../utils/Accounts.sol";
7
8
  import {Ids} from "../utils/Ids.sol";
@@ -13,41 +14,48 @@ import {addrOr} from "../utils/Utils.sol";
13
14
  /// mapping of externally trusted node IDs. Inbound trust is host-based:
14
15
  /// trusted hosts, the commander, and this contract itself may interact
15
16
  /// with the host through the guarded command and peer entrypoints.
16
- abstract contract AccessControl is RootZeroContext, AccessEvent {
17
+ abstract contract AccessControl is RootZeroContext, NodeEvent, GuardianEvent {
17
18
  /// @dev Trusted commander address. All calls from this address are implicitly trusted.
18
19
  /// Defaults to `address(this)` when no external commander is provided.
19
20
  address internal immutable commander;
20
21
  /// @dev Admin account ID derived from the commander address at construction time.
21
- bytes32 internal immutable adminAccount;
22
+ bytes32 internal immutable admin;
22
23
 
23
- /// @dev Mapping from node ID to trust status.
24
- mapping(uint node => bool) internal trusted;
24
+ /// @dev Mapping from guardian account ID to guardian status.
25
+ mapping(bytes32 account => bool) internal guardians;
25
26
 
26
- /// @dev Thrown when `enforceCaller` is called by an address that is not trusted.
27
- error UnauthorizedCaller(address addr);
27
+ /// @dev Mapping from node ID to trust status.
28
+ mapping(uint node => bool) internal nodes;
28
29
 
29
- /// @dev Thrown when a required trusted node is missing from the trusted set.
30
- error UnauthorizedNode(uint node);
30
+ /// @dev Thrown when a caller, account, or node lacks required access.
31
+ error AccessDenied();
31
32
 
32
33
  constructor(address cmdr) {
33
34
  commander = addrOr(cmdr, address(this));
34
- adminAccount = Accounts.toAdmin(commander);
35
+ admin = Accounts.toAdmin(commander);
36
+ }
37
+
38
+ /// @notice Set authorization status for a node.
39
+ /// @param node Node ID to update.
40
+ /// @param active True to authorize the node, false to revoke it.
41
+ function setNode(uint node, bool active) internal {
42
+ nodes[node] = active;
43
+ emit Node(host, node, active);
35
44
  }
36
45
 
37
- /// @notice Grant authorization for a node.
38
- /// Accepts any node ID that should be trusted by this contract.
39
- /// @param node Node ID to authorize.
40
- function authorize(uint node) internal {
41
- trusted[node] = true;
42
- emit Access(host, node, true);
46
+ /// @notice Set guardian status for an account.
47
+ /// @param account Guardian account ID to update.
48
+ /// @param active True to enable the guardian, false to revoke it.
49
+ function setGuardian(bytes32 account, bool active) internal {
50
+ Accounts.guardian(account);
51
+ guardians[account] = active;
52
+ emit Guardian(host, account, active);
43
53
  }
44
54
 
45
- /// @notice Revoke authorization for a node.
46
- /// Accepts any node ID that should no longer be trusted by this contract.
47
- /// @param node Node ID to unauthorize.
48
- function unauthorize(uint node) internal {
49
- trusted[node] = false;
50
- emit Access(host, node, false);
55
+ /// @notice Return true if `addr` is an active guardian.
56
+ /// @param addr EVM address to check as a guardian account.
57
+ function isGuardian(address addr) internal view returns (bool) {
58
+ return guardians[Accounts.toGuardian(addr)];
51
59
  }
52
60
 
53
61
  /// @notice Return true if `caller` is an implicitly trusted address.
@@ -55,15 +63,15 @@ abstract contract AccessControl is RootZeroContext, AccessEvent {
55
63
  /// whose host ID has been explicitly authorized.
56
64
  /// @param caller Address to check.
57
65
  function isTrusted(address caller) internal view returns (bool) {
58
- return caller == commander || caller == address(this) || trusted[Ids.toHost(caller)];
66
+ return caller == commander || caller == address(this) || nodes[Ids.toHost(caller)];
59
67
  }
60
68
 
61
69
  /// @notice Assert that `node` is in the trusted set and return it.
62
70
  /// @param node Node ID to validate.
63
71
  /// @return The same `node` value if trusted.
64
72
  function ensureTrusted(uint node) internal view returns (uint) {
65
- if (node == 0 || !trusted[node]) {
66
- revert UnauthorizedNode(node);
73
+ if (node == 0 || !nodes[node]) {
74
+ revert AccessDenied();
67
75
  }
68
76
  return node;
69
77
  }
@@ -74,7 +82,7 @@ abstract contract AccessControl is RootZeroContext, AccessEvent {
74
82
  /// @return The same `caller` value if trusted.
75
83
  function enforceCaller(address caller) internal view returns (address) {
76
84
  if (caller == address(0) || !isTrusted(caller)) {
77
- revert UnauthorizedCaller(caller);
85
+ revert AccessDenied();
78
86
  }
79
87
  return caller;
80
88
  }
@@ -85,7 +93,7 @@ abstract contract AccessControl is RootZeroContext, AccessEvent {
85
93
  /// @return The same `caller` value if it is the commander.
86
94
  function enforceCommander(address caller) internal view returns (address) {
87
95
  if (caller == address(0) || caller != commander) {
88
- revert UnauthorizedCaller(caller);
96
+ revert AccessDenied();
89
97
  }
90
98
  return caller;
91
99
  }
package/core/Host.sol CHANGED
@@ -2,10 +2,13 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {AccessControl} from "./Access.sol";
5
+ import {Appoint} from "../commands/admin/Appoint.sol";
5
6
  import {Authorize} from "../commands/admin/Authorize.sol";
7
+ import {Dismiss} from "../commands/admin/Dismiss.sol";
6
8
  import {Unauthorize} from "../commands/admin/Unauthorize.sol";
7
9
  import {ExecutePayable} from "../commands/admin/Execute.sol";
8
10
  import {IntroductionEvent} from "../events/Introduction.sol";
11
+ import {Ids} from "../utils/Ids.sol";
9
12
 
10
13
  /// @title IHostIntroduction
11
14
  /// @notice Interface implemented by hosts that accept introductions from other hosts.
@@ -23,7 +26,7 @@ interface IHostIntroduction {
23
26
  /// Inherits admin command support (authorize, unauthorize, executePayable) and
24
27
  /// optionally introduces itself to a commander host at deployment.
25
28
  /// Accepts native ETH payments via the `receive` function.
26
- abstract contract Host is Authorize, Unauthorize, ExecutePayable, IntroductionEvent, IHostIntroduction {
29
+ abstract contract Host is Authorize, Unauthorize, Appoint, Dismiss, ExecutePayable, IntroductionEvent, IHostIntroduction {
27
30
  /// @param cmdr Commander address; passed to `AccessControl`.
28
31
  /// If `cmdr` is a deployed contract, the host calls `introduce`
29
32
  /// on it during construction.
@@ -35,13 +38,13 @@ abstract contract Host is Authorize, Unauthorize, ExecutePayable, IntroductionEv
35
38
  }
36
39
 
37
40
  /// @notice Record a host introduction claim.
38
- /// @dev This event is informational only; it does not authorize or trust the introduced host.
41
+ /// @dev Validates that `peer` matches `msg.sender`; it does not authorize or trust the introduced host.
39
42
  /// @param peer Host node ID being introduced.
40
43
  /// @param blocknum Block number at which the host was deployed.
41
44
  /// @param version Protocol version the host implements.
42
45
  /// @param namespace Human-readable namespace string for the host.
43
46
  function introduce(uint peer, uint blocknum, uint16 version, string calldata namespace) external {
44
- emit Introduction(peer, blocknum, version, namespace);
47
+ emit Introduction(Ids.matchHost(peer, msg.sender), blocknum, version, namespace);
45
48
  }
46
49
 
47
50
  /// @notice Accept native ETH transfers (e.g. from command value flows).
package/docs/Schema.md CHANGED
@@ -58,7 +58,7 @@ Once a child block appears, no more fixed fields may follow.
58
58
  ```txt
59
59
  #call { uint target, uint value, #bytes as payload }
60
60
  #context { bytes32 account, #bytes as state, #bytes as request }
61
- #relay { uint target, uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
61
+ #pipe { uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
62
62
  ```
63
63
 
64
64
  The tail is embedded directly as child block bytes. There is no wrapper around a
@@ -189,7 +189,7 @@ Common protocol schemas live in `contracts/blocks/Schema.sol`:
189
189
  #call { uint target, uint value, #bytes as payload }
190
190
  #step { uint target, uint value, #bytes as request }
191
191
  #context { bytes32 account, #bytes as state, #bytes as request }
192
- #relay { uint target, uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
192
+ #pipe { uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
193
193
  #auth { uint cid, uint deadline, #bytes as proof }
194
194
  ```
195
195
 
package/events/Admin.sol CHANGED
@@ -3,11 +3,11 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { EventEmitter } from "./Emitter.sol";
5
5
 
6
- string constant ABI =
7
- "event Admin(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool acceptsValue)";
8
-
9
6
  /// @notice Emitted once per admin command during host deployment to publish its request schema and state keys.
10
7
  abstract contract AdminEvent is EventEmitter {
8
+ string private constant ABI =
9
+ "event Admin(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool acceptsValue)";
10
+
11
11
  /// @param host Host node ID that owns this admin command.
12
12
  /// @param id Command node ID.
13
13
  /// @param name Human-readable command name.
package/events/Asset.sol CHANGED
@@ -3,14 +3,15 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { EventEmitter } from "./Emitter.sol";
5
5
 
6
- string constant ABI = "event Asset(uint indexed host, bytes32 name, uint32 prefix)";
6
+ /// @notice Emitted when an asset support status is updated on a host.
7
+ abstract contract AssetStatusEvent is EventEmitter {
8
+ string private constant ABI = "event AssetStatus(uint indexed host, bytes32 asset, bytes32 meta, uint status)";
7
9
 
8
- /// @notice Emitted when an asset is registered or updated on a host.
9
- abstract contract AssetEvent is EventEmitter {
10
- /// @param host Host node ID that registered the asset.
11
- /// @param name Asset name encoded as a bytes32 string.
12
- /// @param prefix 4-byte type prefix of the asset ID.
13
- event Asset(uint indexed host, bytes32 name, uint32 prefix);
10
+ /// @param host Host node ID that manages this listing.
11
+ /// @param asset Asset identifier.
12
+ /// @param meta Asset metadata slot.
13
+ /// @param status Asset support status. Zero means unsupported; nonzero means supported.
14
+ event AssetStatus(uint indexed host, bytes32 asset, bytes32 meta, uint status);
14
15
 
15
16
  constructor() {
16
17
  emit EventAbi(ABI);