@rootzero/contracts 1.28.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 +77 -0
- package/Codec.sol +2 -1
- package/Commands.sol +1 -1
- package/Core.sol +1 -1
- package/Endpoints.sol +7 -7
- package/README.md +97 -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/Bootstrap.sol +3 -1
- package/commands/Cashout.sol +24 -21
- package/commands/Credit.sol +6 -5
- package/commands/Debit.sol +7 -6
- package/commands/Relay.sol +14 -15
- package/commands/Repay.sol +6 -5
- package/commands/Settle.sol +6 -5
- 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 +179 -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/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,83 @@ sections are immutable and must continue to describe the tagged release.
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 1.30.0
|
|
12
|
+
|
|
13
|
+
### Breaking Changes
|
|
14
|
+
|
|
15
|
+
- Replaced the obsolete second representation/width byte in every structured
|
|
16
|
+
ID with a trailing flags byte. The type prefix is now
|
|
17
|
+
`[uint8 representation][uint8 category][uint8 subtype][uint8 flags]`, changing
|
|
18
|
+
every structured account, asset, and node ID. Endpoint IDs copy the same flags
|
|
19
|
+
published in their descriptors.
|
|
20
|
+
- Added the canonical handoff command flag. Relay commands now publish command
|
|
21
|
+
IDs carrying `Flags.Handoff`. `Pipeline.pipe` now wraps a flagged STEP's
|
|
22
|
+
ordinary input and untouched continuation in a RELAY block, calls the handoff
|
|
23
|
+
command once, and stops local execution of that continuation.
|
|
24
|
+
The internal RELAY block changed from `(portal, resources, input)` to two
|
|
25
|
+
nested byte streams: command-specific `input` and remaining `steps`. Relay
|
|
26
|
+
hooks now receive `account`, complete `state`, opaque command input, remaining
|
|
27
|
+
steps, and their value budget; each transport implementation decodes its own
|
|
28
|
+
command input.
|
|
29
|
+
- Added `Flags.HandoffFunded` as the canonical combination used by funded
|
|
30
|
+
handoff commands, matching the existing `Flags.AdminFunded` convention.
|
|
31
|
+
- Removed the free `rawCommandCall` API and moved its cursor-aware assembly path
|
|
32
|
+
into `Pipeline` as a private implementation detail. Pipeline's private `run`
|
|
33
|
+
path now owns local execution, trust, command unpacking, handoff selection,
|
|
34
|
+
invocation, and cursor advancement. `pipe` accepts the STEP stream as
|
|
35
|
+
calldata and packs its current position, end, command-input offset, and
|
|
36
|
+
command-input length into one private cursor. The invocation path uses those
|
|
37
|
+
fields directly for ordinary input or to wrap a handoff continuation in a
|
|
38
|
+
RELAY block.
|
|
39
|
+
- Removed the free `unpackCommand` utility. Pipeline now privately validates
|
|
40
|
+
command IDs and extracts the target and direct handoff decision needed for
|
|
41
|
+
routing; the private invoker derives selector and target directly from the ID.
|
|
42
|
+
- Aligned no-argument `raw` access across cursors, decoders, and executions to
|
|
43
|
+
return only the unread region from the current position. `rawState`,
|
|
44
|
+
`rawInput`, `takeRawState`, and `takeRawInput` now follow the same rule;
|
|
45
|
+
explicitly bounded `raw(from, to)` slices remain frame-relative.
|
|
46
|
+
|
|
47
|
+
- Replaced the stateless `RawNodeCalls` inheritance contract with imported
|
|
48
|
+
`tryRawCall`, `rawCall`, and `rawQuery` free functions. Added overloads that
|
|
49
|
+
accept a selector and native target separately from selector-free ABI
|
|
50
|
+
arguments, without allocating a concatenated calldata buffer.
|
|
51
|
+
- Changed `Host`, `CommandHost`, `CommanderAccess`, and `HostIntroduction`
|
|
52
|
+
constructors from an EVM `address` to a protocol-native `uint` commander host
|
|
53
|
+
ID. Nonzero commanders must be valid local host IDs; the EVM address is
|
|
54
|
+
extracted internally for caller checks and introductions, while zero retains
|
|
55
|
+
the self-commanded `Host` convention and remains invalid for `CommandHost`.
|
|
56
|
+
- Removed `RequestAllowanceHook`. `RequestAllowancePort` now reuses the
|
|
57
|
+
authoritative `AllowanceHook` and scopes each allowance update to the
|
|
58
|
+
authenticated peer before calling `allowance(peer, asset, amount)`.
|
|
59
|
+
- Changed `Cashout` from a `#cashout { uint amount }` input command into a
|
|
60
|
+
native-only `#balance` state consumer. Removed the CASHOUT block key, spec,
|
|
61
|
+
schema, codec helpers, and test encoder; cashout now reverts `InvalidAsset`
|
|
62
|
+
for non-native balance state.
|
|
63
|
+
- Reordered `Settlement` hook inheritance to `PostHook`,
|
|
64
|
+
`DebitAccountHook`, `CreditAccountHook`, `SettleHook`, and `RepayHook`.
|
|
65
|
+
Downstream hosts combining `Settlement` with hook-bearing endpoints may need
|
|
66
|
+
to reorder their base contracts.
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- Reduced per-step pipeline overhead by replacing the general lane cursor with
|
|
71
|
+
a private four-field pipeline cursor, specializing STEP advancement around
|
|
72
|
+
decoder-established bounds, preparing handoff input once, and avoiding
|
|
73
|
+
command and flag work on paths that do not need it.
|
|
74
|
+
|
|
75
|
+
## 1.29.0
|
|
76
|
+
|
|
77
|
+
### Breaking Changes
|
|
78
|
+
|
|
79
|
+
- `ExecuteHook.execute` and the optimized local command helpers now return a
|
|
80
|
+
leading `bool handled`. A local command returned as unhandled is invoked
|
|
81
|
+
through its trusted normal external command entrypoint; handled commands keep
|
|
82
|
+
the optimized internal path and are authorized by the hook implementation.
|
|
83
|
+
- Renamed `CashoutInternal`, `DebitAccountInternal`, `CreditAccountInternal`,
|
|
84
|
+
`SettleInternal`, and `RepayInternal` to `ExecuteCashout`,
|
|
85
|
+
`ExecuteDebitAccount`, `ExecuteCreditAccount`, `ExecuteSettle`, and
|
|
86
|
+
`ExecuteRepay`.
|
|
87
|
+
|
|
11
88
|
## 1.28.0
|
|
12
89
|
|
|
13
90
|
### Breaking Changes
|
package/Codec.sol
CHANGED
|
@@ -7,7 +7,8 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
import { AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Debt, Position, Tx } from "./core/Types.sol";
|
|
8
8
|
import { Keys } from "./codec/Keys.sol";
|
|
9
9
|
import { Sizes, Specs } from "./codec/Specs.sol";
|
|
10
|
-
import { Descriptors
|
|
10
|
+
import { Descriptors } from "./codec/Descriptors.sol";
|
|
11
|
+
import { Flags } from "./utils/Flags.sol";
|
|
11
12
|
import { Schemas } from "./codec/Schema.sol";
|
|
12
13
|
import { Decoders } from "./codec/Decoders.sol";
|
|
13
14
|
import { Cursors, Cur } from "./utils/Cursors.sol";
|
package/Commands.sol
CHANGED
|
@@ -5,7 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
// Import this file for both standard Execution-based commands and custom decoders.
|
|
6
6
|
|
|
7
7
|
import {CommandBase} from "./commands/Base.sol";
|
|
8
|
-
import {Flags} from "./
|
|
8
|
+
import {Flags} from "./utils/Flags.sol";
|
|
9
9
|
import {Execution, Executions} from "./execution/Execution.sol";
|
|
10
10
|
import {Lanes} from "./utils/Lanes.sol";
|
|
11
11
|
import {Blocks} from "./codec/Blocks.sol";
|
package/Core.sol
CHANGED
|
@@ -12,7 +12,7 @@ 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 { FailedCall, NodeCalls, PortCalls,
|
|
15
|
+
import { FailedCall, NodeCalls, PortCalls, rawCall, rawQuery, tryRawCall } from "./core/Calls.sol";
|
|
16
16
|
import { EndpointBase, InputEndpointBase } from "./core/Endpoint.sol";
|
|
17
17
|
import { ExecuteHook, PipeHook, Pipeline } from "./core/Pipeline.sol";
|
|
18
18
|
import { Budget, Budgets } from "./execution/Budget.sol";
|
package/Endpoints.sol
CHANGED
|
@@ -5,7 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
// Import this file to inherit from the full rootzero callable host surface without managing individual paths.
|
|
6
6
|
|
|
7
7
|
// Shared endpoint hooks
|
|
8
|
-
import {Flags} from "./
|
|
8
|
+
import {Flags} from "./utils/Flags.sol";
|
|
9
9
|
import {ExecuteHook, PipeHook} from "./core/Pipeline.sol";
|
|
10
10
|
import {CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook} from "./core/Settlement.sol";
|
|
11
11
|
|
|
@@ -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
|
|
@@ -48,7 +48,7 @@ import {Unauthorize} from "./commands/admin/Unauthorize.sol";
|
|
|
48
48
|
// Port endpoints
|
|
49
49
|
import {PortBase} from "./ports/Base.sol";
|
|
50
50
|
import {AllowAssetsPort, DenyAssetsPort, RequestAssetPort, RequestAssetHook} from "./ports/Assets.sol";
|
|
51
|
-
import {RequestAllowancePort
|
|
51
|
+
import {RequestAllowancePort} from "./ports/Allowance.sol";
|
|
52
52
|
import {RedeemBalancePort, RedeemBalanceHook} from "./ports/Redeem.sol";
|
|
53
53
|
import {CreditAccountPort} from "./ports/Credit.sol";
|
|
54
54
|
import {DebitAccountPort} from "./ports/Debit.sol";
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ import { CommandHost, Balances } from "@rootzero/contracts/Core.sol";
|
|
|
38
38
|
import { Deposit } from "@rootzero/contracts/Endpoints.sol";
|
|
39
39
|
|
|
40
40
|
contract ExampleHost is CommandHost, Balances, Deposit {
|
|
41
|
-
constructor(
|
|
41
|
+
constructor(uint commander) CommandHost(commander) {}
|
|
42
42
|
|
|
43
43
|
function deposit(bytes32 account, bytes32 asset, uint amount) internal override {
|
|
44
44
|
uint balance = creditTo(account, asset, amount);
|
|
@@ -47,17 +47,17 @@ contract ExampleHost is CommandHost, Balances, Deposit {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
`CommandHost` requires a nonzero commander and accepts command
|
|
51
|
-
|
|
50
|
+
`CommandHost` requires a nonzero local commander host ID and accepts command
|
|
51
|
+
calls only from its embedded native caller. It has no built-in admin commands, peer registry, guardians,
|
|
52
52
|
inbound introduction endpoint, generic execution command, or native-token
|
|
53
53
|
receive function. Use `Host` instead when the application needs those advanced
|
|
54
54
|
facilities; its commands accept the commander, the host itself, and explicitly
|
|
55
55
|
authorized host callers.
|
|
56
56
|
|
|
57
|
-
Both host types introduce themselves during deployment when the
|
|
58
|
-
contract. That commander must implement
|
|
59
|
-
call, otherwise deployment reverts.
|
|
60
|
-
introduction call.
|
|
57
|
+
Both host types introduce themselves during deployment when the native target
|
|
58
|
+
encoded by the commander host ID is a contract. That commander must implement
|
|
59
|
+
`introduce(uint,uint)` and accept the call, otherwise deployment reverts. Host
|
|
60
|
+
IDs encoding EOAs do not receive an introduction call.
|
|
61
61
|
|
|
62
62
|
Host contracts are designed for fresh deployment rather than proxy upgrades.
|
|
63
63
|
Releases may change inheritance storage layout, immutable configuration, and
|
|
@@ -68,16 +68,18 @@ Commands using trusted outbound `NodeCalls` also require a `TrustAccess`
|
|
|
68
68
|
implementation. The advanced `Host` supplies one through its composed node access, while
|
|
69
69
|
`CommandHost` deliberately does not. A minimal host can explicitly compose a
|
|
70
70
|
custom trust policy, or a command that intentionally targets arbitrary nodes
|
|
71
|
-
can
|
|
71
|
+
can import the free raw-call helpers instead.
|
|
72
72
|
|
|
73
|
-
Deploy it with your
|
|
73
|
+
Deploy it with the local host ID encoding your native identity as commander and you can call its commands
|
|
74
74
|
directly. A input is a run of binary blocks — here, a single `#amount` block
|
|
75
75
|
asking to deposit an asset (the encoders are a few lines each; see
|
|
76
|
+
[`test/helpers/setup.ts`](test/helpers/setup.ts) and
|
|
76
77
|
[`test/helpers/blocks.ts`](test/helpers/blocks.ts) for reference
|
|
77
78
|
implementations):
|
|
78
79
|
|
|
79
80
|
```ts
|
|
80
|
-
const
|
|
81
|
+
const commander = await hostId(deployer.address);
|
|
82
|
+
const host = await ethers.deployContract("ExampleHost", [commander]);
|
|
81
83
|
|
|
82
84
|
const account = encodeUserAccount(user.address); // receiving account
|
|
83
85
|
const input = encodeAmountBlock(asset, 100n); // what to deposit
|
|
@@ -188,7 +190,8 @@ Structured EVM IDs use:
|
|
|
188
190
|
[uint32 type][uint32 chainid][192-bit payload]
|
|
189
191
|
```
|
|
190
192
|
|
|
191
|
-
where `type` packs
|
|
193
|
+
where `type` packs
|
|
194
|
+
`[uint8 representation][uint8 category][uint8 subtype][uint8 flags]`. A
|
|
192
195
|
structured ID announces what it is (an account, an asset, a node) and which
|
|
193
196
|
chain it lives on, and the payload usually embeds the underlying address. User
|
|
194
197
|
accounts are chain-agnostic, while admin accounts are chain-local. Guardians
|
|
@@ -229,8 +232,10 @@ which accepts `hostAsset { uint host, bytes32 asset }` entries and always applie
|
|
|
229
232
|
a zero allowance. The full `Host` composes
|
|
230
233
|
both, while smaller hosts can inherit either bundle separately.
|
|
231
234
|
|
|
232
|
-
Trust is explicit and minimal. Each host
|
|
233
|
-
|
|
235
|
+
Trust is explicit and minimal. Each host receives an immutable **commander host
|
|
236
|
+
ID** at construction. The EVM port validates that it is local, extracts its
|
|
237
|
+
native address internally for caller checks, and derives the **admin account**
|
|
238
|
+
from that native identity.
|
|
234
239
|
Other contracts become callers only when their node ID is authorized into the
|
|
235
240
|
host's trusted set, and **guardians** are accounts allowed to take protective
|
|
236
241
|
actions. At deployment, a host introduces itself to its commander, which is how
|
|
@@ -341,13 +346,16 @@ abstract contract MyCommand is CommandBase {
|
|
|
341
346
|
|
|
342
347
|
The final argument is a packed flags byte. Pass `0` for an ordinary endpoint,
|
|
343
348
|
or compose values such as `Flags.Funded`, `Flags.Admin`, and
|
|
344
|
-
`Flags.AdminFunded` from the command or endpoint package entry point.
|
|
345
|
-
|
|
346
|
-
|
|
349
|
+
`Flags.AdminFunded` from the command or endpoint package entry point.
|
|
350
|
+
The same flags byte is copied into the endpoint ID, keeping runtime behavior and
|
|
351
|
+
published descriptor metadata aligned. `Flags.Handoff` marks a command that
|
|
352
|
+
takes ownership of the remaining pipeline, while `Flags.HandoffFunded` combines
|
|
353
|
+
handoff behavior with native-value funding;
|
|
354
|
+
bit 6 remains endpoint-defined, and bits 2 through 5 remain reserved.
|
|
347
355
|
|
|
348
356
|
The standard commands cover the common ledger movements: `bootstrap` (source
|
|
349
|
-
an initial balance and native-value budget), `cashout` (withdraw
|
|
350
|
-
|
|
357
|
+
an initial balance and native-value budget), `cashout` (withdraw native
|
|
358
|
+
`#balance` state), `deposit` and
|
|
351
359
|
`depositPayable` (external funds in), `settlePayable` (funded settlement),
|
|
352
360
|
`withdraw` and `burn` (funds out),
|
|
353
361
|
`debitAccount` and `creditAccount` (internal movements), `payout` (deliver
|
|
@@ -383,19 +391,18 @@ metadata but is only executable through local pipeline execution. This is the co
|
|
|
383
391
|
`Pipeline.pipe`:
|
|
384
392
|
|
|
385
393
|
```solidity
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
(
|
|
389
|
-
|
|
394
|
+
uint cursor;
|
|
395
|
+
assembly ("memory-safe") {
|
|
396
|
+
cursor := or(steps.offset, shl(32, add(steps.offset, steps.length)))
|
|
397
|
+
}
|
|
398
|
+
while (uint32(cursor) < uint32(cursor >> 32)) {
|
|
399
|
+
uint cmd;
|
|
400
|
+
uint value;
|
|
401
|
+
(cmd, value, cursor) = takeStep(cursor);
|
|
402
|
+
if (value > budget) revert InsufficientValue();
|
|
390
403
|
unchecked { budget -= value; }
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
(state, credit) = execute(cmd, account, state, input, value);
|
|
394
|
-
} else {
|
|
395
|
-
ensureTrusted(cmd);
|
|
396
|
-
(state, credit) = rawCommandCall(selector, target, value, account, state, input);
|
|
397
|
-
}
|
|
398
|
-
budget += credit;
|
|
404
|
+
(state, value, cursor) = run(cmd, account, state, value, cursor);
|
|
405
|
+
budget += value;
|
|
399
406
|
}
|
|
400
407
|
if (state.length != 0) revert UnexpectedState();
|
|
401
408
|
```
|
|
@@ -404,6 +411,42 @@ if (state.length != 0) revert UnexpectedState();
|
|
|
404
411
|
the remaining budget after every step has executed. The enclosing entrypoint
|
|
405
412
|
settles that final value once.
|
|
406
413
|
|
|
414
|
+
The EVM pipeline is deliberately coupled to the canonical wire layout for gas
|
|
415
|
+
efficiency. It extracts command selectors, targets, and flags directly from
|
|
416
|
+
command IDs and writes CONTEXT, BYTES, and RELAY blocks directly in assembly.
|
|
417
|
+
Any change to those ID fields or block encodings must update `Pipeline` at the
|
|
418
|
+
same time; the general node and block helpers are not used on this hot path.
|
|
419
|
+
|
|
420
|
+
A handoff command retains the ordinary command subtype and carries
|
|
421
|
+
`Flags.Handoff` in both its ID and descriptor. The reserved handoff envelope is:
|
|
422
|
+
|
|
423
|
+
```txt
|
|
424
|
+
relay { #bytes as input, #bytes as steps }
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
`input` is the handoff STEP's ordinary command input, while `steps` is the
|
|
428
|
+
untouched remainder of the original pipeline. `Pipeline.pipe` constructs this
|
|
429
|
+
envelope automatically for commands carrying `Flags.Handoff`, calls the command,
|
|
430
|
+
and stops executing the transferred continuation locally. Pipeline authors
|
|
431
|
+
therefore encode only the command's ordinary input in the handoff STEP.
|
|
432
|
+
|
|
433
|
+
Handoff has four operational rules:
|
|
434
|
+
|
|
435
|
+
- A host-local `execute` hook must return `handled = false` for handoff command
|
|
436
|
+
IDs. The hook sees only ordinary STEP input; delegation lets `Pipeline`
|
|
437
|
+
construct the RELAY envelope containing the continuation.
|
|
438
|
+
- Handoff transfers STEP bytes, not the pipeline's complete native-value
|
|
439
|
+
budget. The handoff command receives its assigned STEP value. Returned credit
|
|
440
|
+
rejoins the source budget, and the enclosing source entrypoint settles the
|
|
441
|
+
final remainder normally.
|
|
442
|
+
- The handoff command must consume or forward the current state and return empty
|
|
443
|
+
state. Because the continuation is no longer executed locally, returned
|
|
444
|
+
non-empty state fails pipeline finalization with `UnexpectedState`.
|
|
445
|
+
- A transport that completes asynchronously should relinquish only state that
|
|
446
|
+
is safe before destination success is known. The standard relay commands are
|
|
447
|
+
intended for empty or balance state; debt, position, and similarly fallible
|
|
448
|
+
state must not be relayed this way.
|
|
449
|
+
|
|
407
450
|
A transfer, for instance, is a two-step pipeline: `debitAccount` turns an
|
|
408
451
|
`#amount` input into `#balance` state, and `payout` consumes that state
|
|
409
452
|
toward a recipient. Because a pipeline is just blocks, it is also the unit of
|
|
@@ -414,15 +457,18 @@ parameters. A `resources` word is never itself native value; EVM adapters use
|
|
|
414
457
|
`useResourceValue` to extract its low 128-bit value lane before spending it.
|
|
415
458
|
|
|
416
459
|
Hosts that implement a pipeline locally can inherit `Bootstrap`,
|
|
417
|
-
`
|
|
418
|
-
`
|
|
419
|
-
`
|
|
460
|
+
`ExecuteCashout`, `ExecuteDebitAccount`, `ExecuteCreditAccount`,
|
|
461
|
+
`ExecuteSettle`, and
|
|
462
|
+
`ExecuteRepay` to register canonical command metadata while executing
|
|
420
463
|
their local command IDs through `executeBootstrap`, `executeCashout`,
|
|
421
464
|
`executeDebitAccount`, `executeCreditAccount`, `executeSettle`, and
|
|
422
|
-
`executeRepay`. The bootstrap
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
465
|
+
`executeRepay`. The bootstrap and debit adapters decode fixed-stride calldata
|
|
466
|
+
input directly; cashout, credit, settle, and repay decode memory-backed pipeline
|
|
467
|
+
state. All return `handled = true` and avoid an
|
|
468
|
+
external self-call. The host's `execute` hook must authorize a command before
|
|
469
|
+
returning true. Returning `handled = false` delegates a local command to its
|
|
470
|
+
trusted normal external entrypoint. Pass the step value into each adapter.
|
|
471
|
+
Bootstrap is pipeline-local rather than an
|
|
426
472
|
externally callable command and may consume value for native-asset balance;
|
|
427
473
|
the other five reject nonzero value because those commands are non-funded.
|
|
428
474
|
|
|
@@ -460,8 +506,14 @@ the descriptor's lanes through the published block schemas.
|
|
|
460
506
|
|
|
461
507
|
## Ports
|
|
462
508
|
|
|
463
|
-
Ports are the host-to-host surfaces, callable only by trusted peer hosts.
|
|
464
|
-
|
|
509
|
+
Ports are the host-to-host surfaces, callable only by trusted peer hosts. By
|
|
510
|
+
default, trusting a peer means the host has verified that peer and permits it to
|
|
511
|
+
use the functionality of every port the host exposes without another policy
|
|
512
|
+
decision inside each port. A host that needs narrower capabilities can add
|
|
513
|
+
per-port or per-operation checks in its hook implementations, or expose custom
|
|
514
|
+
ports with a different access model.
|
|
515
|
+
|
|
516
|
+
The central ports are batches all the way down:
|
|
465
517
|
|
|
466
518
|
- `portPost` consumes `transaction { bytes32 from, bytes32 to, bytes32 asset,
|
|
467
519
|
uint amount }` blocks, debiting `from` and crediting `to` per
|
|
@@ -470,14 +522,16 @@ central ones are batches all the way down:
|
|
|
470
522
|
passes the authenticated peer, asset, and amount to a host hook. The hook
|
|
471
523
|
validates asset support and applies the host's request and transfer policy.
|
|
472
524
|
- `portRequestAllowance` consumes the same amount blocks and lets the
|
|
473
|
-
authenticated peer
|
|
474
|
-
|
|
525
|
+
authenticated peer set its own asset allowance through the same authoritative
|
|
526
|
+
hook used by the admin allowance command.
|
|
475
527
|
- `portPipePayable` consumes `context` blocks, each carrying an account, an
|
|
476
528
|
initial state, and a run of steps — a complete pipeline delivered by another
|
|
477
529
|
host, executed locally against the port call's shared value budget.
|
|
478
530
|
|
|
479
|
-
This is also the cross-portal mechanism. `relayPayable
|
|
480
|
-
|
|
531
|
+
This is also the cross-portal mechanism. `relayPayable` and
|
|
532
|
+
`relayBalancePayable` are handoff commands whose transport hooks decode their
|
|
533
|
+
own destination and resource input, while `portDispatchPayable` dispatches an
|
|
534
|
+
explicit portal payload. The adapter wraps and addresses the destination pipe;
|
|
481
535
|
a bridge adapter moves the **raw
|
|
482
536
|
bytes**; the destination host parses them with the same cursor rules and runs
|
|
483
537
|
the same pipeline loop. Nothing in the payload is EVM-specific — step commands
|
package/Utils.sol
CHANGED
|
@@ -10,8 +10,9 @@ 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
|
|
13
|
+
import { Nodes } from "./utils/Nodes.sol";
|
|
14
14
|
import { Layout } from "./utils/Layout.sol";
|
|
15
|
+
import { Flags } from "./utils/Flags.sol";
|
|
15
16
|
import { BadAmount, InsufficientValue, InvalidAccount, InvalidAsset, InvalidContract, InvalidId, InvalidPreimage, MissingCursor, NotDivisible, OutOfBounds, UnauthorizedAsset, UnconsumedData, UnexpectedPosition, ValueOverflow, ZeroAddress, ZeroAmount} from "./utils/Errors.sol";
|
|
16
17
|
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";
|
|
17
18
|
|
package/codec/Blocks.sol
CHANGED
|
@@ -380,20 +380,6 @@ library Blocks {
|
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
/// @notice Write a CASHOUT block at `i`.
|
|
384
|
-
/// @dev DANGER: Unchecked memory write. Reserve `Sizes.Cashout` bytes first.
|
|
385
|
-
/// @param dst Destination buffer.
|
|
386
|
-
/// @param i Relative write position.
|
|
387
|
-
/// @param amount Native-asset amount to withdraw.
|
|
388
|
-
function writeCashout(bytes memory dst, uint i, uint amount) internal pure {
|
|
389
|
-
uint spec = Specs.Cashout;
|
|
390
|
-
assembly ("memory-safe") {
|
|
391
|
-
let p := add(add(dst, 0x20), i)
|
|
392
|
-
mstore(p, spec)
|
|
393
|
-
mstore(add(p, 0x08), amount)
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
383
|
// Two-word payloads
|
|
398
384
|
|
|
399
385
|
/// @notice Write an AMOUNT block at `i`.
|
|
@@ -808,23 +794,23 @@ library Blocks {
|
|
|
808
794
|
/// block size and ensure the encoded payload length fits in uint32.
|
|
809
795
|
/// @param dst Destination buffer.
|
|
810
796
|
/// @param i Relative write position.
|
|
811
|
-
/// @param
|
|
812
|
-
/// @param
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
uint len = 64 + Sizes.Header + input.length;
|
|
797
|
+
/// @param input Command-specific handoff input.
|
|
798
|
+
/// @param steps Remaining pipeline steps.
|
|
799
|
+
function writeRelay(bytes memory dst, uint i, bytes memory input, bytes memory steps) internal pure {
|
|
800
|
+
uint len = 2 * Sizes.Header + input.length + steps.length;
|
|
816
801
|
uint key = uint32(Keys.Relay);
|
|
817
802
|
uint byteskey = uint32(Keys.Bytes);
|
|
818
803
|
assembly ("memory-safe") {
|
|
819
804
|
let p := add(add(dst, 0x20), i)
|
|
820
805
|
mstore(p, or(shl(224, key), shl(192, len)))
|
|
821
|
-
|
|
822
|
-
mstore(add(p, 0x28), resources)
|
|
823
|
-
|
|
824
|
-
let q := add(p, 0x48)
|
|
806
|
+
let q := add(p, 0x08)
|
|
825
807
|
let inputlen := mload(input)
|
|
826
808
|
mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
|
|
827
809
|
mcopy(add(q, 0x08), add(input, 0x20), inputlen)
|
|
810
|
+
q := add(add(q, 0x08), inputlen)
|
|
811
|
+
let stepslen := mload(steps)
|
|
812
|
+
mstore(q, or(shl(224, byteskey), shl(192, stepslen)))
|
|
813
|
+
mcopy(add(q, 0x08), add(steps, 0x20), stepslen)
|
|
828
814
|
}
|
|
829
815
|
}
|
|
830
816
|
|
|
@@ -1053,9 +1039,23 @@ library Blocks {
|
|
|
1053
1039
|
copyComposite(dst, i, Keys.Call, target, resources, payload);
|
|
1054
1040
|
}
|
|
1055
1041
|
|
|
1056
|
-
/// @notice Encode a RELAY block at `i`, copying its nested
|
|
1057
|
-
function copyRelay(bytes memory dst, uint i,
|
|
1058
|
-
|
|
1042
|
+
/// @notice Encode a RELAY block at `i`, copying its nested streams from calldata.
|
|
1043
|
+
function copyRelay(bytes memory dst, uint i, bytes calldata input, bytes calldata steps) internal pure {
|
|
1044
|
+
uint len = 2 * Sizes.Header + input.length + steps.length;
|
|
1045
|
+
uint key = uint32(Keys.Relay);
|
|
1046
|
+
uint byteskey = uint32(Keys.Bytes);
|
|
1047
|
+
assembly ("memory-safe") {
|
|
1048
|
+
let p := add(add(dst, 0x20), i)
|
|
1049
|
+
mstore(p, or(shl(224, key), shl(192, len)))
|
|
1050
|
+
let q := add(p, 0x08)
|
|
1051
|
+
let inputlen := input.length
|
|
1052
|
+
mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
|
|
1053
|
+
calldatacopy(add(q, 0x08), input.offset, inputlen)
|
|
1054
|
+
q := add(add(q, 0x08), inputlen)
|
|
1055
|
+
let stepslen := steps.length
|
|
1056
|
+
mstore(q, or(shl(224, byteskey), shl(192, stepslen)))
|
|
1057
|
+
calldatacopy(add(q, 0x08), steps.offset, stepslen)
|
|
1058
|
+
}
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
1061
|
/// @notice Encode a DISPATCH block at `i`, copying its nested payload from calldata.
|
|
@@ -1458,18 +1458,6 @@ library Blocks {
|
|
|
1458
1458
|
if (head >> 192 != Specs.Status >> 192) revert InvalidBlock();
|
|
1459
1459
|
}
|
|
1460
1460
|
|
|
1461
|
-
/// @notice Decode a low-level fixed-width CASHOUT block at `abs`.
|
|
1462
|
-
/// @param abs Absolute block position.
|
|
1463
|
-
/// @return amount Native-asset amount to withdraw.
|
|
1464
|
-
function unpackCashout(uint abs) internal pure returns (uint amount) {
|
|
1465
|
-
uint head;
|
|
1466
|
-
assembly ("memory-safe") {
|
|
1467
|
-
head := calldataload(abs)
|
|
1468
|
-
amount := calldataload(add(abs, 0x08))
|
|
1469
|
-
}
|
|
1470
|
-
if (head >> 192 != Specs.Cashout >> 192) revert InvalidBlock();
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
1461
|
// Two-word payloads
|
|
1474
1462
|
|
|
1475
1463
|
/// @notice Decode a low-level fixed-width AMOUNT block at `abs`.
|
|
@@ -1866,22 +1854,18 @@ library Blocks {
|
|
|
1866
1854
|
if (end != limit) revert InvalidBlock();
|
|
1867
1855
|
}
|
|
1868
1856
|
|
|
1869
|
-
/// @notice Decode one RELAY block and its nested input.
|
|
1857
|
+
/// @notice Decode one RELAY block and its nested input and continuation.
|
|
1870
1858
|
/// @param abs Absolute block position.
|
|
1871
|
-
/// @return
|
|
1872
|
-
/// @return
|
|
1873
|
-
/// @return input Decoded relay input.
|
|
1859
|
+
/// @return input Decoded command-specific input.
|
|
1860
|
+
/// @return steps Decoded remaining pipeline steps.
|
|
1874
1861
|
/// @return end Absolute position after the block.
|
|
1875
1862
|
function unpackRelay(
|
|
1876
1863
|
uint abs
|
|
1877
|
-
) internal pure returns (
|
|
1864
|
+
) internal pure returns (bytes calldata input, bytes calldata steps, uint end) {
|
|
1878
1865
|
uint limit;
|
|
1879
1866
|
(abs, limit) = expectKey(abs, Keys.Relay);
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
resources := calldataload(add(abs, 0x20))
|
|
1883
|
-
}
|
|
1884
|
-
(input, end) = unpackBytes(abs + 64);
|
|
1867
|
+
(input, abs) = unpackBytes(abs);
|
|
1868
|
+
(steps, end) = unpackBytes(abs);
|
|
1885
1869
|
if (end != limit) revert InvalidBlock();
|
|
1886
1870
|
}
|
|
1887
1871
|
|
|
@@ -2119,14 +2103,6 @@ library Blocks {
|
|
|
2119
2103
|
writeBootstrap(value, 0, asset, amount, budget);
|
|
2120
2104
|
}
|
|
2121
2105
|
|
|
2122
|
-
/// @notice Encode a CASHOUT block.
|
|
2123
|
-
/// @param amount Native-asset amount to withdraw.
|
|
2124
|
-
/// @return value Encoded CASHOUT block bytes.
|
|
2125
|
-
function createCashout(uint amount) internal pure returns (bytes memory value) {
|
|
2126
|
-
value = allocate(Sizes.Cashout);
|
|
2127
|
-
writeCashout(value, 0, amount);
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
2106
|
/// @notice Encode an AMOUNT block.
|
|
2131
2107
|
/// @param asset Asset identifier.
|
|
2132
2108
|
/// @param amount Token amount.
|
|
@@ -2235,22 +2211,20 @@ library Blocks {
|
|
|
2235
2211
|
}
|
|
2236
2212
|
|
|
2237
2213
|
/// @notice Encode a RELAY block.
|
|
2238
|
-
/// @param
|
|
2239
|
-
///
|
|
2240
|
-
/// @param resources Chain-specific resources for the destination context.
|
|
2241
|
-
/// @param input Nested input block stream.
|
|
2214
|
+
/// @param input Nested command-specific input block stream.
|
|
2215
|
+
/// @param steps Nested remaining STEP block stream.
|
|
2242
2216
|
/// @return value Encoded RELAY block bytes.
|
|
2243
|
-
function createRelay(
|
|
2244
|
-
uint len = max32(
|
|
2217
|
+
function createRelay(bytes memory input, bytes memory steps) internal pure returns (bytes memory value) {
|
|
2218
|
+
uint len = max32(3 * Sizes.Header + input.length + steps.length);
|
|
2245
2219
|
value = allocate(len);
|
|
2246
|
-
writeRelay(value, 0,
|
|
2220
|
+
writeRelay(value, 0, input, steps);
|
|
2247
2221
|
}
|
|
2248
2222
|
|
|
2249
|
-
/// @notice Encode a RELAY block by copying its nested
|
|
2250
|
-
function createRelayCopy(
|
|
2251
|
-
uint len = max32(
|
|
2223
|
+
/// @notice Encode a RELAY block by copying its nested streams from calldata.
|
|
2224
|
+
function createRelayCopy(bytes calldata input, bytes calldata steps) internal pure returns (bytes memory value) {
|
|
2225
|
+
uint len = max32(3 * Sizes.Header + input.length + steps.length);
|
|
2252
2226
|
value = allocate(len);
|
|
2253
|
-
copyRelay(value, 0,
|
|
2227
|
+
copyRelay(value, 0, input, steps);
|
|
2254
2228
|
}
|
|
2255
2229
|
|
|
2256
2230
|
/// @notice Encode a DISPATCH block.
|