@rootzero/contracts 1.29.0 → 1.30.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 +64 -0
- package/Codec.sol +2 -1
- package/Commands.sol +1 -1
- package/Core.sol +1 -1
- package/Endpoints.sol +2 -2
- package/README.md +90 -43
- package/Utils.sol +2 -1
- package/codec/Blocks.sol +41 -67
- package/codec/Decoders.sol +12 -20
- package/codec/Descriptors.sol +3 -14
- package/codec/Keys.sol +1 -3
- package/codec/Schema.sol +1 -3
- package/codec/Specs.sol +2 -4
- package/codec/Writers.sol +8 -9
- package/commands/Base.sol +3 -2
- package/commands/Cashout.sol +18 -16
- package/commands/Relay.sol +14 -15
- package/commands/admin/Execute.sol +2 -2
- package/core/Access.sol +14 -12
- package/core/Calls.sol +90 -102
- package/core/Host.sol +13 -11
- package/core/Pipeline.sol +173 -19
- package/core/Settlement.sol +1 -1
- package/execution/Execution.sol +25 -37
- package/package.json +1 -1
- package/ports/Allowance.sol +7 -17
- package/ports/Base.sol +4 -1
- package/ports/Dispatch.sol +1 -1
- package/ports/Pipe.sol +1 -1
- package/queries/Base.sol +1 -1
- package/utils/Accounts.sol +4 -4
- package/utils/Assets.sol +4 -5
- package/utils/Cursors.sol +24 -1
- package/utils/Flags.sol +18 -0
- package/utils/Layout.sol +3 -3
- package/utils/Nodes.sol +36 -31
- package/utils/Utils.sol +5 -5
package/core/Pipeline.sol
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {Blocks} from "../codec/Blocks.sol";
|
|
5
|
+
import {Keys} from "../codec/Keys.sol";
|
|
5
6
|
import {TrustAccess} from "./Access.sol";
|
|
6
|
-
import {rawCommandCall} from "./Calls.sol";
|
|
7
|
-
import {Cursors} from "../utils/Cursors.sol";
|
|
8
7
|
import {InsufficientValue, OutOfBounds, UnexpectedState} from "../utils/Errors.sol";
|
|
9
|
-
import {
|
|
8
|
+
import {Nodes} from "../utils/Nodes.sol";
|
|
9
|
+
import {Flags} from "../utils/Flags.sol";
|
|
10
10
|
|
|
11
11
|
/// @notice Hook implemented by hosts that execute encoded step streams.
|
|
12
12
|
abstract contract PipeHook {
|
|
@@ -24,7 +24,9 @@ abstract contract ExecuteHook {
|
|
|
24
24
|
/// @notice Try to execute one command whose node ID targets the current host.
|
|
25
25
|
/// @dev Implementations returning `handled = true` are responsible for
|
|
26
26
|
/// authorizing the command. Return false without side effects to delegate to
|
|
27
|
-
/// the trusted normal external entrypoint.
|
|
27
|
+
/// the trusted normal external entrypoint. Handoff commands must be delegated
|
|
28
|
+
/// because this hook receives ordinary input without the continuation that
|
|
29
|
+
/// Pipeline adds to the RELAY envelope. Implementations may revert instead.
|
|
28
30
|
function execute(
|
|
29
31
|
uint cmd,
|
|
30
32
|
bytes32 account,
|
|
@@ -36,22 +38,171 @@ abstract contract ExecuteHook {
|
|
|
36
38
|
|
|
37
39
|
/// @title Pipeline
|
|
38
40
|
/// @notice Core pipeline functionality shared by higher-level surfaces.
|
|
41
|
+
/// @dev This gas-sensitive implementation intentionally inlines the command ID
|
|
42
|
+
/// layout and the CONTEXT, BYTES, and RELAY block encodings. Changes to command
|
|
43
|
+
/// selector, target, or flag placement, or to those block layouts, must update
|
|
44
|
+
/// the corresponding assembly and packed-cursor logic here.
|
|
39
45
|
abstract contract Pipeline is TrustAccess, PipeHook, ExecuteHook {
|
|
40
|
-
|
|
46
|
+
/// @dev Private pipeline cursor layout:
|
|
47
|
+
/// bits 0-31 current STEP offset, 32-63 stream end,
|
|
48
|
+
/// 64-95 command-input offset, 96-127 command-input length.
|
|
49
|
+
function unpackBytes(uint abs) private pure returns (uint input, uint end) {
|
|
50
|
+
uint body;
|
|
51
|
+
(body, end) = Blocks.expectKey(abs, Keys.Bytes);
|
|
52
|
+
// `expectKey` constructs `end` as `body + uint32(length)`.
|
|
53
|
+
unchecked {
|
|
54
|
+
input = uint32(body) | ((end - body) << 32);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function takeStep(uint cursor) private pure returns (uint cmd, uint value, uint updated) {
|
|
59
|
+
uint abs = uint32(cursor);
|
|
60
|
+
uint limit;
|
|
61
|
+
(abs, limit) = Blocks.expectKey(abs, Keys.Step);
|
|
62
|
+
assembly ("memory-safe") {
|
|
63
|
+
cmd := calldataload(abs)
|
|
64
|
+
value := calldataload(add(abs, 0x20))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
(uint input, uint end) = unpackBytes(abs + 64);
|
|
68
|
+
if (end != limit) revert Blocks.InvalidBlock();
|
|
69
|
+
if (end > uint32(cursor >> 32)) revert OutOfBounds();
|
|
70
|
+
|
|
71
|
+
updated = uint32(end) | (uint64(cursor) & (uint(type(uint32).max) << 32)) | (input << 64);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function rawInput(uint cursor) private pure returns (bytes calldata input) {
|
|
75
|
+
assembly ("memory-safe") {
|
|
76
|
+
input.offset := and(shr(64, cursor), 0xffffffff)
|
|
77
|
+
input.length := and(shr(96, cursor), 0xffffffff)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// @dev Execute the prepared command call and strictly decode `(bytes, uint)`.
|
|
82
|
+
function invokeCommand(
|
|
41
83
|
uint cmd,
|
|
84
|
+
uint value,
|
|
42
85
|
bytes32 account,
|
|
43
86
|
bytes memory state,
|
|
44
|
-
|
|
45
|
-
uint value
|
|
87
|
+
uint input
|
|
46
88
|
) private returns (bytes memory output, uint credit) {
|
|
47
|
-
(
|
|
48
|
-
|
|
89
|
+
assembly ("memory-safe") {
|
|
90
|
+
let scratch := mload(0x40)
|
|
91
|
+
let ctxlen
|
|
92
|
+
switch iszero(shr(64, input))
|
|
93
|
+
case 1 {
|
|
94
|
+
let statelen := mload(state)
|
|
95
|
+
let inputlen := and(shr(32, input), 0xffffffff)
|
|
96
|
+
ctxlen := add(56, add(statelen, inputlen))
|
|
97
|
+
|
|
98
|
+
mstore(scratch, shl(224, and(shr(160, cmd), 0xffffffff)))
|
|
99
|
+
mstore(add(scratch, 0x04), 0x20)
|
|
100
|
+
mstore(add(scratch, 0x24), ctxlen)
|
|
101
|
+
|
|
102
|
+
let p := add(scratch, 0x44)
|
|
103
|
+
mstore(p, or(shl(224, 0xc5769e23), shl(192, sub(ctxlen, 8))))
|
|
104
|
+
mstore(add(p, 0x08), account)
|
|
105
|
+
p := add(p, 0x28)
|
|
106
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, statelen)))
|
|
107
|
+
mcopy(add(p, 0x08), add(state, 0x20), statelen)
|
|
108
|
+
p := add(add(p, 0x08), statelen)
|
|
109
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, inputlen)))
|
|
110
|
+
calldatacopy(add(p, 0x08), and(input, 0xffffffff), inputlen)
|
|
111
|
+
}
|
|
112
|
+
default {
|
|
113
|
+
let statelen := mload(state)
|
|
114
|
+
let inputlen := and(shr(96, input), 0xffffffff)
|
|
115
|
+
let stepslen := sub(and(shr(32, input), 0xffffffff), and(input, 0xffffffff))
|
|
116
|
+
let relaylen := add(16, add(inputlen, stepslen))
|
|
117
|
+
ctxlen := add(64, add(statelen, relaylen))
|
|
118
|
+
|
|
119
|
+
mstore(scratch, shl(224, and(shr(160, cmd), 0xffffffff)))
|
|
120
|
+
mstore(add(scratch, 0x04), 0x20)
|
|
121
|
+
mstore(add(scratch, 0x24), ctxlen)
|
|
122
|
+
|
|
123
|
+
let p := add(scratch, 0x44)
|
|
124
|
+
mstore(p, or(shl(224, 0xc5769e23), shl(192, sub(ctxlen, 8))))
|
|
125
|
+
mstore(add(p, 0x08), account)
|
|
126
|
+
p := add(p, 0x28)
|
|
127
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, statelen)))
|
|
128
|
+
mcopy(add(p, 0x08), add(state, 0x20), statelen)
|
|
129
|
+
|
|
130
|
+
p := add(add(p, 0x08), statelen)
|
|
131
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, add(8, relaylen))))
|
|
132
|
+
p := add(p, 0x08)
|
|
133
|
+
mstore(p, or(shl(224, 0xc34cc52a), shl(192, relaylen)))
|
|
134
|
+
p := add(p, 0x08)
|
|
135
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, inputlen)))
|
|
136
|
+
calldatacopy(add(p, 0x08), and(shr(64, input), 0xffffffff), inputlen)
|
|
137
|
+
p := add(add(p, 0x08), inputlen)
|
|
138
|
+
mstore(p, or(shl(224, 0x6911b332), shl(192, stepslen)))
|
|
139
|
+
calldatacopy(add(p, 0x08), and(input, 0xffffffff), stepslen)
|
|
140
|
+
}
|
|
141
|
+
ctxlen := add(68, and(add(ctxlen, 0x1f), not(0x1f)))
|
|
142
|
+
let inputend := and(add(add(scratch, ctxlen), 0x1f), not(0x1f))
|
|
143
|
+
let success := call(
|
|
144
|
+
gas(),
|
|
145
|
+
and(cmd, 0xffffffffffffffffffffffffffffffffffffffff),
|
|
146
|
+
value,
|
|
147
|
+
scratch,
|
|
148
|
+
ctxlen,
|
|
149
|
+
0,
|
|
150
|
+
0
|
|
151
|
+
)
|
|
152
|
+
let retlen := returndatasize()
|
|
153
|
+
if iszero(success) {
|
|
154
|
+
mstore(scratch, shl(224, 0x20577b07))
|
|
155
|
+
mstore(add(scratch, 0x04), and(cmd, 0xffffffffffffffffffffffffffffffffffffffff))
|
|
156
|
+
mstore(add(scratch, 0x24), shl(224, and(shr(160, cmd), 0xffffffff)))
|
|
157
|
+
mstore(add(scratch, 0x44), 0x60)
|
|
158
|
+
mstore(add(scratch, 0x64), retlen)
|
|
159
|
+
mstore(add(add(scratch, 0x84), retlen), 0)
|
|
160
|
+
returndatacopy(add(scratch, 0x84), 0, retlen)
|
|
161
|
+
revert(scratch, add(0x84, and(add(retlen, 0x1f), not(0x1f))))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if lt(retlen, 0x60) {
|
|
165
|
+
revert(0, 0)
|
|
166
|
+
}
|
|
167
|
+
returndatacopy(scratch, 0, retlen)
|
|
168
|
+
if iszero(eq(mload(scratch), 0x40)) {
|
|
169
|
+
revert(0, 0)
|
|
170
|
+
}
|
|
171
|
+
let len1 := mload(add(scratch, 0x40))
|
|
172
|
+
if gt(len1, sub(retlen, 0x60)) {
|
|
173
|
+
revert(0, 0)
|
|
174
|
+
}
|
|
175
|
+
let pad1 := and(add(len1, 0x1f), not(0x1f))
|
|
176
|
+
if iszero(eq(retlen, add(0x60, pad1))) {
|
|
177
|
+
revert(0, 0)
|
|
178
|
+
}
|
|
179
|
+
output := add(scratch, 0x40)
|
|
180
|
+
credit := mload(add(scratch, 0x20))
|
|
181
|
+
|
|
182
|
+
let retend := and(add(add(scratch, retlen), 0x1f), not(0x1f))
|
|
183
|
+
if gt(retend, inputend) {
|
|
184
|
+
inputend := retend
|
|
185
|
+
}
|
|
186
|
+
mstore(0x40, inputend)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function run(
|
|
191
|
+
uint cmd,
|
|
192
|
+
bytes32 account,
|
|
193
|
+
bytes memory state,
|
|
194
|
+
uint value,
|
|
195
|
+
uint cursor
|
|
196
|
+
) private returns (bytes memory output, uint credit, uint updated) {
|
|
197
|
+
if (address(uint160(cmd)) == address(this)) {
|
|
49
198
|
bool handled;
|
|
50
|
-
(handled, output, credit) = execute(cmd, account, state,
|
|
51
|
-
if (handled) return (output, credit);
|
|
199
|
+
(handled, output, credit) = execute(cmd, account, state, rawInput(cursor), value);
|
|
200
|
+
if (handled) return (output, credit, cursor);
|
|
52
201
|
}
|
|
53
|
-
ensureTrusted(cmd);
|
|
54
|
-
|
|
202
|
+
ensureTrusted(Nodes.command(cmd));
|
|
203
|
+
bool handoff = uint8(cmd >> 224) & Flags.Handoff != 0;
|
|
204
|
+
(output, credit) = invokeCommand(cmd, value, account, state, handoff ? cursor : cursor >> 64);
|
|
205
|
+
updated = handoff ? (cursor & ~uint(type(uint32).max)) | uint32(cursor >> 32) : cursor;
|
|
55
206
|
}
|
|
56
207
|
|
|
57
208
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
@@ -68,18 +219,21 @@ abstract contract Pipeline is TrustAccess, PipeHook, ExecuteHook {
|
|
|
68
219
|
bytes calldata steps,
|
|
69
220
|
uint budget
|
|
70
221
|
) internal virtual override returns (uint remaining) {
|
|
71
|
-
|
|
222
|
+
uint cursor;
|
|
223
|
+
assembly ("memory-safe") {
|
|
224
|
+
cursor := or(steps.offset, shl(32, add(steps.offset, steps.length)))
|
|
225
|
+
}
|
|
72
226
|
|
|
73
|
-
while (
|
|
74
|
-
|
|
75
|
-
|
|
227
|
+
while (uint32(cursor) < uint32(cursor >> 32)) {
|
|
228
|
+
uint cmd;
|
|
229
|
+
uint value;
|
|
230
|
+
(cmd, value, cursor) = takeStep(cursor);
|
|
76
231
|
if (value > budget) revert InsufficientValue();
|
|
77
232
|
unchecked {
|
|
78
233
|
budget -= value;
|
|
79
234
|
}
|
|
80
|
-
(state, value) = run(cmd, account, state,
|
|
235
|
+
(state, value, cursor) = run(cmd, account, state, value, cursor);
|
|
81
236
|
budget += value;
|
|
82
|
-
abs = next;
|
|
83
237
|
}
|
|
84
238
|
|
|
85
239
|
if (state.length != 0) revert UnexpectedState();
|
package/core/Settlement.sol
CHANGED
|
@@ -44,7 +44,7 @@ abstract contract SettleHook {
|
|
|
44
44
|
|
|
45
45
|
/// @title Settlement
|
|
46
46
|
/// @notice Default account-hook implementation for transaction posting and position settlement.
|
|
47
|
-
abstract contract Settlement is PostHook,
|
|
47
|
+
abstract contract Settlement is PostHook, DebitAccountHook, CreditAccountHook, SettleHook, RepayHook {
|
|
48
48
|
/// @notice Post one transaction by debiting its source and crediting its destination.
|
|
49
49
|
/// Returns without calling either hook when `amount` is zero and skips either
|
|
50
50
|
/// operation when the corresponding account is zero.
|
package/execution/Execution.sol
CHANGED
|
@@ -71,29 +71,29 @@ library Executions {
|
|
|
71
71
|
return exec.decoders.absolute();
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/// @dev Return the
|
|
75
|
-
///
|
|
76
|
-
/// absent because its descriptor is EMPTY returns an empty calldata slice.
|
|
74
|
+
/// @dev Return the unread bounded calldata source for a decoder lane.
|
|
75
|
+
/// A lane absent because its descriptor is EMPTY returns an empty calldata slice.
|
|
77
76
|
/// @param exec Execution whose input or state source is requested.
|
|
78
|
-
/// @return data
|
|
77
|
+
/// @return data Unread calldata region from the lane's current position.
|
|
79
78
|
function raw(Execution memory exec, uint8 lane) private pure returns (bytes calldata data) {
|
|
80
79
|
if (!exec.decoders.contains(lane)) return msg.data[0:0];
|
|
81
80
|
|
|
82
81
|
uint cur = exec.decoders.select(lane);
|
|
83
82
|
if (uint8(cur >> 96) == 0) return msg.data[0:0];
|
|
84
|
-
(uint
|
|
85
|
-
if (
|
|
86
|
-
|
|
83
|
+
(uint i, uint offset, uint len) = cur.decode();
|
|
84
|
+
if (len > msg.data.length || offset > msg.data.length - len) {
|
|
85
|
+
revert Blocks.MalformedBlocks();
|
|
86
|
+
}
|
|
87
|
+
return msg.data[offset + i:offset + len];
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
/// @notice Return the
|
|
90
|
-
/// @dev Remains the complete original state after the state cursor advances.
|
|
90
|
+
/// @notice Return the unread bounded command state without consuming it.
|
|
91
91
|
function rawState(Execution memory exec) internal pure returns (bytes calldata) {
|
|
92
92
|
return raw(exec, Lanes.State);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
/// @notice Return the
|
|
96
|
-
/// @dev Intended for commands that forward their state
|
|
95
|
+
/// @notice Return the unread state lane and mark it fully consumed.
|
|
96
|
+
/// @dev Intended for commands that forward their remaining state without decoding it.
|
|
97
97
|
/// A lane declared EMPTY is returned empty and remains unconsumed so close
|
|
98
98
|
/// still rejects any state supplied against the descriptor.
|
|
99
99
|
function takeRawState(Execution memory exec) internal pure returns (bytes calldata data) {
|
|
@@ -106,14 +106,13 @@ library Executions {
|
|
|
106
106
|
exec.decoders = cur.seek(len);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/// @notice Return the
|
|
110
|
-
/// @dev Remains the complete original input after the input cursor advances.
|
|
109
|
+
/// @notice Return the unread bounded endpoint input without consuming it.
|
|
111
110
|
function rawInput(Execution memory exec) internal pure returns (bytes calldata) {
|
|
112
111
|
return raw(exec, Lanes.Input);
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
/// @notice Return the
|
|
116
|
-
/// @dev Intended for endpoints that forward their input
|
|
114
|
+
/// @notice Return the unread input lane and mark it fully consumed.
|
|
115
|
+
/// @dev Intended for endpoints that forward their remaining input without decoding it.
|
|
117
116
|
/// A lane declared EMPTY is returned empty and remains unconsumed so close
|
|
118
117
|
/// still rejects any input supplied against the descriptor.
|
|
119
118
|
function takeRawInput(Execution memory exec) internal pure returns (bytes calldata data) {
|
|
@@ -330,15 +329,6 @@ library Executions {
|
|
|
330
329
|
node = Blocks.unpackNode(abs);
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
/// @notice Decode and consume one CASHOUT block from the active lane.
|
|
334
|
-
/// @param exec Execution whose decoder is advanced.
|
|
335
|
-
/// @return amount Native-asset amount to withdraw.
|
|
336
|
-
function unpackCashout(Execution memory exec) internal pure returns (uint amount) {
|
|
337
|
-
uint abs;
|
|
338
|
-
(exec.decoders, abs) = exec.decoders.consume(Sizes.Cashout);
|
|
339
|
-
amount = Blocks.unpackCashout(abs);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
332
|
/// @notice Decode and consume one BOOTSTRAP block from the active lane.
|
|
343
333
|
/// @param exec Execution whose decoder is advanced.
|
|
344
334
|
/// @return asset Decoded asset identifier.
|
|
@@ -664,13 +654,12 @@ library Executions {
|
|
|
664
654
|
|
|
665
655
|
/// @notice Decode and consume one RELAY block from the active lane.
|
|
666
656
|
/// @param exec Execution whose decoder is advanced.
|
|
667
|
-
/// @return portal Decoded destination portal.
|
|
668
|
-
/// @return resources Decoded packed resources.
|
|
669
657
|
/// @return input Decoded nested input.
|
|
670
|
-
|
|
658
|
+
/// @return steps Decoded remaining pipeline steps.
|
|
659
|
+
function unpackRelay(Execution memory exec) internal pure returns (bytes calldata input, bytes calldata steps) {
|
|
671
660
|
uint cur = exec.decoders;
|
|
672
661
|
uint end;
|
|
673
|
-
(
|
|
662
|
+
(input, steps, end) = Blocks.unpackRelay(cur.absolute());
|
|
674
663
|
exec.decoders = cur.seekAbs(end);
|
|
675
664
|
}
|
|
676
665
|
|
|
@@ -1048,13 +1037,12 @@ library Executions {
|
|
|
1048
1037
|
|
|
1049
1038
|
/// @notice Append a RELAY block to execution output.
|
|
1050
1039
|
/// @param exec Execution receiving the block.
|
|
1051
|
-
/// @param portal Destination portal to encode.
|
|
1052
|
-
/// @param resources Packed resources to encode.
|
|
1053
1040
|
/// @param input Relay input to encode.
|
|
1054
|
-
|
|
1055
|
-
|
|
1041
|
+
/// @param steps Remaining pipeline steps to encode.
|
|
1042
|
+
function outputRelay(Execution memory exec, bytes memory input, bytes memory steps) internal pure {
|
|
1043
|
+
uint size = 3 * Sizes.Header + input.length + steps.length;
|
|
1056
1044
|
uint i = reserve(exec, size);
|
|
1057
|
-
Blocks.writeRelay(exec.output, i,
|
|
1045
|
+
Blocks.writeRelay(exec.output, i, input, steps);
|
|
1058
1046
|
}
|
|
1059
1047
|
|
|
1060
1048
|
/// @notice Append a DISPATCH block to execution output.
|
|
@@ -1177,11 +1165,11 @@ library Executions {
|
|
|
1177
1165
|
Blocks.copyCall(exec.output, i, target, resources, payload);
|
|
1178
1166
|
}
|
|
1179
1167
|
|
|
1180
|
-
/// @notice Append a RELAY block to execution output by copying its nested
|
|
1181
|
-
function outputCopyRelay(Execution memory exec,
|
|
1182
|
-
uint size =
|
|
1168
|
+
/// @notice Append a RELAY block to execution output by copying its nested streams from calldata.
|
|
1169
|
+
function outputCopyRelay(Execution memory exec, bytes calldata input, bytes calldata steps) internal pure {
|
|
1170
|
+
uint size = 3 * Sizes.Header + input.length + steps.length;
|
|
1183
1171
|
uint i = reserve(exec, size);
|
|
1184
|
-
Blocks.copyRelay(exec.output, i,
|
|
1172
|
+
Blocks.copyRelay(exec.output, i, input, steps);
|
|
1185
1173
|
}
|
|
1186
1174
|
|
|
1187
1175
|
/// @notice Append a DISPATCH block to execution output by copying its nested payload from calldata.
|
package/package.json
CHANGED
package/ports/Allowance.sol
CHANGED
|
@@ -2,34 +2,24 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
+
import {AllowanceHook} from "../commands/admin/Allowance.sol";
|
|
5
6
|
import {Specs} from "../Codec.sol";
|
|
6
7
|
import {Execution, Executions} from "../execution/Execution.sol";
|
|
7
8
|
|
|
8
9
|
using Executions for Execution;
|
|
9
10
|
|
|
10
|
-
/// @notice Hook implemented by hosts that handle allowance requests from peers.
|
|
11
|
-
abstract contract RequestAllowanceHook {
|
|
12
|
-
/// @notice Override to handle one allowance request from a peer host.
|
|
13
|
-
/// @dev The implementation is responsible for validating the request and
|
|
14
|
-
/// deciding what allowance, if any, to grant.
|
|
15
|
-
/// @param peer Peer host node ID requesting the allowance.
|
|
16
|
-
/// @param asset Asset identifier supplied by the peer.
|
|
17
|
-
/// @param amount Allowance amount requested in the asset's native units.
|
|
18
|
-
function requestAllowance(uint peer, bytes32 asset, uint amount) internal virtual;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
11
|
/// @title RequestAllowancePort
|
|
22
|
-
/// @notice Port that lets trusted peers
|
|
23
|
-
/// Each AMOUNT block is scoped to the caller and
|
|
24
|
-
///
|
|
25
|
-
abstract contract RequestAllowancePort is PortBase,
|
|
12
|
+
/// @notice Port that lets trusted peers set their own asset allowances.
|
|
13
|
+
/// Each AMOUNT block is scoped to the authenticated caller and applied through
|
|
14
|
+
/// the shared authoritative allowance hook.
|
|
15
|
+
abstract contract RequestAllowancePort is PortBase, AllowanceHook {
|
|
26
16
|
uint private immutable descriptor;
|
|
27
17
|
|
|
28
18
|
constructor() {
|
|
29
19
|
(, descriptor) = port("portRequestAllowance", Specs.Amount, Specs.Empty, 0);
|
|
30
20
|
}
|
|
31
21
|
|
|
32
|
-
/// @notice
|
|
22
|
+
/// @notice Set asset allowances for the calling peer.
|
|
33
23
|
/// @param data AMOUNT block stream supplied by the trusted peer.
|
|
34
24
|
/// @return Empty response bytes.
|
|
35
25
|
function portRequestAllowance(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
@@ -38,7 +28,7 @@ abstract contract RequestAllowancePort is PortBase, RequestAllowanceHook {
|
|
|
38
28
|
|
|
39
29
|
while (exec.more()) {
|
|
40
30
|
(bytes32 asset, uint amount) = exec.unpackAmount();
|
|
41
|
-
|
|
31
|
+
allowance(peer, asset, amount);
|
|
42
32
|
}
|
|
43
33
|
|
|
44
34
|
return "";
|
package/ports/Base.sol
CHANGED
|
@@ -12,6 +12,9 @@ import { Descriptors } from "../codec/Descriptors.sol";
|
|
|
12
12
|
/// @notice Abstract base for peer-facing rootzero ports.
|
|
13
13
|
/// Ports handle inter-host operations between cooperating hosts.
|
|
14
14
|
/// Access is restricted to trusted peer callers via `onlyPeer`.
|
|
15
|
+
/// @dev By convention, trusting a peer authorizes it to use every port exposed
|
|
16
|
+
/// by the host without an additional policy decision in each port. Hosts that
|
|
17
|
+
/// need narrower capabilities may enforce them in hooks or custom port bases.
|
|
15
18
|
abstract contract PortBase is NodeCalls, NodeAccess, InputEndpointBase {
|
|
16
19
|
|
|
17
20
|
/// @dev Restrict execution to trusted callers, excluding the commander.
|
|
@@ -55,7 +58,7 @@ abstract contract PortBase is NodeCalls, NodeAccess, InputEndpointBase {
|
|
|
55
58
|
string memory name,
|
|
56
59
|
uint descriptor
|
|
57
60
|
) internal returns (uint id, uint published) {
|
|
58
|
-
id = Nodes.toPort(name, address(this));
|
|
61
|
+
id = Nodes.toPort(name, address(this), uint8(descriptor));
|
|
59
62
|
published = endpoint(id, name, descriptor);
|
|
60
63
|
}
|
|
61
64
|
}
|
package/ports/Dispatch.sol
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {Flags} from "../
|
|
5
|
+
import {Flags} from "../utils/Flags.sol";
|
|
6
6
|
import {Specs} from "../Codec.sol";
|
|
7
7
|
import {Execution, Executions} from "../execution/Execution.sol";
|
|
8
8
|
|
package/ports/Pipe.sol
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {Flags} from "../
|
|
5
|
+
import {Flags} from "../utils/Flags.sol";
|
|
6
6
|
import {PipeHook} from "../core/Pipeline.sol";
|
|
7
7
|
import {Specs} from "../Codec.sol";
|
|
8
8
|
import {Execution, Executions} from "../execution/Execution.sol";
|
package/queries/Base.sol
CHANGED
|
@@ -38,7 +38,7 @@ abstract contract QueryBase is InputEndpointBase {
|
|
|
38
38
|
string memory name,
|
|
39
39
|
uint descriptor
|
|
40
40
|
) internal returns (uint id, uint published) {
|
|
41
|
-
id = Nodes.toQuery(name, address(this));
|
|
41
|
+
id = Nodes.toQuery(name, address(this), uint8(descriptor));
|
|
42
42
|
published = endpoint(id, name, descriptor);
|
|
43
43
|
}
|
|
44
44
|
}
|
package/utils/Accounts.sol
CHANGED
|
@@ -19,12 +19,12 @@ import {ensureAddr, isFamily, toLocalBase, toUnspecifiedBase} from "./Utils.sol"
|
|
|
19
19
|
///
|
|
20
20
|
/// The helpers in this library validate and deconstruct structured account IDs.
|
|
21
21
|
library Accounts {
|
|
22
|
-
/// @dev
|
|
23
|
-
|
|
22
|
+
/// @dev 16-bit family tag shared by all EVM-backed account types.
|
|
23
|
+
uint16 constant Family = (uint16(Layout.Evm) << 8) | uint16(Layout.Account);
|
|
24
24
|
/// @dev Full 4-byte type prefix for admin accounts (chain-local EVM address).
|
|
25
|
-
uint32 constant Admin = (uint32(Layout.Evm) <<
|
|
25
|
+
uint32 constant Admin = (uint32(Layout.Evm) << 24) | (uint32(Layout.Account) << 16) | (uint32(Layout.Admin) << 8);
|
|
26
26
|
/// @dev Full 4-byte type prefix for user accounts (chain-agnostic EVM address).
|
|
27
|
-
uint32 constant User = (uint32(Layout.Evm) <<
|
|
27
|
+
uint32 constant User = (uint32(Layout.Evm) << 24) | (uint32(Layout.Account) << 16) | (uint32(Layout.User) << 8);
|
|
28
28
|
|
|
29
29
|
/// @notice Extract the 4-byte type prefix from an account ID.
|
|
30
30
|
/// @param account Account identifier.
|
package/utils/Assets.sol
CHANGED
|
@@ -21,12 +21,12 @@ import {ensureAddr, isFamily, matchesBase, toLocalBase} from "./Utils.sol";
|
|
|
21
21
|
///
|
|
22
22
|
/// All asset IDs are chain-local (include `block.chainid` in bits [223:192]).
|
|
23
23
|
library Assets {
|
|
24
|
-
/// @dev
|
|
25
|
-
|
|
24
|
+
/// @dev 16-bit family tag shared by all EVM-backed asset types.
|
|
25
|
+
uint16 constant Family = (uint16(Layout.Evm) << 8) | uint16(Layout.Asset);
|
|
26
26
|
/// @dev Full 4-byte type prefix for the native chain coin/token asset.
|
|
27
|
-
uint32 constant Native = (uint32(Layout.Evm) <<
|
|
27
|
+
uint32 constant Native = (uint32(Layout.Evm) << 24) | (uint32(Layout.Asset) << 16) | (uint32(Layout.Native) << 8);
|
|
28
28
|
/// @dev Full 4-byte type prefix for ERC-20 assets.
|
|
29
|
-
uint32 constant Erc20 = (uint32(Layout.Evm) <<
|
|
29
|
+
uint32 constant Erc20 = (uint32(Layout.Evm) << 24) | (uint32(Layout.Asset) << 16) | (uint32(Layout.Erc20) << 8);
|
|
30
30
|
|
|
31
31
|
/// @notice Return true if `asset` belongs to the EVM asset family.
|
|
32
32
|
function isEvm(bytes32 asset) internal pure returns (bool) {
|
|
@@ -126,7 +126,6 @@ library Assets {
|
|
|
126
126
|
if (erc20Addr(asset) != token) revert InvalidAsset();
|
|
127
127
|
return asset;
|
|
128
128
|
}
|
|
129
|
-
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
/// @title Amounts
|
package/utils/Cursors.sol
CHANGED
|
@@ -81,7 +81,30 @@ library Cursors {
|
|
|
81
81
|
end = abs + uint32(cur >> 64);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
/// @notice
|
|
84
|
+
/// @notice Return the unread calldata region represented by the lower cursor.
|
|
85
|
+
/// @dev DANGER: This trusts the packed cursor and does not validate the frame
|
|
86
|
+
/// against `msg.data`. Assumes the cursor invariant `i <= len`; any upper
|
|
87
|
+
/// cursor is ignored.
|
|
88
|
+
/// @param cur Packed cursor or cursor pair whose unread region is returned.
|
|
89
|
+
/// @return data Lower-cursor calldata from its current position through its end.
|
|
90
|
+
function raw(uint cur) internal pure returns (bytes calldata data) {
|
|
91
|
+
assembly ("memory-safe") {
|
|
92
|
+
let i := and(cur, 0xffffffff)
|
|
93
|
+
data.offset := add(and(shr(32, cur), 0xffffffff), i)
|
|
94
|
+
data.length := sub(and(shr(64, cur), 0xffffffff), i)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// @notice Create an untagged cursor backed by a calldata slice.
|
|
99
|
+
/// @param source Calldata slice represented by the cursor.
|
|
100
|
+
/// @return cur Packed cursor positioned at the slice beginning.
|
|
101
|
+
function wrap(bytes calldata source) internal pure returns (uint cur) {
|
|
102
|
+
assembly ("memory-safe") {
|
|
103
|
+
cur := or(shl(32, source.offset), shl(64, source.length))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// @notice Create a tagged cursor backed by a calldata slice.
|
|
85
108
|
/// @param source Calldata slice represented by the cursor.
|
|
86
109
|
/// @param tag Cursor identity tag.
|
|
87
110
|
/// @return cur Packed cursor positioned at the slice beginning.
|
package/utils/Flags.sol
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
/// @title Flags
|
|
5
|
+
/// @notice Shared endpoint behavior flags encoded in both endpoint IDs and descriptors.
|
|
6
|
+
/// @dev Bit 6 is reserved for endpoint-defined custom behavior.
|
|
7
|
+
library Flags {
|
|
8
|
+
/// @dev Endpoint accepts nonzero native value.
|
|
9
|
+
uint8 internal constant Funded = 1 << 0;
|
|
10
|
+
/// @dev Endpoint is restricted to the admin account.
|
|
11
|
+
uint8 internal constant Admin = 1 << 1;
|
|
12
|
+
/// @dev Endpoint accepts nonzero native value and is restricted to the admin account.
|
|
13
|
+
uint8 internal constant AdminFunded = Admin | Funded;
|
|
14
|
+
/// @dev Endpoint takes ownership of the remaining pipeline steps.
|
|
15
|
+
uint8 internal constant Handoff = 1 << 7;
|
|
16
|
+
/// @dev Endpoint accepts nonzero native value and takes ownership of the remaining pipeline steps.
|
|
17
|
+
uint8 internal constant HandoffFunded = Handoff | Funded;
|
|
18
|
+
}
|
package/utils/Layout.sol
CHANGED
|
@@ -7,7 +7,7 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
///
|
|
8
8
|
/// IDs are structured as:
|
|
9
9
|
/// `[uint32 type][uint32 chainid][192-bit payload]`
|
|
10
|
-
/// where `type` is `[
|
|
10
|
+
/// where `type` is `[uint8 representation][uint8 category][uint8 subtype][uint8 flags]`.
|
|
11
11
|
///
|
|
12
12
|
/// Values whose first byte is zero are opaque IDs:
|
|
13
13
|
/// `[0x00][bytes31 truncated hash]`
|
|
@@ -17,11 +17,11 @@ pragma solidity ^0.8.33;
|
|
|
17
17
|
/// Values whose first byte is nonzero follow the structured layout above.
|
|
18
18
|
library Layout {
|
|
19
19
|
// -------------------------------------------------------------------------
|
|
20
|
-
// Representation tags (
|
|
20
|
+
// Representation tags (first byte of the ID type field)
|
|
21
21
|
// -------------------------------------------------------------------------
|
|
22
22
|
|
|
23
23
|
/// @dev EVM-compatible ID; lower 20 payload bytes hold an address when present.
|
|
24
|
-
|
|
24
|
+
uint8 constant Evm = 0x01;
|
|
25
25
|
|
|
26
26
|
// -------------------------------------------------------------------------
|
|
27
27
|
// Category tags (uint8, third byte of the ID type field)
|