@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.
- package/{Commands.sol → Endpoints.sol} +24 -9
- package/Events.sol +8 -7
- package/README.md +2 -3
- package/blocks/Cursors.sol +10 -14
- package/blocks/Keys.sol +3 -3
- package/blocks/Schema.sol +4 -4
- package/blocks/Writers.sol +17 -20
- package/commands/Base.sol +1 -9
- package/commands/admin/Appoint.sol +35 -0
- package/commands/admin/Authorize.sol +1 -1
- package/commands/admin/Dismiss.sol +35 -0
- package/commands/admin/Unauthorize.sol +1 -1
- package/core/Access.sol +35 -27
- package/core/Host.sol +6 -3
- package/docs/Schema.md +2 -2
- package/events/Admin.sol +3 -3
- package/events/Asset.sol +8 -7
- package/events/Balance.sol +2 -2
- package/events/Collateral.sol +2 -2
- package/events/Command.sol +3 -3
- package/events/Contexts.sol +12 -0
- package/events/Debt.sol +2 -2
- package/events/Guard.sol +19 -0
- package/events/Guardian.sol +18 -0
- package/events/Introduction.sol +2 -2
- package/events/Node.sol +18 -0
- package/events/Peer.sol +13 -8
- package/events/Position.sol +4 -4
- package/events/Query.sol +2 -2
- package/events/Received.sol +20 -0
- package/events/Rooted.sol +2 -2
- package/events/Spent.sol +20 -0
- package/guards/Base.sol +35 -0
- package/guards/Revoke.sol +31 -0
- package/package.json +1 -1
- package/peer/Pipe.sol +8 -8
- package/queries/Assets.sol +16 -16
- package/utils/Accounts.sol +49 -2
- package/utils/Assets.sol +2 -0
- package/utils/Ids.sol +57 -18
- package/utils/Layout.sol +6 -2
- package/Queries.sol +0 -10
- package/events/Access.sol +0 -21
- package/events/Deposit.sol +0 -22
- package/events/Listing.sol +0 -22
- package/events/Swap.sol +0 -20
- 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
|
|
5
|
-
// Import this file to inherit from the full rootzero
|
|
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
|
-
|
|
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 {
|
|
19
|
-
import {
|
|
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 {
|
|
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 {
|
|
15
|
-
import {
|
|
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 {
|
|
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 {
|
|
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/
|
|
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/
|
|
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;
|
package/blocks/Cursors.sol
CHANGED
|
@@ -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
|
|
535
|
-
/// @param
|
|
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
|
|
541
|
-
function
|
|
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.
|
|
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
|
|
1113
|
+
/// @notice Consume a PIPE block and return its value and context fields.
|
|
1116
1114
|
/// @param cur Cursor; advanced past the block.
|
|
1117
|
-
/// @return
|
|
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
|
|
1119
|
+
function unpackPipe(
|
|
1123
1120
|
Cur memory cur
|
|
1124
|
-
) internal pure returns (uint
|
|
1125
|
-
uint end = cur.enter(Keys.
|
|
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
|
|
47
|
-
bytes4 constant
|
|
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 - (
|
|
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
|
|
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 {
|
|
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 +
|
|
111
|
-
uint constant Status =
|
|
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
|
package/blocks/Writers.sol
CHANGED
|
@@ -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
|
|
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`
|
|
228
|
-
/// @dev The backing buffer expands automatically if encoded
|
|
229
|
-
/// @param count Number of
|
|
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
|
|
232
|
-
return allocFromHint(count, Hints.
|
|
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
|
|
893
|
-
/// @param writer Destination writer; `i` is advanced by the encoded
|
|
894
|
-
/// @param
|
|
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
|
|
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 =
|
|
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.
|
|
910
|
+
uint p = writeHeader(writer.dst, i, Keys.Pipe, uint32(max32(len)));
|
|
913
911
|
assembly ("memory-safe") {
|
|
914
|
-
mstore(add(p, 0x08),
|
|
915
|
-
mstore(add(p, 0x28), value)
|
|
912
|
+
mstore(add(p, 0x08), value)
|
|
916
913
|
}
|
|
917
914
|
|
|
918
|
-
writeBlock32BytesBytes(writer.dst, i + Sizes.Header +
|
|
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
|
|
924
|
-
function appendStatus(Writer memory writer,
|
|
925
|
-
appendBlock32(writer, Keys.Status,
|
|
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 !=
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|
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 {
|
|
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,
|
|
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
|
|
22
|
+
bytes32 internal immutable admin;
|
|
22
23
|
|
|
23
|
-
/// @dev Mapping from
|
|
24
|
-
mapping(
|
|
24
|
+
/// @dev Mapping from guardian account ID to guardian status.
|
|
25
|
+
mapping(bytes32 account => bool) internal guardians;
|
|
25
26
|
|
|
26
|
-
/// @dev
|
|
27
|
-
|
|
27
|
+
/// @dev Mapping from node ID to trust status.
|
|
28
|
+
mapping(uint node => bool) internal nodes;
|
|
28
29
|
|
|
29
|
-
/// @dev Thrown when a
|
|
30
|
-
error
|
|
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
|
-
|
|
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
|
|
38
|
-
///
|
|
39
|
-
/// @param
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
46
|
-
///
|
|
47
|
-
|
|
48
|
-
|
|
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) ||
|
|
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 || !
|
|
66
|
-
revert
|
|
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
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
/// @
|
|
9
|
-
|
|
10
|
-
/// @param
|
|
11
|
-
/// @param
|
|
12
|
-
|
|
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);
|