@rootzero/contracts 1.27.0 → 1.29.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 +37 -0
- package/Core.sol +2 -2
- package/Endpoints.sol +6 -6
- package/README.md +29 -20
- package/Utils.sol +1 -1
- package/codec/Blocks.sol +13 -13
- package/codec/Decoders.sol +1 -1
- package/codec/Keys.sol +1 -1
- package/codec/Schema.sol +6 -5
- package/codec/Specs.sol +3 -4
- package/codec/Writers.sol +2 -2
- package/commands/Bootstrap.sol +4 -2
- package/commands/Cashout.sol +7 -6
- package/commands/Credit.sol +7 -6
- package/commands/Debit.sol +8 -7
- package/commands/Recover.sol +2 -1
- package/commands/Relay.sol +3 -2
- package/commands/Repay.sol +7 -6
- package/commands/Settle.sol +7 -6
- package/commands/admin/Execute.sol +3 -1
- package/core/Access.sol +1 -3
- package/core/Calls.sol +83 -79
- package/core/Pipeline.sol +36 -23
- package/core/Portal.sol +2 -2
- package/execution/Budget.sol +8 -4
- package/execution/Execution.sol +8 -6
- package/package.json +1 -1
- package/ports/Dispatch.sol +3 -2
- package/utils/Nodes.sol +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,43 @@ sections are immutable and must continue to describe the tagged release.
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 1.29.0
|
|
12
|
+
|
|
13
|
+
### Breaking Changes
|
|
14
|
+
|
|
15
|
+
- `ExecuteHook.execute` and the optimized local command helpers now return a
|
|
16
|
+
leading `bool handled`. A local command returned as unhandled is invoked
|
|
17
|
+
through its trusted normal external command entrypoint; handled commands keep
|
|
18
|
+
the optimized internal path and are authorized by the hook implementation.
|
|
19
|
+
- Renamed `CashoutInternal`, `DebitAccountInternal`, `CreditAccountInternal`,
|
|
20
|
+
`SettleInternal`, and `RepayInternal` to `ExecuteCashout`,
|
|
21
|
+
`ExecuteDebitAccount`, `ExecuteCreditAccount`, `ExecuteSettle`, and
|
|
22
|
+
`ExecuteRepay`.
|
|
23
|
+
|
|
24
|
+
## 1.28.0
|
|
25
|
+
|
|
26
|
+
### Breaking Changes
|
|
27
|
+
|
|
28
|
+
- Expanded STEP's native `value` field from `uint128` to `uint`, increasing an
|
|
29
|
+
empty STEP block from 64 to 80 bytes. Plain native values now use `uint`
|
|
30
|
+
throughout runtime APIs; packed `resources` remain separate `uint` words
|
|
31
|
+
whose EVM value lane is extracted explicitly with `useResourceValue`.
|
|
32
|
+
- Replaced the `CommandCalls` abstraction and its allocating `encodeCommandCall`
|
|
33
|
+
and `callCommand` helpers with the free `rawCommandCall` function. The caller
|
|
34
|
+
now supplies the decoded selector and target after validating and authorizing
|
|
35
|
+
the command, then uses the single-scratch assembly call path.
|
|
36
|
+
- Added the required `ExecuteHook` to `Pipeline` and removed its `dispatch` hook.
|
|
37
|
+
Steps whose command IDs target the current host use `execute`; other commands
|
|
38
|
+
are checked through `TrustAccess` and called directly with `rawCommandCall`.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Added the free `unpackCommand` utility for validating a command node and
|
|
43
|
+
extracting its selector and target in one operation. Pipeline steps now use
|
|
44
|
+
it to validate command IDs and select local execution by target address.
|
|
45
|
+
- Simplified `NodeAccess.ensureTrusted` to a single mapping check; an unset zero
|
|
46
|
+
node already resolves to false like every other untrusted node.
|
|
47
|
+
|
|
11
48
|
## 1.27.0
|
|
12
49
|
|
|
13
50
|
### Breaking Changes
|
package/Core.sol
CHANGED
|
@@ -12,9 +12,9 @@ import { Balances, InsufficientFunds } from "./core/Balances.sol";
|
|
|
12
12
|
import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
|
|
13
13
|
import { NativeAsset, Runtime } from "./core/Runtime.sol";
|
|
14
14
|
import { Admins, CommandHost, Guardians, Host, HostIntroduction, IHostIntroduction } from "./core/Host.sol";
|
|
15
|
-
import {
|
|
15
|
+
import { FailedCall, NodeCalls, PortCalls, RawNodeCalls, rawCommandCall } from "./core/Calls.sol";
|
|
16
16
|
import { EndpointBase, InputEndpointBase } from "./core/Endpoint.sol";
|
|
17
|
-
import { PipeHook, Pipeline } from "./core/Pipeline.sol";
|
|
17
|
+
import { ExecuteHook, PipeHook, Pipeline } from "./core/Pipeline.sol";
|
|
18
18
|
import { Budget, Budgets } from "./execution/Budget.sol";
|
|
19
19
|
import { CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook, Settlement } from "./core/Settlement.sol";
|
|
20
20
|
import { Portal } from "./core/Portal.sol";
|
package/Endpoints.sol
CHANGED
|
@@ -6,7 +6,7 @@ pragma solidity ^0.8.33;
|
|
|
6
6
|
|
|
7
7
|
// Shared endpoint hooks
|
|
8
8
|
import {Flags} from "./codec/Descriptors.sol";
|
|
9
|
-
import {PipeHook} from "./core/Pipeline.sol";
|
|
9
|
+
import {ExecuteHook, PipeHook} from "./core/Pipeline.sol";
|
|
10
10
|
import {CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook} from "./core/Settlement.sol";
|
|
11
11
|
|
|
12
12
|
// Commands
|
|
@@ -14,23 +14,23 @@ import {CommandBase} from "./commands/Base.sol";
|
|
|
14
14
|
import {Allocate, AllocateHook} from "./commands/Allocate.sol";
|
|
15
15
|
import {Burn, BurnHook} from "./commands/Burn.sol";
|
|
16
16
|
import {Bootstrap} from "./commands/Bootstrap.sol";
|
|
17
|
-
import {Cashout, CashoutHook,
|
|
18
|
-
import {CreditAccount,
|
|
19
|
-
import {DebitAccount,
|
|
17
|
+
import {Cashout, CashoutHook, ExecuteCashout} from "./commands/Cashout.sol";
|
|
18
|
+
import {CreditAccount, ExecuteCreditAccount} from "./commands/Credit.sol";
|
|
19
|
+
import {DebitAccount, ExecuteDebitAccount} from "./commands/Debit.sol";
|
|
20
20
|
import {Deposit, DepositHook, DepositPayable, DepositPayableHook} from "./commands/Deposit.sol";
|
|
21
21
|
import {Payout, PayoutHook} from "./commands/Payout.sol";
|
|
22
22
|
import {Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook} from "./commands/Provision.sol";
|
|
23
23
|
import {RecoverPayable, RecoverPayableHook} from "./commands/Recover.sol";
|
|
24
24
|
import {
|
|
25
25
|
Repay,
|
|
26
|
-
|
|
26
|
+
ExecuteRepay,
|
|
27
27
|
RepayPayable,
|
|
28
28
|
RepayPayableHook,
|
|
29
29
|
RepayPosition,
|
|
30
30
|
RepayPositionPayable
|
|
31
31
|
} from "./commands/Repay.sol";
|
|
32
32
|
import {RelayPayable, RelayBalancePayable, RelayPayableHook} from "./commands/Relay.sol";
|
|
33
|
-
import {Settle, SettlePayable, SettlePayableHook,
|
|
33
|
+
import {Settle, SettlePayable, SettlePayableHook, ExecuteSettle} from "./commands/Settle.sol";
|
|
34
34
|
import {Withdraw, WithdrawHook} from "./commands/Withdraw.sol";
|
|
35
35
|
|
|
36
36
|
// Admin commands
|
package/README.md
CHANGED
|
@@ -365,7 +365,7 @@ A single command is rarely the whole story. A pipeline is a run of `#step`
|
|
|
365
365
|
blocks executed in order within one transaction:
|
|
366
366
|
|
|
367
367
|
```txt
|
|
368
|
-
step { uint cmd,
|
|
368
|
+
step { uint cmd, uint value, #bytes as input }
|
|
369
369
|
```
|
|
370
370
|
|
|
371
371
|
Each step names a command, the native value it may spend, and its input.
|
|
@@ -379,22 +379,26 @@ debits each asset through the standard account hook, introduces matching
|
|
|
379
379
|
native asset through the same hook. Its pipeline-local implementation uses assigned step value first when
|
|
380
380
|
bootstrapping the native asset, debits any remainder from the account, and
|
|
381
381
|
returns unused assigned value as credit. Bootstrap is registered with command
|
|
382
|
-
metadata but is only executable through local pipeline
|
|
382
|
+
metadata but is only executable through local pipeline execution. This is the core of
|
|
383
383
|
`Pipeline.pipe`:
|
|
384
384
|
|
|
385
385
|
```solidity
|
|
386
386
|
while (cur.more()) {
|
|
387
|
-
(uint cmd,
|
|
387
|
+
(uint cmd, uint value, bytes calldata input) = cur.unpackStep();
|
|
388
|
+
(bytes4 selector, address target) = unpackCommand(cmd);
|
|
388
389
|
if (budget < value) revert InsufficientValue();
|
|
389
390
|
unchecked { budget -= value; }
|
|
391
|
+
bool handled;
|
|
392
|
+
bytes memory output;
|
|
390
393
|
uint credit;
|
|
391
|
-
(
|
|
392
|
-
cmd,
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
value
|
|
397
|
-
|
|
394
|
+
if (target == address(this)) {
|
|
395
|
+
(handled, output, credit) = execute(cmd, account, state, input, value);
|
|
396
|
+
}
|
|
397
|
+
if (!handled) {
|
|
398
|
+
ensureTrusted(cmd);
|
|
399
|
+
(output, credit) = rawCommandCall(selector, target, value, account, state, input);
|
|
400
|
+
}
|
|
401
|
+
state = output;
|
|
398
402
|
budget += credit;
|
|
399
403
|
}
|
|
400
404
|
if (state.length != 0) revert UnexpectedState();
|
|
@@ -407,20 +411,25 @@ settles that final value once.
|
|
|
407
411
|
A transfer, for instance, is a two-step pipeline: `debitAccount` turns an
|
|
408
412
|
`#amount` input into `#balance` state, and `payout` consumes that state
|
|
409
413
|
toward a recipient. Because a pipeline is just blocks, it is also the unit of
|
|
410
|
-
command batching. A step's `
|
|
411
|
-
native-value budget. Transport envelopes retain separate
|
|
412
|
-
`resources` fields for adapters that also need gas or runtime
|
|
414
|
+
command batching. A step's plain `uint value` is drawn directly from the shared
|
|
415
|
+
native-value budget. Transport envelopes retain separate opaque, packed
|
|
416
|
+
chain-specific `resources` fields for adapters that also need gas or runtime
|
|
417
|
+
parameters. A `resources` word is never itself native value; EVM adapters use
|
|
418
|
+
`useResourceValue` to extract its low 128-bit value lane before spending it.
|
|
413
419
|
|
|
414
420
|
Hosts that implement a pipeline locally can inherit `Bootstrap`,
|
|
415
|
-
`
|
|
416
|
-
`
|
|
417
|
-
`
|
|
421
|
+
`ExecuteCashout`, `ExecuteDebitAccount`, `ExecuteCreditAccount`,
|
|
422
|
+
`ExecuteSettle`, and
|
|
423
|
+
`ExecuteRepay` to register canonical command metadata while executing
|
|
418
424
|
their local command IDs through `executeBootstrap`, `executeCashout`,
|
|
419
425
|
`executeDebitAccount`, `executeCreditAccount`, `executeSettle`, and
|
|
420
426
|
`executeRepay`. The bootstrap, cashout, and debit adapters decode fixed-stride
|
|
421
427
|
calldata input directly; the other three
|
|
422
|
-
decode memory-backed pipeline state. All
|
|
423
|
-
|
|
428
|
+
decode memory-backed pipeline state. All return `handled = true` and avoid an
|
|
429
|
+
external self-call. The host's `execute` hook must authorize a command before
|
|
430
|
+
returning true. Returning `handled = false` delegates a local command to its
|
|
431
|
+
trusted normal external entrypoint. Pass the step value into each adapter.
|
|
432
|
+
Bootstrap is pipeline-local rather than an
|
|
424
433
|
externally callable command and may consume value for native-asset balance;
|
|
425
434
|
the other five reject nonzero value because those commands are non-funded.
|
|
426
435
|
|
|
@@ -509,11 +518,11 @@ names, access sets, balances — from logs alone, with no artifact files.
|
|
|
509
518
|
Import from the package entry points rather than deep paths:
|
|
510
519
|
|
|
511
520
|
- `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
|
|
512
|
-
`Settlement`, `PipeHook`, `Pipeline`, `Portal`, validator
|
|
521
|
+
`Settlement`, `ExecuteHook`, `PipeHook`, `Pipeline`, `Portal`, validator
|
|
513
522
|
- `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, `Flags`,
|
|
514
523
|
codec helpers, and shared value types for authoring custom commands
|
|
515
524
|
- `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
|
|
516
|
-
mixins, their hooks (including `PipeHook`), and `Flags`
|
|
525
|
+
mixins, their hooks (including `ExecuteHook` and `PipeHook`), and `Flags`
|
|
517
526
|
- `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
|
|
518
527
|
`Memory`, `Writers`, `Schemas`, `Descriptors`, `Flags`, `Keys`, and
|
|
519
528
|
`Specs`
|
package/Utils.sol
CHANGED
|
@@ -10,7 +10,7 @@ import { Actions } from "./utils/Actions.sol";
|
|
|
10
10
|
import { Amounts, Assets } from "./utils/Assets.sol";
|
|
11
11
|
import { ECDSA } from "./utils/ECDSA.sol";
|
|
12
12
|
import { Ids } from "./utils/Ids.sol";
|
|
13
|
-
import { Nodes } from "./utils/Nodes.sol";
|
|
13
|
+
import { Nodes, unpackCommand } from "./utils/Nodes.sol";
|
|
14
14
|
import { Layout } from "./utils/Layout.sol";
|
|
15
15
|
import { BadAmount, InsufficientValue, InvalidAccount, InvalidAsset, InvalidContract, InvalidId, InvalidPreimage, MissingCursor, NotDivisible, OutOfBounds, UnauthorizedAsset, UnconsumedData, UnexpectedPosition, ValueOverflow, ZeroAddress, ZeroAmount} from "./utils/Errors.sol";
|
|
16
16
|
import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, clear8, clear16, clear32, clear64, divisible, ensureAddr, ensureContract, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, replace8, replace16, replace32, replace64, retryTicket, toLocalBase, toUnspecifiedBase } from "./utils/Utils.sol";
|
package/codec/Blocks.sol
CHANGED
|
@@ -761,17 +761,17 @@ library Blocks {
|
|
|
761
761
|
/// @param cmd Command identifier.
|
|
762
762
|
/// @param value Native value assigned to the step.
|
|
763
763
|
/// @param input Command input.
|
|
764
|
-
function writeStep(bytes memory dst, uint i, uint cmd,
|
|
765
|
-
uint len =
|
|
764
|
+
function writeStep(bytes memory dst, uint i, uint cmd, uint value, bytes memory input) internal pure {
|
|
765
|
+
uint len = 64 + Sizes.Header + input.length;
|
|
766
766
|
uint key = uint32(Keys.Step);
|
|
767
767
|
uint byteskey = uint32(Keys.Bytes);
|
|
768
768
|
assembly ("memory-safe") {
|
|
769
769
|
let p := add(add(dst, 0x20), i)
|
|
770
770
|
mstore(p, or(shl(224, key), shl(192, len)))
|
|
771
771
|
mstore(add(p, 0x08), cmd)
|
|
772
|
-
mstore(add(p, 0x28),
|
|
772
|
+
mstore(add(p, 0x28), value)
|
|
773
773
|
|
|
774
|
-
let q := add(p,
|
|
774
|
+
let q := add(p, 0x48)
|
|
775
775
|
let inputlen := mload(input)
|
|
776
776
|
mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
|
|
777
777
|
mcopy(add(q, 0x08), add(input, 0x20), inputlen)
|
|
@@ -1031,17 +1031,17 @@ library Blocks {
|
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
1033
|
/// @notice Encode a STEP block at `i`, copying its nested input from calldata.
|
|
1034
|
-
function copyStep(bytes memory dst, uint i, uint cmd,
|
|
1035
|
-
uint len = max32(
|
|
1034
|
+
function copyStep(bytes memory dst, uint i, uint cmd, uint value, bytes calldata input) internal pure {
|
|
1035
|
+
uint len = max32(64 + Sizes.Header + input.length);
|
|
1036
1036
|
uint key = uint32(Keys.Step);
|
|
1037
1037
|
uint byteskey = uint32(Keys.Bytes);
|
|
1038
1038
|
assembly ("memory-safe") {
|
|
1039
1039
|
let p := add(add(dst, 0x20), i)
|
|
1040
1040
|
mstore(p, or(shl(224, key), shl(192, len)))
|
|
1041
1041
|
mstore(add(p, 0x08), cmd)
|
|
1042
|
-
mstore(add(p, 0x28),
|
|
1042
|
+
mstore(add(p, 0x28), value)
|
|
1043
1043
|
|
|
1044
|
-
let q := add(p,
|
|
1044
|
+
let q := add(p, 0x48)
|
|
1045
1045
|
let inputlen := input.length
|
|
1046
1046
|
mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
|
|
1047
1047
|
calldatacopy(add(q, 0x08), input.offset, inputlen)
|
|
@@ -1836,14 +1836,14 @@ library Blocks {
|
|
|
1836
1836
|
/// @return end Absolute position after the block.
|
|
1837
1837
|
function unpackStep(
|
|
1838
1838
|
uint abs
|
|
1839
|
-
) internal pure returns (uint cmd,
|
|
1839
|
+
) internal pure returns (uint cmd, uint value, bytes calldata input, uint end) {
|
|
1840
1840
|
uint limit;
|
|
1841
1841
|
(abs, limit) = expectKey(abs, Keys.Step);
|
|
1842
1842
|
assembly ("memory-safe") {
|
|
1843
1843
|
cmd := calldataload(abs)
|
|
1844
|
-
value :=
|
|
1844
|
+
value := calldataload(add(abs, 0x20))
|
|
1845
1845
|
}
|
|
1846
|
-
(input, end) = unpackBytes(abs +
|
|
1846
|
+
(input, end) = unpackBytes(abs + 64);
|
|
1847
1847
|
if (end != limit) revert InvalidBlock();
|
|
1848
1848
|
}
|
|
1849
1849
|
|
|
@@ -2203,14 +2203,14 @@ library Blocks {
|
|
|
2203
2203
|
/// @param value Native value assigned to the step.
|
|
2204
2204
|
/// @param input Raw nested input payload.
|
|
2205
2205
|
/// @return encoded Encoded STEP block bytes.
|
|
2206
|
-
function createStep(uint cmd,
|
|
2206
|
+
function createStep(uint cmd, uint value, bytes memory input) internal pure returns (bytes memory encoded) {
|
|
2207
2207
|
uint len = max32(Sizes.Step + input.length);
|
|
2208
2208
|
encoded = allocate(len);
|
|
2209
2209
|
writeStep(encoded, 0, cmd, value, input);
|
|
2210
2210
|
}
|
|
2211
2211
|
|
|
2212
2212
|
/// @notice Encode a STEP block by copying its nested input from calldata.
|
|
2213
|
-
function createStepCopy(uint cmd,
|
|
2213
|
+
function createStepCopy(uint cmd, uint value, bytes calldata input) internal pure returns (bytes memory encoded) {
|
|
2214
2214
|
uint len = max32(Sizes.Step + input.length);
|
|
2215
2215
|
encoded = allocate(len);
|
|
2216
2216
|
copyStep(encoded, 0, cmd, value, input);
|
package/codec/Decoders.sol
CHANGED
|
@@ -740,7 +740,7 @@ library Decoders {
|
|
|
740
740
|
/// @return input Decoded command input.
|
|
741
741
|
function unpackStep(
|
|
742
742
|
Cur memory cur
|
|
743
|
-
) internal pure returns (uint cmd,
|
|
743
|
+
) internal pure returns (uint cmd, uint value, bytes calldata input) {
|
|
744
744
|
uint abs = cur.state.absolute();
|
|
745
745
|
uint end;
|
|
746
746
|
(cmd, value, input, end) = Blocks.unpackStep(abs);
|
package/codec/Keys.sol
CHANGED
|
@@ -39,7 +39,7 @@ library Keys {
|
|
|
39
39
|
bytes4 constant Account = bytes4(keccak256("#account"));
|
|
40
40
|
/// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, uint amount)
|
|
41
41
|
bytes4 constant Transaction = bytes4(keccak256("#transaction"));
|
|
42
|
-
/// @dev Sub-command invocation - (uint cmd,
|
|
42
|
+
/// @dev Sub-command invocation - (uint cmd, uint value, #bytes as input)
|
|
43
43
|
bytes4 constant Step = bytes4(keccak256("#step"));
|
|
44
44
|
/// @dev Portal relay input - (uint portal, uint resources, #bytes as input)
|
|
45
45
|
bytes4 constant Relay = bytes4(keccak256("#relay"));
|
package/codec/Schema.sol
CHANGED
|
@@ -29,10 +29,11 @@ pragma solidity ^0.8.33;
|
|
|
29
29
|
// - `portal` fields identify destination portal hosts. By convention the value
|
|
30
30
|
// is the portal implementation's host ID; core passes it through unchanged
|
|
31
31
|
// and hooks may validate or resolve it for their transport
|
|
32
|
-
// - `resources` fields are chain-specific
|
|
33
|
-
// interprets them for the destination runtime. EVM
|
|
34
|
-
// 128 bits as native value
|
|
35
|
-
//
|
|
32
|
+
// - `resources` fields are opaque chain-specific packed words, not native
|
|
33
|
+
// values. A portal adapter interprets them for the destination runtime. EVM
|
|
34
|
+
// resources use the low 128 bits as native value, extracted explicitly with
|
|
35
|
+
// `useResourceValue` before spending.
|
|
36
|
+
// - STEP encodes native `value` directly as uint; it does not carry a
|
|
36
37
|
// chain-specific resources word
|
|
37
38
|
// - dotted field names and aliases, e.g. `dst.portal` or `#bytes as dst.payload`,
|
|
38
39
|
// are offchain projection metadata only and do not change runtime encoding
|
|
@@ -116,7 +117,7 @@ library Schemas {
|
|
|
116
117
|
|
|
117
118
|
// Composite payloads
|
|
118
119
|
|
|
119
|
-
string constant Step = "uint cmd,
|
|
120
|
+
string constant Step = "uint cmd, uint value, #bytes as input";
|
|
120
121
|
string constant Call = "uint target, uint resources, #bytes as payload";
|
|
121
122
|
string constant Relay = "uint portal, uint resources, #bytes as input";
|
|
122
123
|
string constant Dispatch = "uint portal, uint resources, #bytes as payload";
|
package/codec/Specs.sol
CHANGED
|
@@ -21,8 +21,8 @@ library Sizes {
|
|
|
21
21
|
uint constant B128 = Header + 4 * Word;
|
|
22
22
|
/// @dev 8 header + 160 payload = 168 bytes total.
|
|
23
23
|
uint constant B160 = Header + 5 * Word;
|
|
24
|
-
/// @dev Minimum STEP size: 8 header + 32 command +
|
|
25
|
-
uint constant Step = 2 * Header +
|
|
24
|
+
/// @dev Minimum STEP size: 8 header + 32 command + 32 value + 8 nested BYTES header.
|
|
25
|
+
uint constant Step = 2 * Header + 2 * Word;
|
|
26
26
|
/// @dev STATUS block: 8 header + 32 status code = 40 bytes
|
|
27
27
|
uint constant Status = B32;
|
|
28
28
|
/// @dev CASHOUT block: 8 header + 32 native-asset amount = 40 bytes
|
|
@@ -64,7 +64,6 @@ library Specs {
|
|
|
64
64
|
uint private constant Exact128 = 128 * SizeFields;
|
|
65
65
|
uint private constant UnboundedHint128 = uint(128) << 136;
|
|
66
66
|
uint private constant UnboundedMin40Hint256 = (uint(40) << 192) | (uint(256) << 136);
|
|
67
|
-
uint private constant UnboundedMin56Hint256 = (uint(56) << 192) | (uint(256) << 136);
|
|
68
67
|
uint private constant UnboundedMin72Hint256 = (uint(72) << 192) | (uint(256) << 136);
|
|
69
68
|
uint private constant UnboundedMin48Hint512 = (uint(48) << 192) | (uint(512) << 136);
|
|
70
69
|
uint private constant UnboundedMin104Hint256 = (uint(104) << 192) | (uint(256) << 136);
|
|
@@ -85,7 +84,7 @@ library Specs {
|
|
|
85
84
|
uint constant String = uint(bytes32(Keys.String)) | UnboundedHint128;
|
|
86
85
|
uint constant Account = uint(bytes32(Keys.Account)) | Exact32;
|
|
87
86
|
uint constant Transaction = uint(bytes32(Keys.Transaction)) | Exact128;
|
|
88
|
-
uint constant Step = uint(bytes32(Keys.Step)) |
|
|
87
|
+
uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin72Hint256;
|
|
89
88
|
uint constant Relay = uint(bytes32(Keys.Relay)) | UnboundedMin72Hint256;
|
|
90
89
|
uint constant Context = uint(bytes32(Keys.Context)) | UnboundedMin48Hint512;
|
|
91
90
|
uint constant Recover = uint(bytes32(Keys.Recover)) | UnboundedMin104Hint256;
|
package/codec/Writers.sol
CHANGED
|
@@ -414,7 +414,7 @@ library Writers {
|
|
|
414
414
|
/// @param cmd Command identifier to encode.
|
|
415
415
|
/// @param value Native value to encode.
|
|
416
416
|
/// @param input Command input to encode.
|
|
417
|
-
function appendStep(Writer memory writer, uint cmd,
|
|
417
|
+
function appendStep(Writer memory writer, uint cmd, uint value, bytes memory input) internal pure {
|
|
418
418
|
uint size = Sizes.Step + input.length;
|
|
419
419
|
uint i = reserve(writer, size);
|
|
420
420
|
Blocks.writeStep(writer.dst, i, cmd, value, input);
|
|
@@ -561,7 +561,7 @@ library Writers {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
/// @notice Append a STEP block by copying its nested input from calldata.
|
|
564
|
-
function copyStep(Writer memory writer, uint cmd,
|
|
564
|
+
function copyStep(Writer memory writer, uint cmd, uint value, bytes calldata input) internal pure {
|
|
565
565
|
uint size = Sizes.Step + input.length;
|
|
566
566
|
uint i = reserve(writer, size);
|
|
567
567
|
Blocks.copyStep(writer.dst, i, cmd, value, input);
|
package/commands/Bootstrap.sol
CHANGED
|
@@ -52,14 +52,15 @@ abstract contract Bootstrap is CommandBase, DebitAccountHook {
|
|
|
52
52
|
/// @param state Empty pipeline state required by the command schema.
|
|
53
53
|
/// @param input BOOTSTRAP block stream.
|
|
54
54
|
/// @param value Native value available to fund native-asset balances.
|
|
55
|
+
/// @return handled Always true because this helper executed the command.
|
|
55
56
|
/// @return output One BALANCE block per BOOTSTRAP input.
|
|
56
57
|
/// @return credit Sourced budget contributions plus unused assigned value.
|
|
57
58
|
function executeBootstrap(
|
|
58
59
|
bytes32 account,
|
|
59
60
|
bytes memory state,
|
|
60
61
|
bytes calldata input,
|
|
61
|
-
|
|
62
|
-
) internal returns (bytes memory output, uint credit) {
|
|
62
|
+
uint value
|
|
63
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
63
64
|
if (state.length != 0) revert UnexpectedState();
|
|
64
65
|
if (input.length % Sizes.Bootstrap != 0) revert Blocks.InvalidBlock();
|
|
65
66
|
|
|
@@ -77,5 +78,6 @@ abstract contract Bootstrap is CommandBase, DebitAccountHook {
|
|
|
77
78
|
i += Sizes.Balance;
|
|
78
79
|
}
|
|
79
80
|
}
|
|
81
|
+
handled = true;
|
|
80
82
|
}
|
|
81
83
|
}
|
package/commands/Cashout.sol
CHANGED
|
@@ -54,22 +54,23 @@ abstract contract Cashout is CommandBase, CashoutHook, Action {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/// @title
|
|
58
|
-
/// @notice Extends cashout with optimized local pipeline
|
|
59
|
-
abstract contract
|
|
57
|
+
/// @title ExecuteCashout
|
|
58
|
+
/// @notice Extends cashout with optimized local pipeline execution.
|
|
59
|
+
abstract contract ExecuteCashout is Cashout {
|
|
60
60
|
/// @notice Execute cashout directly against a calldata CASHOUT stream.
|
|
61
61
|
/// @param account Account whose native asset is withdrawn.
|
|
62
62
|
/// @param state Empty pipeline state required by the command schema.
|
|
63
63
|
/// @param input CASHOUT block stream.
|
|
64
64
|
/// @param value Native value assigned to this command; must be zero.
|
|
65
|
+
/// @return handled Always true because this helper executed the command.
|
|
65
66
|
/// @return output Empty output state.
|
|
66
67
|
/// @return credit Zero native budget credit.
|
|
67
68
|
function executeCashout(
|
|
68
69
|
bytes32 account,
|
|
69
70
|
bytes memory state,
|
|
70
71
|
bytes calldata input,
|
|
71
|
-
|
|
72
|
-
) internal returns (bytes memory, uint) {
|
|
72
|
+
uint value
|
|
73
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
73
74
|
if (value != 0) revert ValueNotAllowed();
|
|
74
75
|
if (state.length != 0) revert UnexpectedState();
|
|
75
76
|
if (input.length % Sizes.Cashout != 0) revert Blocks.InvalidBlock();
|
|
@@ -81,6 +82,6 @@ abstract contract CashoutInternal is Cashout {
|
|
|
81
82
|
abs += Sizes.Cashout;
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
|
-
return ("", 0);
|
|
85
|
+
return (true, "", 0);
|
|
85
86
|
}
|
|
86
87
|
}
|
package/commands/Credit.sol
CHANGED
|
@@ -43,24 +43,25 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/// @title
|
|
47
|
-
/// @notice Extends the advertised credit-account command with memory-state pipeline
|
|
46
|
+
/// @title ExecuteCreditAccount
|
|
47
|
+
/// @notice Extends the advertised credit-account command with memory-state pipeline execution.
|
|
48
48
|
/// @dev This adapter is not a separate command. It uses the command ID and account hook
|
|
49
49
|
/// inherited from `CreditAccount` while accepting the state location used by `Pipeline`.
|
|
50
|
-
abstract contract
|
|
50
|
+
abstract contract ExecuteCreditAccount is CreditAccount {
|
|
51
51
|
/// @notice Execute the inherited credit-account command from an internal pipeline.
|
|
52
52
|
/// @param account Account credited by each balance.
|
|
53
53
|
/// @param state BALANCE block stream held in pipeline memory.
|
|
54
54
|
/// @param input Empty input required by the command schema.
|
|
55
55
|
/// @param value Native value assigned to the command; must be zero.
|
|
56
|
+
/// @return handled Always true because this helper executed the command.
|
|
56
57
|
/// @return output Empty output state.
|
|
57
58
|
/// @return credit Zero native budget credit.
|
|
58
59
|
function executeCreditAccount(
|
|
59
60
|
bytes32 account,
|
|
60
61
|
bytes memory state,
|
|
61
62
|
bytes calldata input,
|
|
62
|
-
|
|
63
|
-
) internal returns (bytes memory, uint) {
|
|
63
|
+
uint value
|
|
64
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
64
65
|
if (value != 0) revert ValueNotAllowed();
|
|
65
66
|
if (input.length != 0) revert UnexpectedInput();
|
|
66
67
|
if (state.length == 0) revert Blocks.EmptyRun();
|
|
@@ -74,6 +75,6 @@ abstract contract CreditAccountInternal is CreditAccount {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
return ("", 0);
|
|
78
|
+
return (true, "", 0);
|
|
78
79
|
}
|
|
79
80
|
}
|
package/commands/Debit.sol
CHANGED
|
@@ -46,31 +46,32 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/// @title
|
|
50
|
-
/// @notice Extends the advertised debit-account command with optimized pipeline
|
|
49
|
+
/// @title ExecuteDebitAccount
|
|
50
|
+
/// @notice Extends the advertised debit-account command with optimized pipeline execution.
|
|
51
51
|
/// @dev This adapter is not a separate command. It uses the command ID and account hook
|
|
52
52
|
/// inherited from `DebitAccount` while decoding its fixed-stride input directly from calldata.
|
|
53
|
-
abstract contract
|
|
53
|
+
abstract contract ExecuteDebitAccount is DebitAccount {
|
|
54
54
|
/// @notice Execute the inherited debit-account command from an internal pipeline.
|
|
55
55
|
/// @param account Account whose funds are debited.
|
|
56
56
|
/// @param state Empty pipeline state required by the command schema.
|
|
57
57
|
/// @param input AMOUNT block stream.
|
|
58
58
|
/// @param value Native value assigned to the command; must be zero.
|
|
59
|
+
/// @return handled Always true because this helper executed the command.
|
|
59
60
|
/// @return output BALANCE block stream matching the debited amounts.
|
|
60
61
|
/// @return credit Zero native budget credit.
|
|
61
62
|
function executeDebitAccount(
|
|
62
63
|
bytes32 account,
|
|
63
64
|
bytes memory state,
|
|
64
65
|
bytes calldata input,
|
|
65
|
-
|
|
66
|
-
) internal returns (bytes memory, uint) {
|
|
66
|
+
uint value
|
|
67
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
67
68
|
if (value != 0) revert ValueNotAllowed();
|
|
68
69
|
if (state.length != 0) revert UnexpectedState();
|
|
69
70
|
if (input.length == 0) revert Blocks.EmptyRun();
|
|
70
71
|
if (input.length % Sizes.Amount != 0) revert Blocks.InvalidBlock();
|
|
71
72
|
|
|
72
73
|
(uint abs, uint end) = Cursors.bounds(input);
|
|
73
|
-
|
|
74
|
+
output = new bytes(input.length);
|
|
74
75
|
uint i;
|
|
75
76
|
|
|
76
77
|
while (abs < end) {
|
|
@@ -83,6 +84,6 @@ abstract contract DebitAccountInternal is DebitAccount {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
return (output, 0);
|
|
87
|
+
return (true, output, 0);
|
|
87
88
|
}
|
|
88
89
|
}
|
package/commands/Recover.sol
CHANGED
|
@@ -9,7 +9,8 @@ using Executions for Execution;
|
|
|
9
9
|
abstract contract RecoverPayableHook {
|
|
10
10
|
/// @notice Override to recover a witness through `handler`.
|
|
11
11
|
/// @param handler Port that should attempt recovery.
|
|
12
|
-
/// @param resources
|
|
12
|
+
/// @param resources Opaque packed chain-specific resources, not plain native
|
|
13
|
+
/// value. EVM handlers extract the low 128-bit value lane with `useResourceValue`.
|
|
13
14
|
/// @param key Recovery lookup key.
|
|
14
15
|
/// @param witness Witness payload used to prove and replay recovery.
|
|
15
16
|
/// @param funds Shared execution containing the source value budget.
|
package/commands/Relay.sol
CHANGED
|
@@ -13,8 +13,9 @@ abstract contract RelayPayableHook {
|
|
|
13
13
|
/// the source state is consumed before destination success is known.
|
|
14
14
|
/// @param portal Destination portal implementation's host ID. Implementations
|
|
15
15
|
/// may validate or resolve it for their transport.
|
|
16
|
-
/// @param resources
|
|
17
|
-
///
|
|
16
|
+
/// @param resources Opaque packed chain-specific destination resources, not
|
|
17
|
+
/// plain native value. EVM adapters extract the low 128-bit value lane with
|
|
18
|
+
/// `useResourceValue`; higher bits may encode execution gas or other data.
|
|
18
19
|
/// @param account Destination command account.
|
|
19
20
|
/// @param state Complete state forwarded into the destination context.
|
|
20
21
|
/// @param input Input forwarded into the destination context.
|
package/commands/Repay.sol
CHANGED
|
@@ -146,24 +146,25 @@ abstract contract RepayPositionPayable is CommandBase, RepayPayableHook, Action
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/// @title
|
|
150
|
-
/// @notice Extends the advertised repay command with memory-state pipeline
|
|
149
|
+
/// @title ExecuteRepay
|
|
150
|
+
/// @notice Extends the advertised repay command with memory-state pipeline execution.
|
|
151
151
|
/// @dev This adapter is not a separate command. It uses the command ID and repayment hook
|
|
152
152
|
/// inherited from `Repay` while accepting the state location used by `Pipeline`.
|
|
153
|
-
abstract contract
|
|
153
|
+
abstract contract ExecuteRepay is Repay {
|
|
154
154
|
/// @notice Execute the inherited repay command from an internal pipeline.
|
|
155
155
|
/// @param account Account whose liabilities are repaid.
|
|
156
156
|
/// @param state DEBT block stream held in pipeline memory.
|
|
157
157
|
/// @param input Empty input required by the command schema.
|
|
158
158
|
/// @param value Native value assigned to the command; must be zero.
|
|
159
|
+
/// @return handled Always true because this helper executed the command.
|
|
159
160
|
/// @return output Empty output state.
|
|
160
161
|
/// @return credit Zero native budget credit.
|
|
161
162
|
function executeRepay(
|
|
162
163
|
bytes32 account,
|
|
163
164
|
bytes memory state,
|
|
164
165
|
bytes calldata input,
|
|
165
|
-
|
|
166
|
-
) internal returns (bytes memory, uint) {
|
|
166
|
+
uint value
|
|
167
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
167
168
|
if (value != 0) revert ValueNotAllowed();
|
|
168
169
|
if (input.length != 0) revert UnexpectedInput();
|
|
169
170
|
if (state.length == 0) revert Blocks.EmptyRun();
|
|
@@ -177,6 +178,6 @@ abstract contract RepayInternal is Repay {
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
return ("", 0);
|
|
181
|
+
return (true, "", 0);
|
|
181
182
|
}
|
|
182
183
|
}
|
package/commands/Settle.sol
CHANGED
|
@@ -93,24 +93,25 @@ abstract contract SettlePayable is CommandBase, SettlePayableHook, Action {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/// @title
|
|
97
|
-
/// @notice Extends the advertised settle command with memory-state pipeline
|
|
96
|
+
/// @title ExecuteSettle
|
|
97
|
+
/// @notice Extends the advertised settle command with memory-state pipeline execution.
|
|
98
98
|
/// @dev This adapter is not a separate command. It uses the command ID and settlement hook
|
|
99
99
|
/// inherited from `Settle` while accepting the state location used by `Pipeline`.
|
|
100
|
-
abstract contract
|
|
100
|
+
abstract contract ExecuteSettle is Settle {
|
|
101
101
|
/// @notice Execute the inherited settle command from an internal pipeline.
|
|
102
102
|
/// @param account Account for which each position is settled.
|
|
103
103
|
/// @param state POSITION block stream held in pipeline memory.
|
|
104
104
|
/// @param input Empty input required by the command schema.
|
|
105
105
|
/// @param value Native value assigned to the command; must be zero.
|
|
106
|
+
/// @return handled Always true because this helper executed the command.
|
|
106
107
|
/// @return output Empty output state.
|
|
107
108
|
/// @return credit Zero native budget credit.
|
|
108
109
|
function executeSettle(
|
|
109
110
|
bytes32 account,
|
|
110
111
|
bytes memory state,
|
|
111
112
|
bytes calldata input,
|
|
112
|
-
|
|
113
|
-
) internal returns (bytes memory, uint) {
|
|
113
|
+
uint value
|
|
114
|
+
) internal returns (bool handled, bytes memory output, uint credit) {
|
|
114
115
|
if (value != 0) revert ValueNotAllowed();
|
|
115
116
|
if (input.length != 0) revert UnexpectedInput();
|
|
116
117
|
if (state.length == 0) revert Blocks.EmptyRun();
|
|
@@ -124,6 +125,6 @@ abstract contract SettleInternal is Settle {
|
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
return ("", 0);
|
|
128
|
+
return (true, "", 0);
|
|
128
129
|
}
|
|
129
130
|
}
|
|
@@ -8,7 +8,9 @@ using Executions for Execution;
|
|
|
8
8
|
|
|
9
9
|
/// @title ExecutePayable
|
|
10
10
|
/// @notice Admin command that forwards raw calldata to one or more target nodes.
|
|
11
|
-
/// Each CALL block specifies a target node ID, packed resources, and raw
|
|
11
|
+
/// Each CALL block specifies a target node ID, opaque packed resources, and raw
|
|
12
|
+
/// calldata payload. Packed resources are converted to plain native value only
|
|
13
|
+
/// through `useResourceValue`.
|
|
12
14
|
/// Only callable by the admin account.
|
|
13
15
|
/// Unspent top-level `msg.value` is returned as native budget credit.
|
|
14
16
|
abstract contract ExecutePayable is RawNodeCalls, AdminBase {
|
package/core/Access.sol
CHANGED
|
@@ -106,9 +106,7 @@ abstract contract NodeAccess is AdminAccess, TrustAccess, NodeEvent {
|
|
|
106
106
|
/// @param node Node ID to validate.
|
|
107
107
|
/// @return The same `node` value if trusted.
|
|
108
108
|
function ensureTrusted(uint node) internal view override returns (uint) {
|
|
109
|
-
if (
|
|
110
|
-
revert AccessDenied();
|
|
111
|
-
}
|
|
109
|
+
if (!nodes[node]) revert AccessDenied();
|
|
112
110
|
return node;
|
|
113
111
|
}
|
|
114
112
|
|
package/core/Calls.sol
CHANGED
|
@@ -3,9 +3,6 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {TrustAccess} from "./Access.sol";
|
|
5
5
|
import {Nodes} from "../utils/Nodes.sol";
|
|
6
|
-
import {Keys} from "../codec/Keys.sol";
|
|
7
|
-
import {Sizes} from "../codec/Specs.sol";
|
|
8
|
-
import {max32} from "../utils/Utils.sol";
|
|
9
6
|
|
|
10
7
|
/// @dev Emitted when a trusted inter-node call fails.
|
|
11
8
|
/// @param addr Contract address that was called.
|
|
@@ -13,6 +10,81 @@ import {max32} from "../utils/Utils.sol";
|
|
|
13
10
|
/// @param err Revert data returned by the failed call.
|
|
14
11
|
error FailedCall(address addr, bytes4 selector, bytes err);
|
|
15
12
|
|
|
13
|
+
/// @notice Execute a raw `command(bytes)` call and decode its state and credit results.
|
|
14
|
+
/// @dev The caller must validate and authorize the command before entering this helper.
|
|
15
|
+
/// Uses one scratch region for both call data and return data. Successful return data
|
|
16
|
+
/// must use the exact ABI layout of `(bytes, uint)`.
|
|
17
|
+
function rawCommandCall(
|
|
18
|
+
bytes4 selector,
|
|
19
|
+
address target,
|
|
20
|
+
uint value,
|
|
21
|
+
bytes32 account,
|
|
22
|
+
bytes memory state,
|
|
23
|
+
bytes calldata input
|
|
24
|
+
) returns (bytes memory output, uint credit) {
|
|
25
|
+
assembly ("memory-safe") {
|
|
26
|
+
let scratch := mload(0x40)
|
|
27
|
+
let statelen := mload(state)
|
|
28
|
+
let ctxlen := add(56, add(statelen, input.length))
|
|
29
|
+
|
|
30
|
+
// ABI envelope for command(bytes).
|
|
31
|
+
mstore(scratch, selector)
|
|
32
|
+
mstore(add(scratch, 0x04), 0x20)
|
|
33
|
+
mstore(add(scratch, 0x24), ctxlen)
|
|
34
|
+
|
|
35
|
+
// CONTEXT(account, BYTES(state), BYTES(input)).
|
|
36
|
+
let ctx := add(scratch, 0x44)
|
|
37
|
+
// 0xc5769e23 = bytes4(keccak256("#context"))
|
|
38
|
+
mstore(ctx, or(shl(224, 0xc5769e23), shl(192, sub(ctxlen, 8))))
|
|
39
|
+
// `ctxlen` is dead after the header and can carry the complete call length.
|
|
40
|
+
ctxlen := add(68, and(add(ctxlen, 0x1f), not(0x1f)))
|
|
41
|
+
mstore(add(ctx, 0x08), account)
|
|
42
|
+
let stateblk := add(ctx, 0x28)
|
|
43
|
+
// 0x6911b332 = bytes4(keccak256("#bytes"))
|
|
44
|
+
mstore(stateblk, or(shl(224, 0x6911b332), shl(192, statelen)))
|
|
45
|
+
mcopy(add(stateblk, 0x08), add(state, 0x20), statelen)
|
|
46
|
+
let inputblk := add(add(stateblk, 0x08), statelen)
|
|
47
|
+
let inputlen := input.length
|
|
48
|
+
mstore(inputblk, or(shl(224, 0x6911b332), shl(192, inputlen)))
|
|
49
|
+
calldatacopy(add(inputblk, 0x08), input.offset, inputlen)
|
|
50
|
+
|
|
51
|
+
// ABI word alignment also covers the input header's full-word write. Advance
|
|
52
|
+
// the free memory pointer once, after the return-data size is also known.
|
|
53
|
+
let inputend := and(add(add(scratch, ctxlen), 0x1f), not(0x1f))
|
|
54
|
+
let success := call(gas(), target, value, scratch, ctxlen, 0, 0)
|
|
55
|
+
let retlen := returndatasize()
|
|
56
|
+
if iszero(success) {
|
|
57
|
+
// FailedCall(target, selector, returndata)
|
|
58
|
+
// 0x20577b07 = FailedCall(address,bytes4,bytes)
|
|
59
|
+
mstore(scratch, shl(224, 0x20577b07))
|
|
60
|
+
mstore(add(scratch, 0x04), target)
|
|
61
|
+
mstore(add(scratch, 0x24), selector)
|
|
62
|
+
mstore(add(scratch, 0x44), 0x60)
|
|
63
|
+
mstore(add(scratch, 0x64), retlen)
|
|
64
|
+
mstore(add(add(scratch, 0x84), retlen), 0)
|
|
65
|
+
returndatacopy(add(scratch, 0x84), 0, retlen)
|
|
66
|
+
revert(scratch, add(0x84, and(add(retlen, 0x1f), not(0x1f))))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if lt(retlen, 0x60) { revert(0, 0) }
|
|
70
|
+
returndatacopy(scratch, 0, retlen)
|
|
71
|
+
|
|
72
|
+
// Strictly validate the exact ABI layout for (bytes, uint). The returned
|
|
73
|
+
// byte array can then point directly into the copied returndata.
|
|
74
|
+
if iszero(eq(mload(scratch), 0x40)) { revert(0, 0) }
|
|
75
|
+
let len1 := mload(add(scratch, 0x40))
|
|
76
|
+
if gt(len1, sub(retlen, 0x60)) { revert(0, 0) }
|
|
77
|
+
let pad1 := and(add(len1, 0x1f), not(0x1f))
|
|
78
|
+
if iszero(eq(retlen, add(0x60, pad1))) { revert(0, 0) }
|
|
79
|
+
output := add(scratch, 0x40)
|
|
80
|
+
credit := mload(add(scratch, 0x20))
|
|
81
|
+
|
|
82
|
+
let retend := and(add(add(scratch, retlen), 0x1f), not(0x1f))
|
|
83
|
+
if gt(retend, inputend) { inputend := retend }
|
|
84
|
+
mstore(0x40, inputend)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
16
88
|
/// @title RawNodeCalls
|
|
17
89
|
/// @notice Low-level inter-node call helpers without target authorization.
|
|
18
90
|
abstract contract RawNodeCalls {
|
|
@@ -21,7 +93,7 @@ abstract contract RawNodeCalls {
|
|
|
21
93
|
/// @param value Native value to forward in wei.
|
|
22
94
|
/// @param data Encoded calldata to send.
|
|
23
95
|
/// @return success True if the low-level call succeeded.
|
|
24
|
-
function tryRawCall(uint node,
|
|
96
|
+
function tryRawCall(uint node, uint value, bytes memory data) internal returns (bool success) {
|
|
25
97
|
address addr = Nodes.addr(node);
|
|
26
98
|
(success, ) = payable(addr).call{value: value}(data);
|
|
27
99
|
}
|
|
@@ -31,7 +103,7 @@ abstract contract RawNodeCalls {
|
|
|
31
103
|
/// @param value Native value to forward in wei.
|
|
32
104
|
/// @param data Encoded calldata to send.
|
|
33
105
|
/// @return out Return data from the successful call.
|
|
34
|
-
function rawCall(uint node,
|
|
106
|
+
function rawCall(uint node, uint value, bytes memory data) internal returns (bytes memory out) {
|
|
35
107
|
bool success;
|
|
36
108
|
address addr = Nodes.addr(node);
|
|
37
109
|
(success, out) = payable(addr).call{value: value}(data);
|
|
@@ -59,7 +131,7 @@ abstract contract NodeCalls is RawNodeCalls, TrustAccess {
|
|
|
59
131
|
/// @param value Native value to forward in wei.
|
|
60
132
|
/// @param data Encoded calldata to send.
|
|
61
133
|
/// @return success True if the low-level call succeeded.
|
|
62
|
-
function tryTrustedCall(uint node,
|
|
134
|
+
function tryTrustedCall(uint node, uint value, bytes memory data) internal returns (bool success) {
|
|
63
135
|
return tryRawCall(ensureTrusted(node), value, data);
|
|
64
136
|
}
|
|
65
137
|
|
|
@@ -68,7 +140,7 @@ abstract contract NodeCalls is RawNodeCalls, TrustAccess {
|
|
|
68
140
|
/// @param value Native value to forward in wei.
|
|
69
141
|
/// @param data Encoded calldata to send.
|
|
70
142
|
/// @return out Return data from the successful call.
|
|
71
|
-
function trustedCall(uint node,
|
|
143
|
+
function trustedCall(uint node, uint value, bytes memory data) internal returns (bytes memory out) {
|
|
72
144
|
return rawCall(ensureTrusted(node), value, data);
|
|
73
145
|
}
|
|
74
146
|
|
|
@@ -81,74 +153,6 @@ abstract contract NodeCalls is RawNodeCalls, TrustAccess {
|
|
|
81
153
|
}
|
|
82
154
|
}
|
|
83
155
|
|
|
84
|
-
/// @title CommandCalls
|
|
85
|
-
/// @notice Trusted command-call helpers for contracts that route command nodes.
|
|
86
|
-
abstract contract CommandCalls is NodeCalls {
|
|
87
|
-
/// @dev Build `command(bytes)` calldata and its nested CONTEXT block in one allocation.
|
|
88
|
-
/// Threaded state is copied from memory and step input directly from calldata.
|
|
89
|
-
function encodeCommandCall(
|
|
90
|
-
bytes4 selector,
|
|
91
|
-
bytes32 account,
|
|
92
|
-
bytes memory state,
|
|
93
|
-
bytes calldata input
|
|
94
|
-
) internal pure returns (bytes memory data) {
|
|
95
|
-
uint contextLen = max32(Sizes.B32 + 2 * Sizes.Header + state.length + input.length);
|
|
96
|
-
uint paddedContextLen = (contextLen + 31) & ~uint(31);
|
|
97
|
-
uint dataLen = 4 + 64 + paddedContextLen;
|
|
98
|
-
|
|
99
|
-
// Reserve one scratch word because the final eight-byte block header is
|
|
100
|
-
// written with mstore. Exclude that word from the returned calldata.
|
|
101
|
-
data = new bytes(dataLen + 32);
|
|
102
|
-
|
|
103
|
-
uint contextKey = uint32(Keys.Context);
|
|
104
|
-
uint bytesKey = uint32(Keys.Bytes);
|
|
105
|
-
assembly ("memory-safe") {
|
|
106
|
-
mstore(data, dataLen)
|
|
107
|
-
let out := add(data, 0x20)
|
|
108
|
-
|
|
109
|
-
// ABI envelope for command(bytes).
|
|
110
|
-
mstore(out, selector)
|
|
111
|
-
mstore(add(out, 0x04), 0x20)
|
|
112
|
-
mstore(add(out, 0x24), contextLen)
|
|
113
|
-
|
|
114
|
-
// CONTEXT(account, BYTES(state), BYTES(input)).
|
|
115
|
-
let context := add(out, 0x44)
|
|
116
|
-
mstore(context, or(shl(224, contextKey), shl(192, sub(contextLen, 8))))
|
|
117
|
-
mstore(add(context, 0x08), account)
|
|
118
|
-
|
|
119
|
-
let stateBlock := add(context, 0x28)
|
|
120
|
-
let stateLen := mload(state)
|
|
121
|
-
mstore(stateBlock, or(shl(224, bytesKey), shl(192, stateLen)))
|
|
122
|
-
mcopy(add(stateBlock, 0x08), add(state, 0x20), stateLen)
|
|
123
|
-
|
|
124
|
-
let inputBlock := add(add(stateBlock, 0x08), stateLen)
|
|
125
|
-
let inputLen := input.length
|
|
126
|
-
mstore(inputBlock, or(shl(224, bytesKey), shl(192, inputLen)))
|
|
127
|
-
calldatacopy(add(inputBlock, 0x08), input.offset, inputLen)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// @notice Encode and call a trusted command node.
|
|
132
|
-
/// @param command Command node ID embedding the target selector.
|
|
133
|
-
/// @param value Native value to forward in wei.
|
|
134
|
-
/// @param account Command account identifier.
|
|
135
|
-
/// @param state Current command state block stream.
|
|
136
|
-
/// @param input Command input block stream.
|
|
137
|
-
/// @return nextState Decoded command output state block stream.
|
|
138
|
-
/// @return credit Trusted native value to add to the caller's execution budget.
|
|
139
|
-
function callCommand(
|
|
140
|
-
uint command,
|
|
141
|
-
uint128 value,
|
|
142
|
-
bytes32 account,
|
|
143
|
-
bytes memory state,
|
|
144
|
-
bytes calldata input
|
|
145
|
-
) internal returns (bytes memory nextState, uint credit) {
|
|
146
|
-
bytes4 selector = Nodes.commandSelector(command);
|
|
147
|
-
bytes memory data = encodeCommandCall(selector, account, state, input);
|
|
148
|
-
return abi.decode(trustedCall(command, value, data), (bytes, uint));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
156
|
/// @title PortCalls
|
|
153
157
|
/// @notice Trusted port-call helpers for contracts that route port nodes.
|
|
154
158
|
abstract contract PortCalls is NodeCalls {
|
|
@@ -157,7 +161,7 @@ abstract contract PortCalls is NodeCalls {
|
|
|
157
161
|
/// @param value Native value to forward in wei.
|
|
158
162
|
/// @param input Port input block stream.
|
|
159
163
|
/// @return success True if the low-level port call succeeded.
|
|
160
|
-
function tryCallPort(uint port,
|
|
164
|
+
function tryCallPort(uint port, uint value, bytes memory input) internal returns (bool success) {
|
|
161
165
|
bytes4 selector = Nodes.portSelector(port);
|
|
162
166
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
163
167
|
return tryTrustedCall(port, value, data);
|
|
@@ -168,7 +172,7 @@ abstract contract PortCalls is NodeCalls {
|
|
|
168
172
|
/// @param value Native value to forward in wei.
|
|
169
173
|
/// @param input Port input block stream.
|
|
170
174
|
/// @return success True if the low-level port call succeeded.
|
|
171
|
-
function tryCallPortCopy(uint port,
|
|
175
|
+
function tryCallPortCopy(uint port, uint value, bytes calldata input) internal returns (bool success) {
|
|
172
176
|
bytes4 selector = Nodes.portSelector(port);
|
|
173
177
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
174
178
|
return tryTrustedCall(port, value, data);
|
|
@@ -179,7 +183,7 @@ abstract contract PortCalls is NodeCalls {
|
|
|
179
183
|
/// @param value Native value to forward in wei.
|
|
180
184
|
/// @param input Port input block stream.
|
|
181
185
|
/// @return Decoded port output block stream.
|
|
182
|
-
function callPort(uint port,
|
|
186
|
+
function callPort(uint port, uint value, bytes memory input) internal returns (bytes memory) {
|
|
183
187
|
bytes4 selector = Nodes.portSelector(port);
|
|
184
188
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
185
189
|
return abi.decode(trustedCall(port, value, data), (bytes));
|
|
@@ -190,7 +194,7 @@ abstract contract PortCalls is NodeCalls {
|
|
|
190
194
|
/// @param value Native value to forward in wei.
|
|
191
195
|
/// @param input Port input block stream.
|
|
192
196
|
/// @return Decoded port output block stream.
|
|
193
|
-
function callPortCopy(uint port,
|
|
197
|
+
function callPortCopy(uint port, uint value, bytes calldata input) internal returns (bytes memory) {
|
|
194
198
|
bytes4 selector = Nodes.portSelector(port);
|
|
195
199
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
196
200
|
return abi.decode(trustedCall(port, value, data), (bytes));
|
package/core/Pipeline.sol
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {Blocks} from "../codec/Blocks.sol";
|
|
5
|
+
import {TrustAccess} from "./Access.sol";
|
|
6
|
+
import {rawCommandCall} from "./Calls.sol";
|
|
5
7
|
import {Cursors} from "../utils/Cursors.sol";
|
|
6
8
|
import {InsufficientValue, OutOfBounds, UnexpectedState} from "../utils/Errors.sol";
|
|
9
|
+
import {unpackCommand} from "../utils/Nodes.sol";
|
|
7
10
|
|
|
8
11
|
/// @notice Hook implemented by hosts that execute encoded step streams.
|
|
9
12
|
abstract contract PipeHook {
|
|
@@ -16,27 +19,40 @@ abstract contract PipeHook {
|
|
|
16
19
|
) internal virtual returns (uint remaining);
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
/// @notice Hook implemented by pipeline hosts that execute host-local commands.
|
|
23
|
+
abstract contract ExecuteHook {
|
|
24
|
+
/// @notice Try to execute one command whose node ID targets the current host.
|
|
25
|
+
/// @dev Implementations returning `handled = true` are responsible for
|
|
26
|
+
/// authorizing the command. Return false without side effects to delegate to
|
|
27
|
+
/// the trusted normal external entrypoint. Implementations may revert instead.
|
|
28
|
+
function execute(
|
|
29
|
+
uint cmd,
|
|
30
|
+
bytes32 account,
|
|
31
|
+
bytes memory state,
|
|
32
|
+
bytes calldata input,
|
|
33
|
+
uint value
|
|
34
|
+
) internal virtual returns (bool handled, bytes memory output, uint credit);
|
|
35
|
+
}
|
|
36
|
+
|
|
19
37
|
/// @title Pipeline
|
|
20
38
|
/// @notice Core pipeline functionality shared by higher-level surfaces.
|
|
21
|
-
abstract contract Pipeline is PipeHook {
|
|
22
|
-
|
|
23
|
-
/// Called once per STEP block. The returned state becomes the state passed to
|
|
24
|
-
/// the next step, and the final returned state must be empty. Returned
|
|
25
|
-
/// credit is added to the shared native-value budget before the next step runs.
|
|
26
|
-
/// @param cmd Command node ID to invoke or handle.
|
|
27
|
-
/// @param account Account identifier for the piped context.
|
|
28
|
-
/// @param state Current threaded state block stream.
|
|
29
|
-
/// @param input Step input block stream.
|
|
30
|
-
/// @param value Native EVM value assigned to this step.
|
|
31
|
-
/// @return output Updated state block stream for the next step.
|
|
32
|
-
/// @return credit Trusted native value to add to the pipeline budget.
|
|
33
|
-
function dispatch(
|
|
39
|
+
abstract contract Pipeline is TrustAccess, PipeHook, ExecuteHook {
|
|
40
|
+
function run(
|
|
34
41
|
uint cmd,
|
|
35
42
|
bytes32 account,
|
|
36
43
|
bytes memory state,
|
|
37
44
|
bytes calldata input,
|
|
38
|
-
|
|
39
|
-
)
|
|
45
|
+
uint value
|
|
46
|
+
) private returns (bytes memory output, uint credit) {
|
|
47
|
+
(bytes4 selector, address target) = unpackCommand(cmd);
|
|
48
|
+
if (target == address(this)) {
|
|
49
|
+
bool handled;
|
|
50
|
+
(handled, output, credit) = execute(cmd, account, state, input, value);
|
|
51
|
+
if (handled) return (output, credit);
|
|
52
|
+
}
|
|
53
|
+
ensureTrusted(cmd);
|
|
54
|
+
return rawCommandCall(selector, target, value, account, state, input);
|
|
55
|
+
}
|
|
40
56
|
|
|
41
57
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
42
58
|
/// @dev Reverts with `UnexpectedState` if the final threaded state is non-empty.
|
|
@@ -55,18 +71,15 @@ abstract contract Pipeline is PipeHook {
|
|
|
55
71
|
(uint abs, uint end) = Cursors.bounds(steps);
|
|
56
72
|
|
|
57
73
|
while (abs < end) {
|
|
58
|
-
uint cmd;
|
|
59
|
-
|
|
60
|
-
bytes calldata input;
|
|
61
|
-
(cmd, value, input, abs) = Blocks.unpackStep(abs);
|
|
62
|
-
if (abs > end) revert OutOfBounds();
|
|
74
|
+
(uint cmd, uint value, bytes calldata input, uint next) = Blocks.unpackStep(abs);
|
|
75
|
+
if (next > end) revert OutOfBounds();
|
|
63
76
|
if (value > budget) revert InsufficientValue();
|
|
64
77
|
unchecked {
|
|
65
78
|
budget -= value;
|
|
66
79
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
(state, value) = run(cmd, account, state, input, value);
|
|
81
|
+
budget += value;
|
|
82
|
+
abs = next;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
if (state.length != 0) revert UnexpectedState();
|
package/core/Portal.sol
CHANGED
|
@@ -20,7 +20,7 @@ abstract contract Portal is PortCalls, NodeAccess, UnresolvedEvent, ResolvedEven
|
|
|
20
20
|
/// @param message Encoded port input to forward.
|
|
21
21
|
/// @param value Native EVM value assigned to the forwarding attempt.
|
|
22
22
|
/// @return miss Message digest recorded for recovery when forwarding fails; zero on success.
|
|
23
|
-
function forward(uint port, bytes32 key, bytes calldata message,
|
|
23
|
+
function forward(uint port, bytes32 key, bytes calldata message, uint value) internal returns (bytes32 miss) {
|
|
24
24
|
if (tryCallPortCopy(port, value, message)) return bytes32(0);
|
|
25
25
|
|
|
26
26
|
miss = keccak256(message);
|
|
@@ -33,7 +33,7 @@ abstract contract Portal is PortCalls, NodeAccess, UnresolvedEvent, ResolvedEven
|
|
|
33
33
|
/// @param key Recovery lookup key.
|
|
34
34
|
/// @param witness Witness payload used to prove and replay recovery.
|
|
35
35
|
/// @param value Native EVM value assigned to the resolution attempt.
|
|
36
|
-
function resolve(uint port, bytes32 key, bytes calldata witness,
|
|
36
|
+
function resolve(uint port, bytes32 key, bytes calldata witness, uint value) internal virtual {
|
|
37
37
|
if (unresolved[key] != keccak256(witness)) revert BadWitness();
|
|
38
38
|
|
|
39
39
|
delete unresolved[key];
|
package/execution/Budget.sol
CHANGED
|
@@ -38,15 +38,19 @@ library Budgets {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/// @notice Deduct the EVM value lane of `resources` from `budget`.
|
|
41
|
-
/// @dev
|
|
41
|
+
/// @dev `resources` is not a native value. This helper explicitly extracts
|
|
42
|
+
/// its low 128-bit EVM value lane and widens that lane to a plain `uint`.
|
|
42
43
|
/// @param budget Mutable budget to debit.
|
|
43
44
|
/// @param resources Packed resources whose low 128 bits contain native value.
|
|
44
45
|
/// @return value Native value to forward in wei.
|
|
45
|
-
function useResourceValue(Budget memory budget, uint resources) internal pure returns (
|
|
46
|
-
|
|
46
|
+
function useResourceValue(Budget memory budget, uint resources) internal pure returns (uint value) {
|
|
47
|
+
value = uint128(resources);
|
|
48
|
+
useValue(budget, value);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/// @notice Deduct the EVM value lane of `resources` from a scalar budget.
|
|
52
|
+
/// @dev `resources` is not a native value. This helper explicitly extracts
|
|
53
|
+
/// its low 128-bit EVM value lane and widens that lane to a plain `uint`.
|
|
50
54
|
/// @param budget Remaining native value in wei.
|
|
51
55
|
/// @param resources Packed resources whose low 128 bits contain native value.
|
|
52
56
|
/// @return remaining Native value remaining after the deduction.
|
|
@@ -54,7 +58,7 @@ library Budgets {
|
|
|
54
58
|
function useResourceValue(
|
|
55
59
|
uint budget,
|
|
56
60
|
uint resources
|
|
57
|
-
) internal pure returns (uint remaining,
|
|
61
|
+
) internal pure returns (uint remaining, uint value) {
|
|
58
62
|
value = uint128(resources);
|
|
59
63
|
remaining = useValue(budget, value);
|
|
60
64
|
}
|
package/execution/Execution.sol
CHANGED
|
@@ -618,7 +618,7 @@ library Executions {
|
|
|
618
618
|
/// @return cmd Decoded command identifier.
|
|
619
619
|
/// @return value Decoded native value.
|
|
620
620
|
/// @return input Decoded nested input.
|
|
621
|
-
function unpackStep(Execution memory exec) internal pure returns (uint cmd,
|
|
621
|
+
function unpackStep(Execution memory exec) internal pure returns (uint cmd, uint value, bytes calldata input) {
|
|
622
622
|
uint cur = exec.decoders;
|
|
623
623
|
uint end;
|
|
624
624
|
(cmd, value, input, end) = Blocks.unpackStep(cur.absolute());
|
|
@@ -1029,7 +1029,7 @@ library Executions {
|
|
|
1029
1029
|
/// @param cmd Command identifier to encode.
|
|
1030
1030
|
/// @param value Native value to encode.
|
|
1031
1031
|
/// @param input Command input to encode.
|
|
1032
|
-
function outputStep(Execution memory exec, uint cmd,
|
|
1032
|
+
function outputStep(Execution memory exec, uint cmd, uint value, bytes memory input) internal pure {
|
|
1033
1033
|
uint size = Sizes.Step + input.length;
|
|
1034
1034
|
uint i = reserve(exec, size);
|
|
1035
1035
|
Blocks.writeStep(exec.output, i, cmd, value, input);
|
|
@@ -1164,7 +1164,7 @@ library Executions {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
|
|
1166
1166
|
/// @notice Append a STEP block to execution output by copying its nested input from calldata.
|
|
1167
|
-
function outputCopyStep(Execution memory exec, uint cmd,
|
|
1167
|
+
function outputCopyStep(Execution memory exec, uint cmd, uint value, bytes calldata input) internal pure {
|
|
1168
1168
|
uint size = Sizes.Step + input.length;
|
|
1169
1169
|
uint i = reserve(exec, size);
|
|
1170
1170
|
Blocks.copyStep(exec.output, i, cmd, value, input);
|
|
@@ -1247,12 +1247,14 @@ library Executions {
|
|
|
1247
1247
|
}
|
|
1248
1248
|
|
|
1249
1249
|
/// @notice Deduct the EVM value lane of `resources` from the execution budget.
|
|
1250
|
-
/// @dev
|
|
1250
|
+
/// @dev `resources` is not a native value. This helper explicitly extracts
|
|
1251
|
+
/// its low 128-bit EVM value lane and widens that lane to a plain `uint`.
|
|
1251
1252
|
/// @param exec Mutable execution whose budget is charged.
|
|
1252
1253
|
/// @param resources Packed resources whose value lane should be spent.
|
|
1253
1254
|
/// @return value Native value to forward in wei.
|
|
1254
|
-
function useResourceValue(Execution memory exec, uint resources) internal pure returns (
|
|
1255
|
-
|
|
1255
|
+
function useResourceValue(Execution memory exec, uint resources) internal pure returns (uint value) {
|
|
1256
|
+
value = uint128(resources);
|
|
1257
|
+
useValue(exec, value);
|
|
1256
1258
|
}
|
|
1257
1259
|
|
|
1258
1260
|
// -------------------------------------------------------------------------
|
package/package.json
CHANGED
package/ports/Dispatch.sol
CHANGED
|
@@ -13,8 +13,9 @@ abstract contract DispatchPayableHook {
|
|
|
13
13
|
/// @notice Override to dispatch an encoded payload to `portal`.
|
|
14
14
|
/// @param portal Destination portal implementation's host ID. Implementations
|
|
15
15
|
/// may validate or resolve it for their transport.
|
|
16
|
-
/// @param resources
|
|
17
|
-
///
|
|
16
|
+
/// @param resources Opaque packed chain-specific destination resources, not
|
|
17
|
+
/// plain native value. EVM adapters extract the low 128-bit value lane with
|
|
18
|
+
/// `useResourceValue`; higher bits may encode execution gas or other data.
|
|
18
19
|
/// @param payload Encoded payload ready for the transport layer.
|
|
19
20
|
/// @param funds Execution used for source value available for transport fees
|
|
20
21
|
/// and destination resource funding.
|
package/utils/Nodes.sol
CHANGED
|
@@ -6,6 +6,21 @@ import {InvalidId} from "./Errors.sol";
|
|
|
6
6
|
import {Ids} from "./Ids.sol";
|
|
7
7
|
import {ensureAddr, isFamily, matchesBase, toLocalBase} from "./Utils.sol";
|
|
8
8
|
|
|
9
|
+
/// @notice Validate and unpack a command node into its ABI selector and target address.
|
|
10
|
+
/// @dev Validates only the command type prefix; chain locality and authorization are caller concerns.
|
|
11
|
+
/// @param cmd Command node ID to unpack.
|
|
12
|
+
/// @return selector ABI selector stored in bits [191:160].
|
|
13
|
+
/// @return target Contract address stored in bits [159:0].
|
|
14
|
+
function unpackCommand(uint cmd) pure returns (bytes4 selector, address target) {
|
|
15
|
+
uint32 command = (uint32(Layout.Evm) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
|
|
16
|
+
if (uint32(cmd >> 224) != command) revert InvalidId();
|
|
17
|
+
|
|
18
|
+
assembly ("memory-safe") {
|
|
19
|
+
selector := shl(224, and(shr(160, cmd), 0xffffffff))
|
|
20
|
+
target := and(cmd, 0xffffffffffffffffffffffffffffffffffffffff)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
9
24
|
/// @title Nodes
|
|
10
25
|
/// @notice Encoding and decoding helpers for 256-bit node identifiers.
|
|
11
26
|
///
|