@rootzero/contracts 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/Core.sol +2 -1
- package/Endpoints.sol +2 -2
- package/Events.sol +2 -1
- package/README.md +14 -13
- package/blocks/Cursors.sol +61 -61
- package/blocks/Keys.sol +4 -4
- package/blocks/Schema.sol +8 -7
- package/blocks/Writers.sol +2 -2
- package/commands/Burn.sol +1 -1
- package/commands/Credit.sol +1 -1
- package/commands/Debit.sol +1 -1
- package/commands/Deposit.sol +2 -2
- package/commands/Payout.sol +2 -2
- package/commands/Provision.sol +2 -2
- package/commands/Recover.sol +21 -20
- package/commands/Relay.sol +11 -11
- package/commands/Withdraw.sol +1 -1
- package/commands/admin/AllowAssets.sol +1 -1
- package/commands/admin/Allowance.sol +1 -1
- package/commands/admin/Appoint.sol +1 -1
- package/commands/admin/Authorize.sol +1 -1
- package/commands/admin/DenyAssets.sol +1 -1
- package/commands/admin/Destroy.sol +1 -0
- package/commands/admin/Dismiss.sol +1 -1
- package/commands/admin/Execute.sol +2 -2
- package/commands/admin/Init.sol +1 -0
- package/commands/admin/Label.sol +1 -1
- package/commands/admin/Unauthorize.sol +1 -1
- package/core/Access.sol +1 -0
- package/core/Calls.sol +12 -1
- package/core/Payable.sol +8 -1
- package/core/Pipeline.sol +1 -1
- package/core/Portal.sol +39 -0
- package/docs/Schema.md +15 -11
- package/events/Dispatch.sol +5 -5
- package/events/Recovered.sol +17 -0
- package/events/Route.sol +5 -5
- package/events/Undelivered.sol +18 -0
- package/guards/Revoke.sol +1 -1
- package/package.json +1 -1
- package/ports/AllowAssets.sol +1 -1
- package/ports/Allowance.sol +1 -1
- package/ports/Credit.sol +1 -1
- package/ports/Debit.sol +1 -1
- package/ports/DenyAssets.sol +1 -1
- package/ports/Dispatch.sol +8 -8
- package/ports/Pipe.sol +1 -1
- package/ports/Redeem.sol +1 -1
- package/ports/Settle.sol +1 -1
- package/queries/Assets.sol +1 -1
- package/queries/Balances.sol +1 -1
- package/queries/Positions.sol +2 -1
- package/core/Commitments.sol +0 -19
- package/events/Commitment.sol +0 -19
package/commands/Recover.sol
CHANGED
|
@@ -8,37 +8,38 @@ import {Budget} from "../utils/Value.sol";
|
|
|
8
8
|
|
|
9
9
|
using Cursors for Cur;
|
|
10
10
|
|
|
11
|
-
abstract contract
|
|
12
|
-
/// @notice Override to recover
|
|
13
|
-
/// @param
|
|
14
|
-
/// @param key
|
|
15
|
-
/// @param
|
|
16
|
-
/// @param
|
|
17
|
-
|
|
18
|
-
function recoverContext(uint port, bytes32 key, uint resources, Cur memory context, Budget memory budget) internal virtual;
|
|
11
|
+
abstract contract RecoverHook {
|
|
12
|
+
/// @notice Override to recover a witness through `handler`.
|
|
13
|
+
/// @param handler Port that should attempt recovery.
|
|
14
|
+
/// @param key Recovery lookup key.
|
|
15
|
+
/// @param witness Witness payload used to prove and replay recovery.
|
|
16
|
+
/// @param value Native EVM value assigned to the recovery attempt.
|
|
17
|
+
function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
/// @title
|
|
22
|
-
/// @notice Command that forwards
|
|
20
|
+
/// @title RecoverPayable
|
|
21
|
+
/// @notice Command that forwards recover request blocks to a virtual hook.
|
|
22
|
+
/// Recovery is witness-driven: the command account pays and receives leftover
|
|
23
|
+
/// value settlement, but the recovered subject is defined by each witness.
|
|
23
24
|
/// Produces no output state.
|
|
24
|
-
abstract contract
|
|
25
|
-
uint internal immutable
|
|
25
|
+
abstract contract RecoverPayable is CommandBase, Payable, RecoverHook {
|
|
26
|
+
uint internal immutable recoverPayableId = commandId(this.recoverPayable.selector);
|
|
26
27
|
|
|
27
28
|
constructor() {
|
|
28
|
-
emit Command(host,
|
|
29
|
-
emit Labeled(
|
|
29
|
+
emit Command(host, recoverPayableId, "1:0:0", Schemas.Recover, Keys.Empty, Keys.Empty, true);
|
|
30
|
+
emit Labeled(recoverPayableId, bytes32(0), "recoverPayable");
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
/// @notice Recover each
|
|
33
|
-
/// @param c Command context; `c.request` must contain
|
|
33
|
+
/// @notice Recover each recover block in the command request.
|
|
34
|
+
/// @param c Command context; `c.request` must contain Recover blocks.
|
|
34
35
|
/// @return Empty output state.
|
|
35
|
-
function
|
|
36
|
-
(Cur memory request,
|
|
36
|
+
function recoverPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory) {
|
|
37
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
37
38
|
Budget memory budget = openValue();
|
|
38
39
|
|
|
39
40
|
while (request.i < request.len) {
|
|
40
|
-
(uint
|
|
41
|
-
|
|
41
|
+
(uint handler, uint resources, bytes32 key, bytes calldata witness) = request.unpackRecover();
|
|
42
|
+
recover(handler, key, witness, useValue(budget, resources));
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
closeValue(c.account, budget);
|
package/commands/Relay.sol
CHANGED
|
@@ -8,15 +8,15 @@ import {Budget} from "../utils/Value.sol";
|
|
|
8
8
|
|
|
9
9
|
using Cursors for Cur;
|
|
10
10
|
|
|
11
|
-
abstract contract
|
|
12
|
-
/// @notice Override to
|
|
13
|
-
/// @param
|
|
14
|
-
/// @param resources Chain-
|
|
11
|
+
abstract contract RoutePayableHook {
|
|
12
|
+
/// @notice Override to route an encoded payload through `portal`.
|
|
13
|
+
/// @param portal Destination portal identifier, often the destination host ID.
|
|
14
|
+
/// @param resources Chain-specific destination resources. EVM adapters
|
|
15
15
|
/// may interpret this as packed execution gas and destination value.
|
|
16
16
|
/// @param payload Encoded payload ready for the transport layer.
|
|
17
|
-
/// @param budget Source
|
|
17
|
+
/// @param budget Source native-value budget available for transport
|
|
18
18
|
/// fees and destination resource funding.
|
|
19
|
-
function
|
|
19
|
+
function route(uint portal, uint resources, bytes memory payload, Budget memory budget) internal virtual;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/// @title RelayPayable
|
|
@@ -24,7 +24,7 @@ abstract contract DispatchPayableHook {
|
|
|
24
24
|
/// Reverts unless the request contains exactly one RELAY block, preventing
|
|
25
25
|
/// the same state from being duplicated across multiple relays.
|
|
26
26
|
/// Produces no output state.
|
|
27
|
-
abstract contract RelayPayable is CommandBase, Payable,
|
|
27
|
+
abstract contract RelayPayable is CommandBase, Payable, RoutePayableHook {
|
|
28
28
|
uint internal immutable relayPayableId = commandId(this.relayPayable.selector);
|
|
29
29
|
|
|
30
30
|
constructor() {
|
|
@@ -36,12 +36,12 @@ abstract contract RelayPayable is CommandBase, Payable, DispatchPayableHook {
|
|
|
36
36
|
/// @param c Command context; `c.request` must contain exactly one RELAY block.
|
|
37
37
|
/// @return output Empty output state.
|
|
38
38
|
function relayPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory output) {
|
|
39
|
-
|
|
39
|
+
Cur memory request = Cursors.init(c.request, 1, 1);
|
|
40
40
|
Budget memory budget = openValue();
|
|
41
41
|
|
|
42
|
-
(uint
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
(uint portal, uint resources, bytes memory context) = request.relayToContext(c.account, c.state);
|
|
43
|
+
route(portal, resources, context, budget);
|
|
44
|
+
|
|
45
45
|
closeValue(c.account, budget);
|
|
46
46
|
request.complete();
|
|
47
47
|
return "";
|
package/commands/Withdraw.sol
CHANGED
|
@@ -32,7 +32,7 @@ abstract contract Withdraw is CommandBase, WithdrawHook {
|
|
|
32
32
|
function withdraw(
|
|
33
33
|
CommandContext calldata c
|
|
34
34
|
) external onlyCommand returns (bytes memory) {
|
|
35
|
-
(Cur memory state,
|
|
35
|
+
(Cur memory state, ) = Cursors.init(c.state, 1);
|
|
36
36
|
|
|
37
37
|
while (state.i < state.len) {
|
|
38
38
|
(bytes32 asset, uint amount) = state.unpackBalance();
|
|
@@ -29,7 +29,7 @@ abstract contract AllowAssets is AdminBase, AllowAssetsHook {
|
|
|
29
29
|
function allowAssets(
|
|
30
30
|
CommandContext calldata c
|
|
31
31
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
32
|
-
(Cur memory request,
|
|
32
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
33
33
|
|
|
34
34
|
while (request.i < request.len) {
|
|
35
35
|
bytes32 asset = request.unpackAsset();
|
|
@@ -31,7 +31,7 @@ abstract contract Allowance is AdminBase, AllowanceHook {
|
|
|
31
31
|
/// @param c Admin command context; `c.request` must contain ALLOWANCE blocks.
|
|
32
32
|
/// @return Empty output state.
|
|
33
33
|
function allowance(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
|
|
34
|
-
(Cur memory request,
|
|
34
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
35
35
|
|
|
36
36
|
while (request.i < request.len) {
|
|
37
37
|
(uint peer, bytes32 asset, uint amount) = request.unpackAllowance();
|
|
@@ -23,7 +23,7 @@ abstract contract Appoint is AdminBase {
|
|
|
23
23
|
function appoint(
|
|
24
24
|
CommandContext calldata c
|
|
25
25
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
26
|
-
(Cur memory request,
|
|
26
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
27
27
|
|
|
28
28
|
while (request.i < request.len) {
|
|
29
29
|
bytes32 account = request.unpackAccount();
|
|
@@ -23,7 +23,7 @@ abstract contract Authorize is AdminBase {
|
|
|
23
23
|
function authorize(
|
|
24
24
|
CommandContext calldata c
|
|
25
25
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
26
|
-
(Cur memory request,
|
|
26
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
27
27
|
|
|
28
28
|
while (request.i < request.len) {
|
|
29
29
|
uint node = request.unpackNode();
|
|
@@ -29,7 +29,7 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
|
|
|
29
29
|
function denyAssets(
|
|
30
30
|
CommandContext calldata c
|
|
31
31
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
32
|
-
(Cur memory request,
|
|
32
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
33
33
|
|
|
34
34
|
while (request.i < request.len) {
|
|
35
35
|
bytes32 asset = request.unpackAsset();
|
|
@@ -18,6 +18,7 @@ abstract contract DestroyHook {
|
|
|
18
18
|
abstract contract Destroy is AdminBase, DestroyHook {
|
|
19
19
|
uint internal immutable destroyId = commandId(this.destroy.selector);
|
|
20
20
|
|
|
21
|
+
/// @param input Request schema advertised for destruction input.
|
|
21
22
|
constructor(string memory input) {
|
|
22
23
|
emit Admin(host, destroyId, "1:0:0", input, Keys.Empty, Keys.Empty, false);
|
|
23
24
|
emit Labeled(destroyId, bytes32(0), "destroy");
|
|
@@ -23,7 +23,7 @@ abstract contract Dismiss is AdminBase {
|
|
|
23
23
|
function dismiss(
|
|
24
24
|
CommandContext calldata c
|
|
25
25
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
26
|
-
(Cur memory request,
|
|
26
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
27
27
|
|
|
28
28
|
while (request.i < request.len) {
|
|
29
29
|
bytes32 account = request.unpackAccount();
|
|
@@ -10,7 +10,7 @@ using Cursors for Cur;
|
|
|
10
10
|
|
|
11
11
|
/// @title ExecutePayable
|
|
12
12
|
/// @notice Admin command that forwards raw calldata to one or more target nodes.
|
|
13
|
-
/// Each CALL block specifies a target node ID,
|
|
13
|
+
/// Each CALL block specifies a target node ID, packed resources, and raw calldata payload.
|
|
14
14
|
/// Only callable by the admin account.
|
|
15
15
|
/// Unspent top-level `msg.value` remains on this host.
|
|
16
16
|
abstract contract ExecutePayable is AdminBase, Payable {
|
|
@@ -25,7 +25,7 @@ abstract contract ExecutePayable is AdminBase, Payable {
|
|
|
25
25
|
/// @param c Admin command context; `c.request` must contain CALL blocks.
|
|
26
26
|
/// @return Empty output state.
|
|
27
27
|
function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
|
|
28
|
-
(Cur memory request,
|
|
28
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
29
29
|
Budget memory budget = openValue();
|
|
30
30
|
|
|
31
31
|
while (request.i < request.len) {
|
package/commands/admin/Init.sol
CHANGED
|
@@ -18,6 +18,7 @@ abstract contract InitHook {
|
|
|
18
18
|
abstract contract Init is AdminBase, InitHook {
|
|
19
19
|
uint internal immutable initId = commandId(this.init.selector);
|
|
20
20
|
|
|
21
|
+
/// @param input Request schema advertised for initialization input.
|
|
21
22
|
constructor(string memory input) {
|
|
22
23
|
emit Admin(host, initId, "1:0:0", input, Keys.Empty, Keys.Empty, false);
|
|
23
24
|
emit Labeled(initId, bytes32(0), "init");
|
package/commands/admin/Label.sol
CHANGED
|
@@ -21,7 +21,7 @@ abstract contract Label is AdminBase {
|
|
|
21
21
|
/// @param c Admin command context; `c.request` must contain LABEL blocks.
|
|
22
22
|
/// @return Empty output state.
|
|
23
23
|
function label(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
|
|
24
|
-
(Cur memory request,
|
|
24
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
25
25
|
|
|
26
26
|
while (request.i < request.len) {
|
|
27
27
|
(uint id, bytes32 namespace, string memory name) = request.unpackLabel();
|
|
@@ -23,7 +23,7 @@ abstract contract Unauthorize is AdminBase {
|
|
|
23
23
|
function unauthorize(
|
|
24
24
|
CommandContext calldata c
|
|
25
25
|
) external onlyAdmin(c.account) returns (bytes memory) {
|
|
26
|
-
(Cur memory request,
|
|
26
|
+
(Cur memory request, ) = Cursors.init(c.request, 1);
|
|
27
27
|
|
|
28
28
|
while (request.i < request.len) {
|
|
29
29
|
uint node = request.unpackNode();
|
package/core/Access.sol
CHANGED
|
@@ -30,6 +30,7 @@ abstract contract AccessControl is Runtime, NodeEvent, GuardianEvent {
|
|
|
30
30
|
/// @dev Thrown when a caller, account, or node lacks required access.
|
|
31
31
|
error AccessDenied();
|
|
32
32
|
|
|
33
|
+
/// @param cmdr Commander address, or zero to make this contract self-managed.
|
|
33
34
|
constructor(address cmdr) {
|
|
34
35
|
commander = addrOr(cmdr, address(this));
|
|
35
36
|
admin = Accounts.toAdmin(commander);
|
package/core/Calls.sol
CHANGED
|
@@ -107,12 +107,23 @@ abstract contract CommandCalls is NodeCalls {
|
|
|
107
107
|
/// @title PortCalls
|
|
108
108
|
/// @notice Trusted port-call helpers for contracts that route port nodes.
|
|
109
109
|
abstract contract PortCalls is NodeCalls {
|
|
110
|
+
/// @notice Try to encode and call a trusted port node.
|
|
111
|
+
/// @param port Port node ID embedding the target selector.
|
|
112
|
+
/// @param value Native value to forward in wei.
|
|
113
|
+
/// @param input Port input block stream.
|
|
114
|
+
/// @return success True if the low-level port call succeeded.
|
|
115
|
+
function tryCallPort(uint port, uint128 value, bytes calldata input) internal returns (bool success) {
|
|
116
|
+
bytes4 selector = Nodes.portSelector(port);
|
|
117
|
+
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
118
|
+
return tryTrustedCall(port, value, data);
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
/// @notice Encode and call a trusted port node.
|
|
111
122
|
/// @param port Port node ID embedding the target selector.
|
|
112
123
|
/// @param value Native value to forward in wei.
|
|
113
124
|
/// @param input Port input block stream.
|
|
114
125
|
/// @return Decoded port output block stream.
|
|
115
|
-
function callPort(uint port, uint128 value, bytes
|
|
126
|
+
function callPort(uint port, uint128 value, bytes calldata input) internal returns (bytes memory) {
|
|
116
127
|
bytes4 selector = Nodes.portSelector(port);
|
|
117
128
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
118
129
|
return abi.decode(trustedCall(port, value, data), (bytes));
|
package/core/Payable.sol
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {Budget, Values} from "../utils/Value.sol";
|
|
5
|
+
import {max128} from "../utils/Utils.sol";
|
|
5
6
|
|
|
6
7
|
/// @title Payable
|
|
7
8
|
/// @notice Abstract mixin for entrypoints that accept native value (`msg.value`).
|
|
@@ -16,10 +17,16 @@ abstract contract Payable {
|
|
|
16
17
|
return Budget({remaining: msg.value});
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/// @notice Return the current call's native value as a checked uint128.
|
|
21
|
+
/// @return value Current `msg.value` in wei.
|
|
22
|
+
function msgValue() internal view returns (uint128 value) {
|
|
23
|
+
return uint128(max128(msg.value));
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
/// @notice Deduct the EVM value lane from a packed resource word and return it.
|
|
20
27
|
/// @dev EVM resources use the low 128 bits as native value/endowment.
|
|
21
28
|
/// @param budget Mutable budget to deduct from.
|
|
22
|
-
/// @param resources Packed
|
|
29
|
+
/// @param resources Packed resources.
|
|
23
30
|
/// @return value Native value to forward in wei.
|
|
24
31
|
function useValue(Budget memory budget, uint resources) internal pure returns (uint128 value) {
|
|
25
32
|
value = uint128(resources);
|
package/core/Pipeline.sol
CHANGED
|
@@ -43,7 +43,7 @@ abstract contract Pipeline is Payable {
|
|
|
43
43
|
bytes calldata steps,
|
|
44
44
|
Budget memory budget
|
|
45
45
|
) internal {
|
|
46
|
-
(Cur memory input,
|
|
46
|
+
(Cur memory input, ) = Cursors.init(steps, 1);
|
|
47
47
|
|
|
48
48
|
while (input.i < input.len) {
|
|
49
49
|
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
package/core/Portal.sol
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PortCalls} from "./Calls.sol";
|
|
5
|
+
|
|
6
|
+
/// @title Portal
|
|
7
|
+
/// @notice Base contract for hosts that route payloads through portal adapters.
|
|
8
|
+
abstract contract Portal is PortCalls {
|
|
9
|
+
error BadWitness();
|
|
10
|
+
|
|
11
|
+
mapping(bytes32 key => bytes32 digest) internal undelivered;
|
|
12
|
+
|
|
13
|
+
/// @notice Try to forward `message` to `port`.
|
|
14
|
+
/// @dev Records and returns `keccak256(message)` under `key` only when forwarding fails.
|
|
15
|
+
/// @param port Port that should handle the forwarded message.
|
|
16
|
+
/// @param key Forwarding/recovery lookup key.
|
|
17
|
+
/// @param message Encoded port input to forward.
|
|
18
|
+
/// @param value Native EVM value assigned to the forwarding attempt.
|
|
19
|
+
/// @return miss Message digest recorded for recovery when forwarding fails; zero on success.
|
|
20
|
+
function forward(uint port, bytes32 key, bytes calldata message, uint128 value) internal returns (bytes32 miss) {
|
|
21
|
+
if (tryCallPort(port, value, message)) return bytes32(0);
|
|
22
|
+
|
|
23
|
+
miss = keccak256(message);
|
|
24
|
+
undelivered[key] = miss;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// @notice Retry a previously undelivered witness through `port`.
|
|
28
|
+
/// @dev The witness must hash to the digest stored under `key`.
|
|
29
|
+
/// @param port Port that should attempt recovery.
|
|
30
|
+
/// @param key Recovery lookup key.
|
|
31
|
+
/// @param witness Witness payload used to prove and replay recovery.
|
|
32
|
+
/// @param value Native EVM value assigned to the recovery attempt.
|
|
33
|
+
function retry(uint port, bytes32 key, bytes calldata witness, uint128 value) internal virtual {
|
|
34
|
+
if (undelivered[key] != keccak256(witness)) revert BadWitness();
|
|
35
|
+
|
|
36
|
+
delete undelivered[key];
|
|
37
|
+
callPort(port, value, witness);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/docs/Schema.md
CHANGED
|
@@ -120,7 +120,7 @@ Aliases may be used on any block item, including child blocks and prime items.
|
|
|
120
120
|
A child block without an inline body may also be used as a schema reference:
|
|
121
121
|
|
|
122
122
|
```txt
|
|
123
|
-
#
|
|
123
|
+
#recover { uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
Alias resolution is context-dependent. A consumer may resolve `#context` from the
|
|
@@ -136,13 +136,13 @@ path does not change the block key, payload bytes, payload length, cursor
|
|
|
136
136
|
behavior, or any onchain validation. It is metadata only.
|
|
137
137
|
|
|
138
138
|
```txt
|
|
139
|
-
#dispatch { uint dst.
|
|
139
|
+
#dispatch { uint dst.portal, uint dst.resources, #bytes as dst.payload }
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
This has the same runtime layout as:
|
|
143
143
|
|
|
144
144
|
```txt
|
|
145
|
-
#dispatch { uint
|
|
145
|
+
#dispatch { uint portal, uint resources, #bytes as payload }
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
Offchain tooling may decode the dotted form into a nested object:
|
|
@@ -150,7 +150,7 @@ Offchain tooling may decode the dotted form into a nested object:
|
|
|
150
150
|
```ts
|
|
151
151
|
{
|
|
152
152
|
dst: {
|
|
153
|
-
|
|
153
|
+
portal,
|
|
154
154
|
resources,
|
|
155
155
|
payload
|
|
156
156
|
}
|
|
@@ -165,8 +165,8 @@ object.
|
|
|
165
165
|
Tooling should reject duplicate full paths and prefix/value collisions:
|
|
166
166
|
|
|
167
167
|
```txt
|
|
168
|
-
uint dst.
|
|
169
|
-
uint dst, uint dst.
|
|
168
|
+
uint dst.portal, uint dst.portal // duplicate path
|
|
169
|
+
uint dst, uint dst.portal // prefix/value collision
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
The same rule applies to block aliases:
|
|
@@ -196,10 +196,14 @@ true. `bytesN` values are encoded as exactly `N` bytes with no padding.
|
|
|
196
196
|
|
|
197
197
|
## Chain Resources
|
|
198
198
|
|
|
199
|
-
Fields named `
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
Fields named `portal` are routing identifiers; they are often the destination
|
|
200
|
+
host ID, but a transport adapter may define a different stable handle.
|
|
201
|
+
|
|
202
|
+
Fields named `resources` are chain-specific resource words. A portal adapter
|
|
203
|
+
interprets them for the destination runtime. Different runtimes may pack these
|
|
204
|
+
words differently, but a given runtime must use one stable format everywhere.
|
|
205
|
+
For EVM chains, the low 128 bits are native value / endowment in wei; higher
|
|
206
|
+
bits are reserved for execution resources such as gas.
|
|
203
207
|
|
|
204
208
|
## Protocol IDs
|
|
205
209
|
|
|
@@ -282,7 +286,7 @@ Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
|
282
286
|
#call { uint target, uint resources, #bytes as payload }
|
|
283
287
|
#step { uint target, uint resources, #bytes as request }
|
|
284
288
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
|
285
|
-
#
|
|
289
|
+
#recover { uint handler, uint resources, bytes32 key, #bytes as witness }
|
|
286
290
|
#auth { uint cid, uint deadline, #bytes as proof }
|
|
287
291
|
```
|
|
288
292
|
|
package/events/Dispatch.sol
CHANGED
|
@@ -5,14 +5,14 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when a host records an outbound dispatch reference.
|
|
7
7
|
abstract contract DispatchEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Dispatch(uint indexed host, uint
|
|
8
|
+
string private constant ABI = "event Dispatch(uint indexed host, uint portal, uint resources, bytes32 key, bytes32 digest)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID that owns the dispatch.
|
|
11
|
-
/// @param
|
|
12
|
-
/// @param resources Chain-
|
|
11
|
+
/// @param portal Destination portal identifier, often the destination host ID.
|
|
12
|
+
/// @param resources Chain-specific resources assigned to the dispatch.
|
|
13
|
+
/// @param key Dispatch correlation or recovery lookup key.
|
|
13
14
|
/// @param digest Digest of the dispatched payload or canonical envelope.
|
|
14
|
-
|
|
15
|
-
event Dispatch(uint indexed host, uint chain, uint resources, bytes32 digest, bytes32 ref);
|
|
15
|
+
event Dispatch(uint indexed host, uint portal, uint resources, bytes32 key, bytes32 digest);
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
18
|
emit EventAbi(ABI);
|
|
@@ -0,0 +1,17 @@
|
|
|
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 when a host recovers a previously recorded key.
|
|
7
|
+
abstract contract RecoveredEvent is EventEmitter {
|
|
8
|
+
string private constant ABI = "event Recovered(uint indexed host, bytes32 key)";
|
|
9
|
+
|
|
10
|
+
/// @param host Host node ID that owns the recovered key.
|
|
11
|
+
/// @param key Recovery lookup key.
|
|
12
|
+
event Recovered(uint indexed host, bytes32 key);
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
emit EventAbi(ABI);
|
|
16
|
+
}
|
|
17
|
+
}
|
package/events/Route.sol
CHANGED
|
@@ -3,14 +3,14 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {EventEmitter} from "./Emitter.sol";
|
|
5
5
|
|
|
6
|
-
/// @notice Emitted when a host
|
|
6
|
+
/// @notice Emitted when a host updates a portal route.
|
|
7
7
|
abstract contract RouteEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Route(uint indexed host, uint
|
|
8
|
+
string private constant ABI = "event Route(uint indexed host, uint portal, uint status)";
|
|
9
9
|
|
|
10
10
|
/// @param host Host node ID that owns the route.
|
|
11
|
-
/// @param
|
|
12
|
-
/// @param
|
|
13
|
-
event Route(uint indexed host, uint
|
|
11
|
+
/// @param portal Destination portal identifier, often the destination host ID.
|
|
12
|
+
/// @param status Route status. Zero means inactive; nonzero means active.
|
|
13
|
+
event Route(uint indexed host, uint portal, uint status);
|
|
14
14
|
|
|
15
15
|
constructor() {
|
|
16
16
|
emit EventAbi(ABI);
|
|
@@ -0,0 +1,18 @@
|
|
|
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 when a host records an undelivered portal message.
|
|
7
|
+
abstract contract UndeliveredEvent is EventEmitter {
|
|
8
|
+
string private constant ABI = "event Undelivered(uint indexed host, bytes32 key, bytes32 digest)";
|
|
9
|
+
|
|
10
|
+
/// @param host Host node ID that owns the undelivered message.
|
|
11
|
+
/// @param key Delivery lookup key.
|
|
12
|
+
/// @param digest Digest of the undelivered message.
|
|
13
|
+
event Undelivered(uint indexed host, bytes32 key, bytes32 digest);
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
emit EventAbi(ABI);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/guards/Revoke.sol
CHANGED
|
@@ -18,7 +18,7 @@ abstract contract Revoke is GuardBase {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function revoke(bytes calldata request) external onlyGuardian {
|
|
21
|
-
(Cur memory input,
|
|
21
|
+
(Cur memory input, ) = Cursors.init(request, 1);
|
|
22
22
|
|
|
23
23
|
while (input.i < input.len) {
|
|
24
24
|
uint node = input.unpackNode();
|
package/package.json
CHANGED
package/ports/AllowAssets.sol
CHANGED
|
@@ -22,7 +22,7 @@ abstract contract PortAllowAssets is PortBase, AllowAssetsHook {
|
|
|
22
22
|
/// @param data ASSET block stream supplied by the trusted peer.
|
|
23
23
|
/// @return Empty response bytes.
|
|
24
24
|
function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory assets,
|
|
25
|
+
(Cur memory assets, ) = Cursors.init(data, 1);
|
|
26
26
|
|
|
27
27
|
while (assets.i < assets.len) {
|
|
28
28
|
bytes32 asset = assets.unpackAsset();
|
package/ports/Allowance.sol
CHANGED
|
@@ -23,7 +23,7 @@ abstract contract PortAllowance is PortBase, AllowanceHook {
|
|
|
23
23
|
/// @param data AMOUNT block stream requested by the trusted peer.
|
|
24
24
|
/// @return Empty response bytes.
|
|
25
25
|
function portAllowance(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
26
|
-
(Cur memory amounts,
|
|
26
|
+
(Cur memory amounts, ) = Cursors.init(data, 1);
|
|
27
27
|
uint peer = caller();
|
|
28
28
|
|
|
29
29
|
while (amounts.i < amounts.len) {
|
package/ports/Credit.sol
CHANGED
|
@@ -22,7 +22,7 @@ abstract contract PortCreditAccount is PortBase, CreditAccountHook {
|
|
|
22
22
|
/// @param data ACCOUNT_AMOUNT block stream supplied by the trusted peer.
|
|
23
23
|
/// @return Empty response bytes.
|
|
24
24
|
function portCreditAccount(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory amounts,
|
|
25
|
+
(Cur memory amounts, ) = Cursors.init(data, 1);
|
|
26
26
|
|
|
27
27
|
while (amounts.i < amounts.len) {
|
|
28
28
|
(bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
|
package/ports/Debit.sol
CHANGED
|
@@ -22,7 +22,7 @@ abstract contract PortDebitAccount is PortBase, DebitAccountHook {
|
|
|
22
22
|
/// @param data ACCOUNT_AMOUNT block stream supplied by the trusted peer.
|
|
23
23
|
/// @return Empty response bytes.
|
|
24
24
|
function portDebitAccount(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory amounts,
|
|
25
|
+
(Cur memory amounts, ) = Cursors.init(data, 1);
|
|
26
26
|
|
|
27
27
|
while (amounts.i < amounts.len) {
|
|
28
28
|
(bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
|
package/ports/DenyAssets.sol
CHANGED
|
@@ -22,7 +22,7 @@ abstract contract PortDenyAssets is PortBase, DenyAssetsHook {
|
|
|
22
22
|
/// @param data ASSET block stream supplied by the trusted peer.
|
|
23
23
|
/// @return Empty response bytes.
|
|
24
24
|
function portDenyAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
(Cur memory assets,
|
|
25
|
+
(Cur memory assets, ) = Cursors.init(data, 1);
|
|
26
26
|
|
|
27
27
|
while (assets.i < assets.len) {
|
|
28
28
|
bytes32 asset = assets.unpackAsset();
|
package/ports/Dispatch.sol
CHANGED
|
@@ -3,15 +3,15 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { PortBase } from "./Base.sol";
|
|
5
5
|
import { Payable } from "../core/Payable.sol";
|
|
6
|
+
import { RoutePayableHook } from "../commands/Relay.sol";
|
|
6
7
|
import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
7
|
-
import { DispatchPayableHook } from "../commands/Relay.sol";
|
|
8
8
|
import { Budget } from "../utils/Value.sol";
|
|
9
9
|
|
|
10
10
|
using Cursors for Cur;
|
|
11
11
|
|
|
12
12
|
/// @title PortDispatchPayable
|
|
13
|
-
/// @notice Port endpoint that forwards DISPATCH blocks to a host-defined
|
|
14
|
-
abstract contract PortDispatchPayable is PortBase, Payable,
|
|
13
|
+
/// @notice Port endpoint that forwards DISPATCH blocks to a host-defined route hook.
|
|
14
|
+
abstract contract PortDispatchPayable is PortBase, Payable, RoutePayableHook {
|
|
15
15
|
uint internal immutable portDispatchPayableId = portId(this.portDispatchPayable.selector);
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
@@ -19,18 +19,18 @@ abstract contract PortDispatchPayable is PortBase, Payable, DispatchPayableHook
|
|
|
19
19
|
emit Labeled(portDispatchPayableId, bytes32(0), "portDispatchPayable");
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/// @notice Forward peer-supplied dispatches to the host-defined
|
|
23
|
-
/// @dev
|
|
22
|
+
/// @notice Forward peer-supplied dispatches to the host-defined route hook.
|
|
23
|
+
/// @dev Route hooks receive the shared top-level source value
|
|
24
24
|
/// budget. Any `msg.value` not spent by the hook remains on this host.
|
|
25
25
|
/// @param data DISPATCH block stream supplied by the trusted peer.
|
|
26
26
|
/// @return output Empty response bytes.
|
|
27
27
|
function portDispatchPayable(bytes calldata data) external payable onlyPeer returns (bytes memory output) {
|
|
28
|
-
(Cur memory input,
|
|
28
|
+
(Cur memory input, ) = Cursors.init(data, 1);
|
|
29
29
|
Budget memory budget = openValue();
|
|
30
30
|
|
|
31
31
|
while (input.i < input.len) {
|
|
32
|
-
(uint
|
|
33
|
-
|
|
32
|
+
(uint portal, uint resources, bytes calldata payload) = input.unpackDispatch();
|
|
33
|
+
route(portal, resources, bytes(payload), budget);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
input.complete();
|
package/ports/Pipe.sol
CHANGED
|
@@ -25,7 +25,7 @@ abstract contract PortPipePayable is PortBase, Pipeline {
|
|
|
25
25
|
/// @param data CONTEXT block stream supplied by the trusted peer.
|
|
26
26
|
/// @return Empty response bytes.
|
|
27
27
|
function portPipePayable(bytes calldata data) external payable onlyPeer returns (bytes memory) {
|
|
28
|
-
(Cur memory input,
|
|
28
|
+
(Cur memory input, ) = Cursors.init(data, 1);
|
|
29
29
|
Budget memory budget = openValue();
|
|
30
30
|
|
|
31
31
|
while (input.i < input.len) {
|