@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/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 {unpackCommand} from "../utils/Nodes.sol";
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. Implementations may revert instead.
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
- function run(
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
- bytes calldata input,
45
- uint value
87
+ uint input
46
88
  ) private returns (bytes memory output, uint credit) {
47
- (bytes4 selector, address target) = unpackCommand(cmd);
48
- if (target == address(this)) {
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, input, value);
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
- return rawCommandCall(selector, target, value, account, state, input);
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
- (uint abs, uint end) = Cursors.bounds(steps);
222
+ uint cursor;
223
+ assembly ("memory-safe") {
224
+ cursor := or(steps.offset, shl(32, add(steps.offset, steps.length)))
225
+ }
72
226
 
73
- while (abs < end) {
74
- (uint cmd, uint value, bytes calldata input, uint next) = Blocks.unpackStep(abs);
75
- if (next > end) revert OutOfBounds();
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, input, value);
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();
@@ -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, SettleHook, RepayHook, DebitAccountHook, CreditAccountHook {
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.
@@ -71,29 +71,29 @@ library Executions {
71
71
  return exec.decoders.absolute();
72
72
  }
73
73
 
74
- /// @dev Return the complete bounded calldata source for a decoder lane.
75
- /// @dev Does not consume or depend on the lane's current position. A lane
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 Complete calldata region represented by the lane.
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 abs, uint end) = cur.bounds();
85
- if (end > msg.data.length) revert Blocks.MalformedBlocks();
86
- return msg.data[abs:end];
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 complete bounded command state without consuming it.
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 complete state lane and mark it fully consumed.
96
- /// @dev Intended for commands that forward their state intact without decoding it.
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 complete bounded endpoint input without consuming it.
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 complete input lane and mark it fully consumed.
116
- /// @dev Intended for endpoints that forward their input intact without decoding it.
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
- function unpackRelay(Execution memory exec) internal pure returns (uint portal, uint resources, bytes calldata input) {
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
- (portal, resources, input, end) = Blocks.unpackRelay(cur.absolute());
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
- function outputRelay(Execution memory exec, uint portal, uint resources, bytes memory input) internal pure {
1055
- uint size = Sizes.B64 + Sizes.Header + input.length;
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, portal, resources, input);
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 input from calldata.
1181
- function outputCopyRelay(Execution memory exec, uint portal, uint resources, bytes calldata input) internal pure {
1182
- uint size = Sizes.B64 + Sizes.Header + input.length;
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, portal, resources, input);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "1.29.0",
3
+ "version": "1.30.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",
@@ -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 request their own asset allowances.
23
- /// Each AMOUNT block is scoped to the caller and passed unchanged to
24
- /// `requestAllowance(peer, asset, amount)`.
25
- abstract contract RequestAllowancePort is PortBase, RequestAllowanceHook {
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 Request asset allowances for the calling peer.
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
- requestAllowance(peer, asset, amount);
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
  }
@@ -2,7 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {PortBase} from "./Base.sol";
5
- import {Flags} from "../codec/Descriptors.sol";
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 "../codec/Descriptors.sol";
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
  }
@@ -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 24-bit family tag shared by all EVM-backed account types.
23
- uint24 constant Family = (uint24(Layout.Evm) << 8) | uint24(Layout.Account);
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) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.Admin);
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) << 16) | (uint32(Layout.Account) << 8) | uint32(Layout.User);
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 24-bit family tag shared by all EVM-backed asset types.
25
- uint24 constant Family = (uint24(Layout.Evm) << 8) | uint24(Layout.Asset);
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) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Native);
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) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc20);
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 Create a cursor backed by a calldata slice.
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.
@@ -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 `[uint16 representation][uint8 category][uint8 subtype]`.
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 (top 2 bytes of the ID type field)
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
- uint16 constant Evm = 0x0120;
24
+ uint8 constant Evm = 0x01;
25
25
 
26
26
  // -------------------------------------------------------------------------
27
27
  // Category tags (uint8, third byte of the ID type field)