@rootzero/contracts 1.0.0 → 1.1.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 +14 -0
- package/Endpoints.sol +1 -1
- package/blocks/Cursors.sol +80 -35
- package/blocks/Keys.sol +5 -5
- package/blocks/Schema.sol +8 -5
- package/blocks/Writers.sol +9 -9
- package/commands/Relay.sol +12 -20
- package/commands/admin/Execute.sol +3 -3
- package/core/Calls.sol +3 -3
- package/core/Payable.sol +12 -10
- package/core/Pipeline.sol +4 -4
- package/docs/Schema.md +13 -6
- package/package.json +2 -1
- package/peer/Dispatch.sol +4 -15
- package/peer/Pipe.sol +4 -4
- package/utils/Value.sol +5 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Until the protocol reaches integration-stable status, minor versions may include
|
|
4
|
+
breaking API changes. Breaking changes are called out explicitly.
|
|
5
|
+
|
|
6
|
+
## 1.1.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Renamed local execution fields from `value` to `resources` in PIPE, CALL, and STEP schemas.
|
|
11
|
+
- Interprets EVM value as the low 128 bits of packed `resources`.
|
|
12
|
+
- Changed native value APIs and dispatch hooks to use `uint128` for actual EVM value.
|
|
13
|
+
- Replaced the relay-specific hook with shared `DispatchPayableHook`.
|
|
14
|
+
- `RelayPayable` now dispatches an encoded PIPE payload and settles leftover user command value.
|
package/Endpoints.sol
CHANGED
|
@@ -16,7 +16,7 @@ import { DebitAccount, DebitAccountHook } from "./commands/Debit.sol";
|
|
|
16
16
|
import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
|
|
17
17
|
import { Payout, PayoutHook } from "./commands/Payout.sol";
|
|
18
18
|
import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
|
|
19
|
-
import { RelayPayable,
|
|
19
|
+
import { RelayPayable, DispatchPayableHook } from "./commands/Relay.sol";
|
|
20
20
|
import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
|
|
21
21
|
|
|
22
22
|
// Admin commands
|
package/blocks/Cursors.sol
CHANGED
|
@@ -518,20 +518,20 @@ library Cursors {
|
|
|
518
518
|
|
|
519
519
|
/// @notice Encode a STEP block.
|
|
520
520
|
/// @param target Command target identifier.
|
|
521
|
-
/// @param
|
|
521
|
+
/// @param resources Chain resources assigned to the step.
|
|
522
522
|
/// @param request Raw nested request payload.
|
|
523
523
|
/// @return Encoded STEP block bytes.
|
|
524
|
-
function toStepBlock(uint target, uint
|
|
525
|
-
return createBlock(Keys.Step, bytes.concat(bytes32(target), bytes32(
|
|
524
|
+
function toStepBlock(uint target, uint resources, bytes memory request) internal pure returns (bytes memory) {
|
|
525
|
+
return createBlock(Keys.Step, bytes.concat(bytes32(target), bytes32(resources), toBytesBlock(request)));
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
/// @notice Encode a CALL block.
|
|
529
529
|
/// @param target Target node identifier.
|
|
530
|
-
/// @param
|
|
530
|
+
/// @param resources Chain resources assigned to the call.
|
|
531
531
|
/// @param data Raw calldata payload for the target.
|
|
532
532
|
/// @return Encoded CALL block bytes.
|
|
533
|
-
function toCallBlock(uint target, uint
|
|
534
|
-
return createBlock(Keys.Call, bytes.concat(bytes32(target), bytes32(
|
|
533
|
+
function toCallBlock(uint target, uint resources, bytes memory data) internal pure returns (bytes memory) {
|
|
534
|
+
return createBlock(Keys.Call, bytes.concat(bytes32(target), bytes32(resources), toBytesBlock(data)));
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/// @notice Encode a CONTEXT block.
|
|
@@ -548,36 +548,36 @@ library Cursors {
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
/// @notice Encode a PIPE block.
|
|
551
|
-
/// @param
|
|
551
|
+
/// @param resources Chain resources assigned to the pipe.
|
|
552
552
|
/// @param account Command account identifier.
|
|
553
553
|
/// @param state Embedded state block stream.
|
|
554
554
|
/// @param steps Embedded step block stream.
|
|
555
555
|
/// @return Encoded PIPE block bytes.
|
|
556
556
|
function toPipeBlock(
|
|
557
|
-
uint
|
|
557
|
+
uint resources,
|
|
558
558
|
bytes32 account,
|
|
559
559
|
bytes memory state,
|
|
560
560
|
bytes memory steps
|
|
561
561
|
) internal pure returns (bytes memory) {
|
|
562
|
-
return createBlock(Keys.Pipe, bytes.concat(bytes32(
|
|
562
|
+
return createBlock(Keys.Pipe, bytes.concat(bytes32(resources), toContextBlock(account, state, steps)));
|
|
563
563
|
}
|
|
564
564
|
|
|
565
565
|
/// @notice Encode a RELAY block.
|
|
566
566
|
/// @param chain Destination chain node ID.
|
|
567
|
-
/// @param
|
|
567
|
+
/// @param resources Chain-adapter-specific resources for the destination pipe.
|
|
568
568
|
/// @param steps Nested step block stream.
|
|
569
569
|
/// @return Encoded RELAY block bytes.
|
|
570
|
-
function toRelayBlock(uint chain, uint
|
|
571
|
-
return createBlock(Keys.Relay, bytes.concat(bytes32(chain), bytes32(
|
|
570
|
+
function toRelayBlock(uint chain, uint resources, bytes memory steps) internal pure returns (bytes memory) {
|
|
571
|
+
return createBlock(Keys.Relay, bytes.concat(bytes32(chain), bytes32(resources), toBytesBlock(steps)));
|
|
572
572
|
}
|
|
573
573
|
|
|
574
574
|
/// @notice Encode a DISPATCH block.
|
|
575
575
|
/// @param chain Destination chain node ID.
|
|
576
|
-
/// @param
|
|
576
|
+
/// @param resources Chain-adapter-specific resources for the destination dispatch.
|
|
577
577
|
/// @param payload Encoded cross-chain payload.
|
|
578
578
|
/// @return Encoded DISPATCH block bytes.
|
|
579
|
-
function toDispatchBlock(uint chain, uint
|
|
580
|
-
return createBlock(Keys.Dispatch, bytes.concat(bytes32(chain), bytes32(
|
|
579
|
+
function toDispatchBlock(uint chain, uint resources, bytes memory payload) internal pure returns (bytes memory) {
|
|
580
|
+
return createBlock(Keys.Dispatch, bytes.concat(bytes32(chain), bytes32(resources), toBytesBlock(payload)));
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
// -------------------------------------------------------------------------
|
|
@@ -598,6 +598,18 @@ library Cursors {
|
|
|
598
598
|
cur.i += n;
|
|
599
599
|
}
|
|
600
600
|
|
|
601
|
+
/// @notice Read the next 4 bytes from the cursor and advance by 4 bytes.
|
|
602
|
+
/// @dev Performs no bounds, key, length, or cursor checks.
|
|
603
|
+
/// @param cur Cursor whose current position is advanced by 4 bytes.
|
|
604
|
+
/// @return value Loaded bytes4 value.
|
|
605
|
+
function read4(Cur memory cur) internal pure returns (bytes4 value) {
|
|
606
|
+
uint abs = cur.offset + cur.i;
|
|
607
|
+
assembly ("memory-safe") {
|
|
608
|
+
value := calldataload(abs)
|
|
609
|
+
}
|
|
610
|
+
cur.i += 4;
|
|
611
|
+
}
|
|
612
|
+
|
|
601
613
|
/// @notice Read the next 16 bytes from the cursor and advance by 16 bytes.
|
|
602
614
|
/// @dev Performs no bounds, key, length, or cursor checks.
|
|
603
615
|
/// @param cur Cursor whose current position is advanced by 16 bytes.
|
|
@@ -622,6 +634,18 @@ library Cursors {
|
|
|
622
634
|
cur.i += 32;
|
|
623
635
|
}
|
|
624
636
|
|
|
637
|
+
/// @notice Read the next uint from the cursor and advance by one word.
|
|
638
|
+
/// @dev Performs no bounds, key, length, or cursor checks.
|
|
639
|
+
/// @param cur Cursor whose current position is advanced by 32 bytes.
|
|
640
|
+
/// @return value Loaded uint value.
|
|
641
|
+
function readUint(Cur memory cur) internal pure returns (uint value) {
|
|
642
|
+
uint abs = cur.offset + cur.i;
|
|
643
|
+
assembly ("memory-safe") {
|
|
644
|
+
value := calldataload(abs)
|
|
645
|
+
}
|
|
646
|
+
cur.i += 32;
|
|
647
|
+
}
|
|
648
|
+
|
|
625
649
|
/// @notice Read the next two 32-byte words from the cursor and advance by 64 bytes.
|
|
626
650
|
/// @dev Performs no bounds, key, length, or cursor checks.
|
|
627
651
|
/// @param cur Cursor whose current position is advanced by 64 bytes.
|
|
@@ -1055,12 +1079,12 @@ library Cursors {
|
|
|
1055
1079
|
/// The `req` slice is the raw payload of the block's required BYTES child.
|
|
1056
1080
|
/// @param cur Cursor; advanced past the block.
|
|
1057
1081
|
/// @return target Destination node ID for the sub-command.
|
|
1058
|
-
/// @return
|
|
1082
|
+
/// @return resources Chain resources assigned to the step.
|
|
1059
1083
|
/// @return req Embedded request bytes for the sub-command.
|
|
1060
|
-
function unpackStep(Cur memory cur) internal pure returns (uint target, uint
|
|
1084
|
+
function unpackStep(Cur memory cur) internal pure returns (uint target, uint resources, bytes calldata req) {
|
|
1061
1085
|
uint end = cur.enter(Keys.Step, 64 + Sizes.Header, 0);
|
|
1062
1086
|
target = uint(cur.read32());
|
|
1063
|
-
|
|
1087
|
+
resources = uint(cur.read32());
|
|
1064
1088
|
req = cur.unpackBytes();
|
|
1065
1089
|
cur.exit(end);
|
|
1066
1090
|
}
|
|
@@ -1069,12 +1093,12 @@ library Cursors {
|
|
|
1069
1093
|
/// The `data` slice is the raw payload of the block's required BYTES child.
|
|
1070
1094
|
/// @param cur Cursor; advanced past the block.
|
|
1071
1095
|
/// @return target Target node ID to call.
|
|
1072
|
-
/// @return
|
|
1096
|
+
/// @return resources Chain resources assigned to the call.
|
|
1073
1097
|
/// @return data Raw calldata payload for the target.
|
|
1074
|
-
function unpackCall(Cur memory cur) internal pure returns (uint target, uint
|
|
1098
|
+
function unpackCall(Cur memory cur) internal pure returns (uint target, uint resources, bytes calldata data) {
|
|
1075
1099
|
uint end = cur.enter(Keys.Call, 64 + Sizes.Header, 0);
|
|
1076
1100
|
target = uint(cur.read32());
|
|
1077
|
-
|
|
1101
|
+
resources = uint(cur.read32());
|
|
1078
1102
|
data = cur.unpackBytes();
|
|
1079
1103
|
cur.exit(end);
|
|
1080
1104
|
}
|
|
@@ -1095,47 +1119,47 @@ library Cursors {
|
|
|
1095
1119
|
cur.exit(end);
|
|
1096
1120
|
}
|
|
1097
1121
|
|
|
1098
|
-
/// @notice Consume a PIPE block and return its
|
|
1122
|
+
/// @notice Consume a PIPE block and return its resources and context fields.
|
|
1099
1123
|
/// @param cur Cursor; advanced past the block.
|
|
1100
|
-
/// @return
|
|
1124
|
+
/// @return resources Chain resources assigned to the pipe.
|
|
1101
1125
|
/// @return account Command account identifier.
|
|
1102
1126
|
/// @return state Embedded state block stream.
|
|
1103
1127
|
/// @return steps Embedded step block stream.
|
|
1104
1128
|
function unpackPipe(
|
|
1105
1129
|
Cur memory cur
|
|
1106
|
-
) internal pure returns (uint
|
|
1130
|
+
) internal pure returns (uint resources, bytes32 account, bytes calldata state, bytes calldata steps) {
|
|
1107
1131
|
uint end = cur.enter(Keys.Pipe, 32 + Sizes.Header + 32 + 2 * Sizes.Header, 0);
|
|
1108
|
-
|
|
1132
|
+
resources = uint(cur.read32());
|
|
1109
1133
|
(account, state, steps) = cur.unpackContext();
|
|
1110
1134
|
cur.exit(end);
|
|
1111
1135
|
}
|
|
1112
1136
|
|
|
1113
|
-
/// @notice Consume a RELAY block and return its destination chain,
|
|
1137
|
+
/// @notice Consume a RELAY block and return its destination chain, resources, and step stream.
|
|
1114
1138
|
/// @param cur Cursor; advanced past the block.
|
|
1115
1139
|
/// @return chain Destination chain node ID.
|
|
1116
|
-
/// @return
|
|
1140
|
+
/// @return resources Chain-adapter-specific resources for the destination pipe.
|
|
1117
1141
|
/// @return steps Embedded step block stream.
|
|
1118
1142
|
function unpackRelay(
|
|
1119
1143
|
Cur memory cur
|
|
1120
|
-
) internal pure returns (uint chain, uint
|
|
1144
|
+
) internal pure returns (uint chain, uint resources, bytes calldata steps) {
|
|
1121
1145
|
uint end = cur.enter(Keys.Relay, 64 + Sizes.Header, 0);
|
|
1122
|
-
chain =
|
|
1123
|
-
|
|
1146
|
+
chain = cur.readUint();
|
|
1147
|
+
resources = cur.readUint();
|
|
1124
1148
|
steps = cur.unpackBytes();
|
|
1125
1149
|
cur.exit(end);
|
|
1126
1150
|
}
|
|
1127
1151
|
|
|
1128
|
-
/// @notice Consume a DISPATCH block and return its destination chain,
|
|
1152
|
+
/// @notice Consume a DISPATCH block and return its destination chain, resources, and payload.
|
|
1129
1153
|
/// @param cur Cursor; advanced past the block.
|
|
1130
1154
|
/// @return chain Destination chain node ID.
|
|
1131
|
-
/// @return
|
|
1155
|
+
/// @return resources Chain-adapter-specific resources for the destination dispatch.
|
|
1132
1156
|
/// @return payload Encoded cross-chain payload.
|
|
1133
1157
|
function unpackDispatch(
|
|
1134
1158
|
Cur memory cur
|
|
1135
|
-
) internal pure returns (uint chain, uint
|
|
1159
|
+
) internal pure returns (uint chain, uint resources, bytes calldata payload) {
|
|
1136
1160
|
uint end = cur.enter(Keys.Dispatch, 64 + Sizes.Header, 0);
|
|
1137
|
-
chain =
|
|
1138
|
-
|
|
1161
|
+
chain = cur.readUint();
|
|
1162
|
+
resources = cur.readUint();
|
|
1139
1163
|
payload = cur.unpackBytes();
|
|
1140
1164
|
cur.exit(end);
|
|
1141
1165
|
}
|
|
@@ -1361,6 +1385,27 @@ library Cursors {
|
|
|
1361
1385
|
if (uint(bytes32(msg.data[abs + 128:abs + 160])) < amount) revert UnexpectedValue();
|
|
1362
1386
|
}
|
|
1363
1387
|
|
|
1388
|
+
// -------------------------------------------------------------------------
|
|
1389
|
+
// Transform helpers
|
|
1390
|
+
// -------------------------------------------------------------------------
|
|
1391
|
+
|
|
1392
|
+
/// @notice Consume a RELAY block and encode its destination pipe payload.
|
|
1393
|
+
/// @param cur Cursor; advanced past the RELAY block.
|
|
1394
|
+
/// @param account Account identifier to embed in the destination pipe context.
|
|
1395
|
+
/// @param state State block stream to embed in the destination pipe context.
|
|
1396
|
+
/// @return chain Destination chain node ID.
|
|
1397
|
+
/// @return resources Chain resources assigned to the destination pipe.
|
|
1398
|
+
/// @return pipe Encoded PIPE block containing `account`, `state`, and relay steps.
|
|
1399
|
+
function relayToPipe(
|
|
1400
|
+
Cur memory cur,
|
|
1401
|
+
bytes32 account,
|
|
1402
|
+
bytes calldata state
|
|
1403
|
+
) internal pure returns (uint chain, uint resources, bytes memory pipe) {
|
|
1404
|
+
bytes calldata steps;
|
|
1405
|
+
(chain, resources, steps) = cur.unpackRelay();
|
|
1406
|
+
pipe = toPipeBlock(resources, account, bytes(state), bytes(steps));
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1364
1409
|
// -------------------------------------------------------------------------
|
|
1365
1410
|
// Search helpers
|
|
1366
1411
|
// -------------------------------------------------------------------------
|
package/blocks/Keys.sol
CHANGED
|
@@ -37,17 +37,17 @@ library Keys {
|
|
|
37
37
|
bytes4 constant Account = bytes4(keccak256("#account"));
|
|
38
38
|
/// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount)
|
|
39
39
|
bytes4 constant Transaction = bytes4(keccak256("#transaction"));
|
|
40
|
-
/// @dev Sub-command invocation - (uint target, uint
|
|
40
|
+
/// @dev Sub-command invocation - (uint target, uint resources, #bytes as request)
|
|
41
41
|
bytes4 constant Step = bytes4(keccak256("#step"));
|
|
42
|
-
/// @dev Cross-chain pipe relay - (uint chain, uint
|
|
42
|
+
/// @dev Cross-chain pipe relay - (uint chain, uint resources, #bytes as steps)
|
|
43
43
|
bytes4 constant Relay = bytes4(keccak256("#relay"));
|
|
44
|
-
/// @dev Cross-chain encoded payload dispatch - (uint chain, uint
|
|
44
|
+
/// @dev Cross-chain encoded payload dispatch - (uint chain, uint resources, #bytes as payload)
|
|
45
45
|
bytes4 constant Dispatch = bytes4(keccak256("#dispatch"));
|
|
46
|
-
/// @dev Raw external call - (uint target, uint
|
|
46
|
+
/// @dev Raw external call - (uint target, uint resources, #bytes as payload)
|
|
47
47
|
bytes4 constant Call = bytes4(keccak256("#call"));
|
|
48
48
|
/// @dev Command context transport - (bytes32 account, #bytes as state, #bytes as request)
|
|
49
49
|
bytes4 constant Context = bytes4(keccak256("#context"));
|
|
50
|
-
/// @dev Pipeline invocation - (uint
|
|
50
|
+
/// @dev Pipeline invocation - (uint resources, #context)
|
|
51
51
|
bytes4 constant Pipe = bytes4(keccak256("#pipe"));
|
|
52
52
|
/// @dev Authentication proof - (uint cid, uint deadline, #bytes as proof); must appear last in its segment
|
|
53
53
|
bytes4 constant Auth = bytes4(keccak256("#auth"));
|
package/blocks/Schema.sol
CHANGED
|
@@ -18,6 +18,9 @@ pragma solidity ^0.8.33;
|
|
|
18
18
|
// - run items may repeat at top level for batching
|
|
19
19
|
// - `maybe #x { ... }` marks an optional block item
|
|
20
20
|
// - `many #x { ... }` emits one generic list block containing repeated `#x` items
|
|
21
|
+
// - `resources` fields are chain-specific resource words; one chain type may
|
|
22
|
+
// pack them differently from another, but a given chain type must use one
|
|
23
|
+
// stable format everywhere. EVM resources use the low 128 bits as native value.
|
|
21
24
|
// - fixed fields are packed in declaration order
|
|
22
25
|
// - blocks have fixed fields followed by a dynamic child-block tail
|
|
23
26
|
// - child block tails are embedded directly, without an extra stream wrapper
|
|
@@ -62,11 +65,11 @@ library Schemas {
|
|
|
62
65
|
string constant Allowance = "#allowance { uint host, bytes32 asset, bytes32 meta, uint amount }";
|
|
63
66
|
string constant Transaction = "#transaction { bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount }";
|
|
64
67
|
string constant Context = "#context { bytes32 account, #bytes as state, #bytes as request }";
|
|
65
|
-
string constant Pipe = "#pipe { uint
|
|
66
|
-
string constant Call = "#call { uint target, uint
|
|
67
|
-
string constant Step = "#step { uint target, uint
|
|
68
|
-
string constant Relay = "#relay { uint chain, uint
|
|
69
|
-
string constant Dispatch = "#dispatch { uint chain, uint
|
|
68
|
+
string constant Pipe = "#pipe { uint resources, #context { bytes32 account, #bytes as state, #bytes as steps } }";
|
|
69
|
+
string constant Call = "#call { uint target, uint resources, #bytes as payload }";
|
|
70
|
+
string constant Step = "#step { uint target, uint resources, #bytes as request }";
|
|
71
|
+
string constant Relay = "#relay { uint chain, uint resources, #bytes as steps }";
|
|
72
|
+
string constant Dispatch = "#dispatch { uint chain, uint resources, #bytes as payload }";
|
|
70
73
|
string constant Bounty = "#bounty { uint amount, bytes32 relayer }";
|
|
71
74
|
string constant Fee = "#fee { uint amount }";
|
|
72
75
|
string constant Auth = "#auth { uint cid, uint deadline, #bytes as proof }";
|
package/blocks/Writers.sol
CHANGED
|
@@ -865,19 +865,19 @@ library Writers {
|
|
|
865
865
|
/// @notice Append a STEP block with a nested request BYTES payload.
|
|
866
866
|
/// @param writer Destination writer; `i` is advanced by the encoded STEP block length.
|
|
867
867
|
/// @param target Command target identifier.
|
|
868
|
-
/// @param
|
|
868
|
+
/// @param resources Chain resources assigned to the step.
|
|
869
869
|
/// @param request Raw nested request payload.
|
|
870
|
-
function appendStep(Writer memory writer, uint target, uint
|
|
871
|
-
appendBlock64Bytes(writer, Keys.Step, bytes32(target), bytes32(
|
|
870
|
+
function appendStep(Writer memory writer, uint target, uint resources, bytes memory request) internal pure {
|
|
871
|
+
appendBlock64Bytes(writer, Keys.Step, bytes32(target), bytes32(resources), request);
|
|
872
872
|
}
|
|
873
873
|
|
|
874
874
|
/// @notice Append a CALL block with a nested payload BYTES block.
|
|
875
875
|
/// @param writer Destination writer; `i` is advanced by the encoded CALL block length.
|
|
876
876
|
/// @param target Call target identifier.
|
|
877
|
-
/// @param
|
|
877
|
+
/// @param resources Chain resources assigned to the call.
|
|
878
878
|
/// @param data Raw nested call payload.
|
|
879
|
-
function appendCall(Writer memory writer, uint target, uint
|
|
880
|
-
appendBlock64Bytes(writer, Keys.Call, bytes32(target), bytes32(
|
|
879
|
+
function appendCall(Writer memory writer, uint target, uint resources, bytes memory data) internal pure {
|
|
880
|
+
appendBlock64Bytes(writer, Keys.Call, bytes32(target), bytes32(resources), data);
|
|
881
881
|
}
|
|
882
882
|
|
|
883
883
|
/// @notice Append a CONTEXT block with nested state/request BYTES payloads.
|
|
@@ -891,13 +891,13 @@ library Writers {
|
|
|
891
891
|
|
|
892
892
|
/// @notice Append a PIPE block with a nested CONTEXT block.
|
|
893
893
|
/// @param writer Destination writer; `i` is advanced by the encoded PIPE block length.
|
|
894
|
-
/// @param
|
|
894
|
+
/// @param resources Chain resources assigned to the pipe.
|
|
895
895
|
/// @param account Command account identifier.
|
|
896
896
|
/// @param state Raw nested state payload.
|
|
897
897
|
/// @param steps Raw nested step payload.
|
|
898
898
|
function appendPipe(
|
|
899
899
|
Writer memory writer,
|
|
900
|
-
uint
|
|
900
|
+
uint resources,
|
|
901
901
|
bytes32 account,
|
|
902
902
|
bytes memory state,
|
|
903
903
|
bytes memory steps
|
|
@@ -909,7 +909,7 @@ library Writers {
|
|
|
909
909
|
|
|
910
910
|
uint p = writeHeader(writer.dst, i, Keys.Pipe, uint32(max32(len)));
|
|
911
911
|
assembly ("memory-safe") {
|
|
912
|
-
mstore(add(p, 0x08),
|
|
912
|
+
mstore(add(p, 0x08), resources)
|
|
913
913
|
}
|
|
914
914
|
|
|
915
915
|
writeBlock32BytesBytes(writer.dst, i + Sizes.Header + 32, Keys.Context, account, state, steps);
|
package/commands/Relay.sol
CHANGED
|
@@ -8,25 +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
|
|
11
|
+
abstract contract DispatchPayableHook {
|
|
12
|
+
/// @notice Override to dispatch an encoded payload to `chain`.
|
|
13
13
|
/// @param chain Destination chain node ID.
|
|
14
|
-
/// @param
|
|
15
|
-
///
|
|
16
|
-
/// the
|
|
17
|
-
/// @param account Command account identifier.
|
|
18
|
-
/// @param state Current command state block stream.
|
|
19
|
-
/// @param steps Embedded destination step block stream.
|
|
14
|
+
/// @param resources Chain-adapter-specific destination resources. EVM adapters
|
|
15
|
+
/// may interpret this as packed execution gas and destination value.
|
|
16
|
+
/// @param payload Encoded payload ready for the transport layer.
|
|
20
17
|
/// @param budget Source-chain native-value budget available for transport
|
|
21
|
-
/// fees and destination
|
|
22
|
-
function
|
|
23
|
-
uint chain,
|
|
24
|
-
uint endowment,
|
|
25
|
-
bytes32 account,
|
|
26
|
-
bytes calldata state,
|
|
27
|
-
bytes calldata steps,
|
|
28
|
-
Budget memory budget
|
|
29
|
-
) internal virtual;
|
|
18
|
+
/// fees and destination resource funding.
|
|
19
|
+
function dispatch(uint chain, uint resources, bytes memory payload, Budget memory budget) internal virtual;
|
|
30
20
|
}
|
|
31
21
|
|
|
32
22
|
/// @title RelayPayable
|
|
@@ -34,7 +24,7 @@ abstract contract RelayPayableHook {
|
|
|
34
24
|
/// Reverts unless the request contains exactly one RELAY block, preventing
|
|
35
25
|
/// the same state from being duplicated across multiple relays.
|
|
36
26
|
/// Produces no output state.
|
|
37
|
-
abstract contract RelayPayable is CommandBase, Payable,
|
|
27
|
+
abstract contract RelayPayable is CommandBase, Payable, DispatchPayableHook {
|
|
38
28
|
string private constant NAME = "relayPayable";
|
|
39
29
|
|
|
40
30
|
uint internal immutable relayPayableId = commandId(NAME);
|
|
@@ -50,8 +40,10 @@ abstract contract RelayPayable is CommandBase, Payable, RelayPayableHook {
|
|
|
50
40
|
(Cur memory request, ) = Cursors.init(c.request, 0, 1, 1);
|
|
51
41
|
Budget memory budget = valueBudget();
|
|
52
42
|
|
|
53
|
-
(uint chain, uint
|
|
54
|
-
|
|
43
|
+
(uint chain, uint resources, bytes memory pipe) = request.relayToPipe(c.account, c.state);
|
|
44
|
+
dispatch(chain, resources, pipe, budget);
|
|
45
|
+
|
|
46
|
+
settleValue(c.account, budget);
|
|
55
47
|
request.complete();
|
|
56
48
|
return "";
|
|
57
49
|
}
|
|
@@ -12,7 +12,7 @@ using Cursors for Cur;
|
|
|
12
12
|
|
|
13
13
|
/// @title ExecutePayable
|
|
14
14
|
/// @notice Admin command that forwards raw calldata to one or more target nodes.
|
|
15
|
-
/// Each CALL block specifies a target node ID,
|
|
15
|
+
/// Each CALL block specifies a target node ID, chain resources, and raw calldata payload.
|
|
16
16
|
/// Only callable by the admin account.
|
|
17
17
|
/// Unspent top-level `msg.value` remains on this host.
|
|
18
18
|
abstract contract ExecutePayable is CommandBase, Payable, AdminEvent {
|
|
@@ -32,9 +32,9 @@ abstract contract ExecutePayable is CommandBase, Payable, AdminEvent {
|
|
|
32
32
|
Budget memory budget = valueBudget();
|
|
33
33
|
|
|
34
34
|
while (request.i < request.len) {
|
|
35
|
-
(uint target, uint
|
|
35
|
+
(uint target, uint resources, bytes calldata data) = request.unpackCall();
|
|
36
36
|
address addr = Ids.nodeAddr(target);
|
|
37
|
-
callAddr(addr, useValue(budget,
|
|
37
|
+
callAddr(addr, useValue(budget, resources), data);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
request.complete();
|
package/core/Calls.sol
CHANGED
|
@@ -28,7 +28,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
28
28
|
/// @param value Native value to forward in wei.
|
|
29
29
|
/// @param data Encoded calldata to send.
|
|
30
30
|
/// @return out Return data from the successful call.
|
|
31
|
-
function callAddr(address addr,
|
|
31
|
+
function callAddr(address addr, uint128 value, bytes memory data) internal returns (bytes memory out) {
|
|
32
32
|
bool success;
|
|
33
33
|
(success, out) = payable(addr).call{value: value}(data);
|
|
34
34
|
if (!success) revert FailedCall(addr, bytes4(data), out);
|
|
@@ -53,7 +53,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
53
53
|
/// @param value Native value to forward in wei.
|
|
54
54
|
/// @param data Encoded calldata to send.
|
|
55
55
|
/// @return out Return data from the successful call.
|
|
56
|
-
function callTo(uint node,
|
|
56
|
+
function callTo(uint node, uint128 value, bytes memory data) internal returns (bytes memory out) {
|
|
57
57
|
ensureTrusted(node);
|
|
58
58
|
address addr = Ids.nodeAddr(node);
|
|
59
59
|
return callAddr(addr, value, data);
|
|
@@ -76,7 +76,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
76
76
|
/// @param value Native value to forward in wei.
|
|
77
77
|
/// @param ctx Command execution context.
|
|
78
78
|
/// @return Decoded command output block stream.
|
|
79
|
-
function callCommand(uint id,
|
|
79
|
+
function callCommand(uint id, uint128 value, CommandContext memory ctx) internal returns (bytes memory) {
|
|
80
80
|
bytes4 selector = Ids.commandSelector(id);
|
|
81
81
|
bytes memory data = abi.encodeWithSelector(selector, ctx);
|
|
82
82
|
return abi.decode(callTo(id, value, data), (bytes));
|
package/core/Payable.sol
CHANGED
|
@@ -18,20 +18,22 @@ abstract contract Payable {
|
|
|
18
18
|
return Budget({remaining: msg.value});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/// @notice Deduct
|
|
21
|
+
/// @notice Deduct the EVM value lane from a packed resource word and return it.
|
|
22
|
+
/// @dev EVM resources use the low 128 bits as native value/endowment.
|
|
22
23
|
/// @param budget Mutable budget to deduct from.
|
|
23
|
-
/// @param
|
|
24
|
-
/// @return
|
|
25
|
-
function useValue(Budget memory budget, uint
|
|
26
|
-
return Values.use(budget,
|
|
24
|
+
/// @param resources Packed chain resources.
|
|
25
|
+
/// @return value Native value to forward in wei.
|
|
26
|
+
function useValue(Budget memory budget, uint resources) internal pure returns (uint128 value) {
|
|
27
|
+
return Values.use(budget, uint128(resources));
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/// @notice Deduct
|
|
30
|
+
/// @notice Deduct the EVM value lane from a packed resource word as a new sub-budget.
|
|
31
|
+
/// @dev EVM resources use the low 128 bits as native value/endowment.
|
|
30
32
|
/// @param budget Mutable parent budget to deduct from.
|
|
31
|
-
/// @param
|
|
32
|
-
/// @return A new budget with
|
|
33
|
-
function allocateValue(Budget memory budget, uint
|
|
34
|
-
return Values.allocate(budget,
|
|
33
|
+
/// @param resources Packed chain resources.
|
|
34
|
+
/// @return A new budget with the EVM value lane remaining.
|
|
35
|
+
function allocateValue(Budget memory budget, uint resources) internal pure returns (Budget memory) {
|
|
36
|
+
return Values.allocate(budget, uint128(resources));
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/// @notice Drains the budget and settles any remaining native value.
|
package/core/Pipeline.sol
CHANGED
|
@@ -20,14 +20,14 @@ abstract contract Pipeline is Payable {
|
|
|
20
20
|
/// @param account Account identifier for the piped context.
|
|
21
21
|
/// @param state Current threaded state block stream.
|
|
22
22
|
/// @param request Step request block stream.
|
|
23
|
-
/// @param value Native value assigned to this step.
|
|
23
|
+
/// @param value Native EVM value assigned to this step.
|
|
24
24
|
/// @return Updated state block stream for the next step.
|
|
25
25
|
function dispatch(
|
|
26
26
|
uint target,
|
|
27
27
|
bytes32 account,
|
|
28
28
|
bytes memory state,
|
|
29
29
|
bytes calldata request,
|
|
30
|
-
|
|
30
|
+
uint128 value
|
|
31
31
|
) internal virtual returns (bytes memory);
|
|
32
32
|
|
|
33
33
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
@@ -45,8 +45,8 @@ abstract contract Pipeline is Payable {
|
|
|
45
45
|
(Cur memory input, , ) = Cursors.init(steps, 0, 1);
|
|
46
46
|
|
|
47
47
|
while (input.i < input.len) {
|
|
48
|
-
(uint target, uint
|
|
49
|
-
state = dispatch(target, account, state, request, useValue(budget,
|
|
48
|
+
(uint target, uint resources, bytes calldata request) = input.unpackStep();
|
|
49
|
+
state = dispatch(target, account, state, request, useValue(budget, resources));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (state.length != 0) revert UnexpectedState();
|
package/docs/Schema.md
CHANGED
|
@@ -56,9 +56,9 @@ A block payload has fixed fields first, followed by an optional child-block tail
|
|
|
56
56
|
Once a child block appears, no more fixed fields may follow.
|
|
57
57
|
|
|
58
58
|
```txt
|
|
59
|
-
#call { uint target, uint
|
|
59
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
60
60
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
|
61
|
-
#pipe { uint
|
|
61
|
+
#pipe { uint resources, #context { bytes32 account, #bytes as state, #bytes as steps } }
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The tail is embedded directly as child block bytes. There is no wrapper around a
|
|
@@ -108,7 +108,7 @@ or runtime keys.
|
|
|
108
108
|
|
|
109
109
|
```txt
|
|
110
110
|
maybe #account { bytes32 account } as recipient
|
|
111
|
-
#call { uint target, uint
|
|
111
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
Aliases may be used on any block item, including child blocks and prime items.
|
|
@@ -131,6 +131,13 @@ Integers are encoded big-endian. Signed integers use two's-complement encoding
|
|
|
131
131
|
for their declared width. `bool` is one byte: `0x00` for false and `0x01` for
|
|
132
132
|
true. `bytesN` values are encoded as exactly `N` bytes with no padding.
|
|
133
133
|
|
|
134
|
+
## Chain Resources
|
|
135
|
+
|
|
136
|
+
Fields named `resources` are chain-specific resource words. Different chain
|
|
137
|
+
types may pack these words differently, but a given chain type must use one
|
|
138
|
+
stable format everywhere. For EVM chains, the low 128 bits are native value /
|
|
139
|
+
endowment in wei; higher bits are reserved for execution resources such as gas.
|
|
140
|
+
|
|
134
141
|
## Identifiers
|
|
135
142
|
|
|
136
143
|
Block names, field names, and aliases use lower camelCase ASCII identifiers:
|
|
@@ -185,10 +192,10 @@ Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
|
185
192
|
#amount { bytes32 asset, bytes32 meta, uint amount }
|
|
186
193
|
#balance { bytes32 asset, bytes32 meta, uint amount }
|
|
187
194
|
#custody { uint host, bytes32 asset, bytes32 meta, uint amount }
|
|
188
|
-
#call { uint target, uint
|
|
189
|
-
#step { uint target, uint
|
|
195
|
+
#call { uint target, uint resources, #bytes as payload }
|
|
196
|
+
#step { uint target, uint resources, #bytes as request }
|
|
190
197
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
|
191
|
-
#pipe { uint
|
|
198
|
+
#pipe { uint resources, #context { bytes32 account, #bytes as state, #bytes as steps } }
|
|
192
199
|
#auth { uint cid, uint deadline, #bytes as proof }
|
|
193
200
|
```
|
|
194
201
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootzero/contracts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"**/*.sol",
|
|
10
10
|
"README.md",
|
|
11
|
+
"CHANGELOG.md",
|
|
11
12
|
"LICENSE",
|
|
12
13
|
"docs/Schema.md"
|
|
13
14
|
],
|
package/peer/Dispatch.sol
CHANGED
|
@@ -4,25 +4,14 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
import { PeerBase } from "./Base.sol";
|
|
5
5
|
import { Payable } from "../core/Payable.sol";
|
|
6
6
|
import { Cursors, Cur, Schemas } from "../Cursors.sol";
|
|
7
|
+
import { DispatchPayableHook } from "../commands/Relay.sol";
|
|
7
8
|
import { Budget } from "../utils/Value.sol";
|
|
8
9
|
|
|
9
10
|
using Cursors for Cur;
|
|
10
11
|
|
|
11
|
-
abstract contract PeerDispatchPayableHook {
|
|
12
|
-
/// @notice Override to dispatch an already encoded payload to `chain`.
|
|
13
|
-
/// @param chain Destination chain node ID.
|
|
14
|
-
/// @param endowment Native value requested for the destination dispatch. The
|
|
15
|
-
/// hook decides how much source-chain budget must be spent to fund this
|
|
16
|
-
/// value on the destination chain.
|
|
17
|
-
/// @param payload Encoded payload ready for the transport layer.
|
|
18
|
-
/// @param budget Source-chain native-value budget available for transport
|
|
19
|
-
/// fees and destination endowment funding.
|
|
20
|
-
function dispatch(uint chain, uint endowment, bytes calldata payload, Budget memory budget) internal virtual;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
12
|
/// @title PeerDispatchPayable
|
|
24
13
|
/// @notice Peer endpoint that forwards DISPATCH blocks to a host-defined dispatch hook.
|
|
25
|
-
abstract contract PeerDispatchPayable is PeerBase, Payable,
|
|
14
|
+
abstract contract PeerDispatchPayable is PeerBase, Payable, DispatchPayableHook {
|
|
26
15
|
string private constant NAME = "peerDispatchPayable";
|
|
27
16
|
uint internal immutable peerDispatchPayableId = peerId(NAME);
|
|
28
17
|
|
|
@@ -40,8 +29,8 @@ abstract contract PeerDispatchPayable is PeerBase, Payable, PeerDispatchPayableH
|
|
|
40
29
|
Budget memory budget = valueBudget();
|
|
41
30
|
|
|
42
31
|
while (input.i < input.len) {
|
|
43
|
-
(uint chain, uint
|
|
44
|
-
dispatch(chain,
|
|
32
|
+
(uint chain, uint resources, bytes calldata payload) = input.unpackDispatch();
|
|
33
|
+
dispatch(chain, resources, bytes(payload), budget);
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
input.complete();
|
package/peer/Pipe.sol
CHANGED
|
@@ -10,7 +10,7 @@ using Cursors for Cur;
|
|
|
10
10
|
|
|
11
11
|
/// @title PeerPipePayable
|
|
12
12
|
/// @notice Peer that consumes PIPE blocks and executes each context step stream.
|
|
13
|
-
/// Each PIPE block carries
|
|
13
|
+
/// Each PIPE block carries chain resources plus a CONTEXT block; the nested
|
|
14
14
|
/// context steps are passed to the shared pipeline as the step stream.
|
|
15
15
|
abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
16
16
|
string private constant NAME = "peerPipePayable";
|
|
@@ -21,7 +21,7 @@ abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/// @notice Execute peer-supplied pipes through the shared payable pipe.
|
|
24
|
-
/// @dev Each pipe receives its own explicit value sub-budget. Any top-level
|
|
24
|
+
/// @dev Each pipe receives its own explicit EVM value sub-budget. Any top-level
|
|
25
25
|
/// `msg.value` not assigned to a pipe remains on this host.
|
|
26
26
|
/// @param request PIPE block stream supplied by the trusted peer.
|
|
27
27
|
/// @return Empty response bytes.
|
|
@@ -30,8 +30,8 @@ abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
|
30
30
|
Budget memory budget = valueBudget();
|
|
31
31
|
|
|
32
32
|
while (input.i < input.len) {
|
|
33
|
-
(uint
|
|
34
|
-
pipe(account, state, steps, allocateValue(budget,
|
|
33
|
+
(uint resources, bytes32 account, bytes calldata state, bytes calldata steps) = input.unpackPipe();
|
|
34
|
+
pipe(account, state, steps, allocateValue(budget, resources));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
input.complete();
|
package/utils/Value.sol
CHANGED
|
@@ -18,9 +18,10 @@ library Values {
|
|
|
18
18
|
/// @param budget Mutable budget to deduct from.
|
|
19
19
|
/// @param amount Native value to spend in wei.
|
|
20
20
|
/// @return The same `amount`, ready to forward to a callee.
|
|
21
|
-
function use(Budget memory budget,
|
|
22
|
-
|
|
23
|
-
budget.remaining
|
|
21
|
+
function use(Budget memory budget, uint128 amount) internal pure returns (uint128) {
|
|
22
|
+
uint value = uint(amount);
|
|
23
|
+
if (value > budget.remaining) revert InsufficientValue();
|
|
24
|
+
budget.remaining -= value;
|
|
24
25
|
return amount;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -29,7 +30,7 @@ library Values {
|
|
|
29
30
|
/// @param budget Mutable parent budget to deduct from.
|
|
30
31
|
/// @param amount Native value to assign to the sub-budget, in wei.
|
|
31
32
|
/// @return A new budget with `amount` remaining.
|
|
32
|
-
function allocate(Budget memory budget,
|
|
33
|
+
function allocate(Budget memory budget, uint128 amount) internal pure returns (Budget memory) {
|
|
33
34
|
return Budget({remaining: use(budget, amount)});
|
|
34
35
|
}
|
|
35
36
|
}
|