@rootzero/contracts 1.12.0 → 1.14.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 +76 -8
- package/Codec.sol +21 -0
- package/Commands.sol +14 -0
- package/Core.sol +4 -4
- package/Endpoints.sol +5 -7
- package/README.md +62 -35
- package/Utils.sol +2 -4
- package/codec/Blocks.sol +1606 -0
- package/codec/Buffers.sol +165 -0
- package/codec/Decoders.sol +558 -0
- package/codec/Descriptors.sol +124 -0
- package/{blocks → codec}/Keys.sol +4 -28
- package/codec/Readers.sol +114 -0
- package/{blocks → codec}/Schema.sol +38 -80
- package/codec/Specs.sol +218 -0
- package/codec/Writers.sol +487 -0
- package/commands/Allocate.sol +50 -0
- package/commands/Base.sol +71 -37
- package/commands/Burn.sol +19 -13
- package/commands/Credit.sol +25 -31
- package/commands/Debit.sol +28 -38
- package/commands/Deposit.sol +39 -39
- package/commands/Payout.sol +22 -15
- package/commands/Provision.sol +40 -40
- package/commands/Recover.sol +21 -20
- package/commands/Relay.sol +25 -23
- package/commands/Withdraw.sol +18 -20
- package/commands/admin/AllowAssets.sol +20 -16
- package/commands/admin/Allowance.sol +19 -13
- package/commands/admin/Appoint.sol +18 -15
- package/commands/admin/Authorize.sol +24 -14
- package/commands/admin/Base.sol +1 -1
- package/commands/admin/DenyAssets.sol +20 -16
- package/commands/admin/Dismiss.sol +18 -15
- package/commands/admin/Execute.sol +20 -18
- package/commands/admin/Label.sol +18 -13
- package/commands/admin/Schemas.sol +19 -14
- package/commands/admin/Unauthorize.sol +24 -14
- package/core/Calls.sol +7 -14
- package/core/Endpoint.sol +43 -126
- package/core/Host.sol +10 -2
- package/core/Pipeline.sol +29 -25
- package/core/Settlement.sol +39 -0
- package/core/Types.sol +1 -1
- package/docs/Schema.md +38 -32
- package/events/Endpoint.sol +2 -2
- package/events/Introduction.sol +4 -3
- package/events/Schema.sol +5 -5
- package/execution/Budget.sol +40 -0
- package/execution/Execution.sol +1083 -0
- package/guards/Base.sol +8 -9
- package/guards/Revoke.sol +11 -9
- package/package.json +1 -1
- package/ports/AllowAssets.sol +12 -15
- package/ports/Allowance.sol +11 -9
- package/ports/Base.sol +18 -11
- package/ports/Credit.sol +11 -9
- package/ports/Debit.sol +11 -9
- package/ports/DenyAssets.sol +10 -13
- package/ports/Dispatch.sol +13 -15
- package/ports/Pipe.sol +14 -12
- package/ports/Redeem.sol +12 -9
- package/ports/Settle.sol +13 -13
- package/queries/Assets.sol +15 -15
- package/queries/Balances.sol +17 -17
- package/queries/Base.sol +11 -12
- package/utils/Actions.sol +1 -0
- package/utils/Cursors.sol +308 -0
- package/utils/Lanes.sol +14 -0
- package/utils/Selectors.sol +2 -2
- package/utils/Utils.sol +46 -0
- package/Cursors.sol +0 -16
- package/blocks/Cursors.sol +0 -1400
- package/blocks/Writers.sol +0 -1028
- package/core/Payable.sol +0 -53
- package/utils/Value.sol +0 -43
|
@@ -7,38 +7,20 @@ pragma solidity ^0.8.33;
|
|
|
7
7
|
/// Custom block keys only need to be unique in the context where they are used;
|
|
8
8
|
/// hosts may publish custom key meanings with the `Schema` event.
|
|
9
9
|
library Keys {
|
|
10
|
-
/// @notice Create a context-local block key.
|
|
11
|
-
/// @dev Local keys are opaque tags for host- or endpoint-specific schemas.
|
|
12
|
-
/// The caller is responsible for choosing values that are unique in the
|
|
13
|
-
/// context where they are used and publishing their meaning with `Schema`.
|
|
14
|
-
/// @param value Opaque local key value.
|
|
15
|
-
/// @return Context-local block key.
|
|
16
|
-
function local(uint32 value) internal pure returns (bytes4) {
|
|
17
|
-
return bytes4(value);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
10
|
/// @dev Empty / unset key.
|
|
21
11
|
bytes4 constant Empty = bytes4(0);
|
|
22
12
|
/// @dev Wildcard key used in discovery when any block stream is accepted.
|
|
23
13
|
bytes4 constant Any = 0xffffffff;
|
|
24
|
-
/// @dev Default context-local block key for hosts/endpoints that need one custom schema.
|
|
25
|
-
bytes4 constant Local = bytes4(uint32(1));
|
|
26
14
|
/// @dev Input amount - (bytes32 asset, uint amount)
|
|
27
15
|
bytes4 constant Amount = bytes4(keccak256("#amount"));
|
|
28
16
|
/// @dev Ledger balance - (bytes32 asset, uint amount)
|
|
29
17
|
bytes4 constant Balance = bytes4(keccak256("#balance"));
|
|
30
|
-
/// @dev
|
|
31
|
-
bytes4 constant BalanceLimit = bytes4(keccak256("#balanceLimit"));
|
|
32
|
-
/// @dev Host-scoped request amount - (uint host, bytes32 asset, uint amount)
|
|
18
|
+
/// @dev Host-scoped input amount - (uint host, bytes32 asset, uint amount)
|
|
33
19
|
bytes4 constant Allocation = bytes4(keccak256("#allocation"));
|
|
34
20
|
/// @dev Host-scoped allowance cap - (uint host, bytes32 asset, uint amount)
|
|
35
21
|
bytes4 constant Allowance = bytes4(keccak256("#allowance"));
|
|
36
22
|
/// @dev Cross-host custody state - (uint host, bytes32 asset, uint amount)
|
|
37
23
|
bytes4 constant Custody = bytes4(keccak256("#custody"));
|
|
38
|
-
/// @dev Cross-host custody constraint - (uint host, bytes32 asset, uint min, uint max)
|
|
39
|
-
bytes4 constant CustodyLimit = bytes4(keccak256("#custodyLimit"));
|
|
40
|
-
/// @dev Fee amount - (uint amount)
|
|
41
|
-
bytes4 constant Fee = bytes4(keccak256("#fee"));
|
|
42
24
|
/// @dev List wrapper; payload is an embedded repeated block stream
|
|
43
25
|
bytes4 constant List = bytes4(keccak256("#list"));
|
|
44
26
|
/// @dev EVM-encoded payload field; layout follows standard ABI tuple encoding
|
|
@@ -51,11 +33,11 @@ library Keys {
|
|
|
51
33
|
bytes4 constant Account = bytes4(keccak256("#account"));
|
|
52
34
|
/// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, uint amount)
|
|
53
35
|
bytes4 constant Transaction = bytes4(keccak256("#transaction"));
|
|
54
|
-
/// @dev Sub-command invocation - (uint
|
|
36
|
+
/// @dev Sub-command invocation - (uint cmd, uint resources, #bytes as input)
|
|
55
37
|
bytes4 constant Step = bytes4(keccak256("#step"));
|
|
56
|
-
/// @dev Portal relay
|
|
38
|
+
/// @dev Portal relay input - (uint portal, uint resources, #bytes as input)
|
|
57
39
|
bytes4 constant Relay = bytes4(keccak256("#relay"));
|
|
58
|
-
/// @dev Command context transport - (bytes32 account, #bytes as state, #bytes as
|
|
40
|
+
/// @dev Command context transport - (bytes32 account, #bytes as state, #bytes as input)
|
|
59
41
|
bytes4 constant Context = bytes4(keccak256("#context"));
|
|
60
42
|
/// @dev Recoverable witness - (uint handler, uint resources, bytes32 key, #bytes as witness)
|
|
61
43
|
bytes4 constant Recover = bytes4(keccak256("#recover"));
|
|
@@ -63,14 +45,10 @@ library Keys {
|
|
|
63
45
|
bytes4 constant Dispatch = bytes4(keccak256("#dispatch"));
|
|
64
46
|
/// @dev Raw external call - (uint target, uint resources, #bytes as payload)
|
|
65
47
|
bytes4 constant Call = bytes4(keccak256("#call"));
|
|
66
|
-
/// @dev Authentication proof - (uint cid, uint deadline, #bytes as proof); must appear last in its segment
|
|
67
|
-
bytes4 constant Auth = bytes4(keccak256("#auth"));
|
|
68
48
|
/// @dev Asset descriptor without amount - (bytes32 asset)
|
|
69
49
|
bytes4 constant Asset = bytes4(keccak256("#asset"));
|
|
70
50
|
/// @dev Node identifier - (uint id)
|
|
71
51
|
bytes4 constant Node = bytes4(keccak256("#node"));
|
|
72
|
-
/// @dev Relayer bounty - (uint amount, bytes32 relayer)
|
|
73
|
-
bytes4 constant Bounty = bytes4(keccak256("#bounty"));
|
|
74
52
|
/// @dev Mutable node label - (uint id, bytes32 namespace, #string as name)
|
|
75
53
|
bytes4 constant Label = bytes4(keccak256("#label"));
|
|
76
54
|
/// @dev Block schema publication - (bytes4 key, #string as body, bytes32 name)
|
|
@@ -78,8 +56,6 @@ library Keys {
|
|
|
78
56
|
|
|
79
57
|
/// @dev Structural status form - (uint code)
|
|
80
58
|
bytes4 constant Status = bytes4(keccak256("#status"));
|
|
81
|
-
/// @dev Structural asset amount form - (bytes32 asset, uint amount)
|
|
82
|
-
bytes4 constant AssetAmount = bytes4(keccak256("#assetAmount"));
|
|
83
59
|
/// @dev Structural account asset form - (bytes32 account, bytes32 asset)
|
|
84
60
|
bytes4 constant AccountAsset = bytes4(keccak256("#accountAsset"));
|
|
85
61
|
/// @dev Structural account amount form - (bytes32 account, bytes32 asset, uint amount)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {Sizes, Specs} from "./Specs.sol";
|
|
5
|
+
import {Keys} from "./Keys.sol";
|
|
6
|
+
|
|
7
|
+
/// @notice Mutable reader over a block stream stored in memory.
|
|
8
|
+
/// All positions (`i`) are byte offsets relative to the start of `source`.
|
|
9
|
+
struct Reader {
|
|
10
|
+
/// @dev Current read position, relative to the source start.
|
|
11
|
+
uint i;
|
|
12
|
+
/// @dev Memory bytes containing the complete source region.
|
|
13
|
+
bytes source;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
using Readers for Reader;
|
|
17
|
+
|
|
18
|
+
/// @title Readers
|
|
19
|
+
/// @notice Memory block stream parser for the rootzero protocol.
|
|
20
|
+
/// A `Reader` advances through an existing `bytes memory` source without copying its contents.
|
|
21
|
+
/// Blocks are encoded as `[bytes4 key][bytes4 payloadLen][payload]`.
|
|
22
|
+
library Readers {
|
|
23
|
+
/// @dev The current block has a truncated header or payload, an unexpected key,
|
|
24
|
+
/// or a payload size outside the accepted range.
|
|
25
|
+
error InvalidBlock();
|
|
26
|
+
|
|
27
|
+
/// @notice Create a reader backed by a memory byte array.
|
|
28
|
+
/// @param source Memory bytes containing the block stream.
|
|
29
|
+
/// @return cur Reader positioned at the beginning of `source`.
|
|
30
|
+
function open(bytes memory source) internal pure returns (Reader memory cur) {
|
|
31
|
+
cur.source = source;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @notice Return whether the reader has consumed its entire source.
|
|
35
|
+
/// @param cur Reader whose position should be checked.
|
|
36
|
+
/// @return Whether `cur.i` equals the source length.
|
|
37
|
+
function done(Reader memory cur) internal pure returns (bool) {
|
|
38
|
+
return cur.i == cur.source.length;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @notice Return whether the reader has bytes left to consume.
|
|
42
|
+
/// @param cur Reader whose position should be checked.
|
|
43
|
+
/// @return Whether `cur.i` differs from the source length.
|
|
44
|
+
function more(Reader memory cur) internal pure returns (bool) {
|
|
45
|
+
return cur.i != cur.source.length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @notice Validate and consume the current block, advancing `cur.i` past it.
|
|
49
|
+
/// @param cur Reader to advance.
|
|
50
|
+
/// @param key Expected block key.
|
|
51
|
+
/// @param min Minimum payload length.
|
|
52
|
+
/// @param max Maximum payload length; zero means unbounded.
|
|
53
|
+
/// @return abs Absolute memory address of the payload start.
|
|
54
|
+
function consume(
|
|
55
|
+
Reader memory cur,
|
|
56
|
+
bytes4 key,
|
|
57
|
+
uint min,
|
|
58
|
+
uint max
|
|
59
|
+
) private pure returns (uint abs) {
|
|
60
|
+
bytes memory source = cur.source;
|
|
61
|
+
uint i = cur.i;
|
|
62
|
+
|
|
63
|
+
if (i > source.length || source.length - i < Sizes.Header) revert InvalidBlock();
|
|
64
|
+
|
|
65
|
+
bytes4 current;
|
|
66
|
+
uint len;
|
|
67
|
+
assembly ("memory-safe") {
|
|
68
|
+
let header := mload(add(add(source, 0x20), i))
|
|
69
|
+
current := header
|
|
70
|
+
len := and(shr(192, header), 0xffffffff)
|
|
71
|
+
abs := add(add(source, 0x28), i)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (current != key || len < min || (max != 0 && len > max)) revert InvalidBlock();
|
|
75
|
+
if (len > source.length - i - Sizes.Header) revert InvalidBlock();
|
|
76
|
+
cur.i = i + Sizes.Header + len;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// @notice Validate and consume the current block described by `spec`.
|
|
80
|
+
function consume(Reader memory cur, uint spec) internal pure returns (uint abs) {
|
|
81
|
+
(bytes4 key, uint32 min, uint32 max) = Specs.decode(spec);
|
|
82
|
+
return consume(cur, key, min, max);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @notice Consume a BALANCE block and return its fields.
|
|
86
|
+
/// @param cur Reader; advanced past the block.
|
|
87
|
+
/// @return asset Asset identifier.
|
|
88
|
+
/// @return amount Token amount.
|
|
89
|
+
function unpackBalance(Reader memory cur) internal pure returns (bytes32 asset, uint amount) {
|
|
90
|
+
uint abs = consume(cur, Keys.Balance, 64, 64);
|
|
91
|
+
assembly ("memory-safe") {
|
|
92
|
+
asset := mload(abs)
|
|
93
|
+
amount := mload(add(abs, 0x20))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @notice Consume a TRANSACTION block and return its fields.
|
|
98
|
+
/// @param cur Reader; advanced past the block.
|
|
99
|
+
/// @return from Source account identifier.
|
|
100
|
+
/// @return to Destination account identifier.
|
|
101
|
+
/// @return asset Asset identifier.
|
|
102
|
+
/// @return amount Token amount.
|
|
103
|
+
function unpackTransaction(
|
|
104
|
+
Reader memory cur
|
|
105
|
+
) internal pure returns (bytes32 from, bytes32 to, bytes32 asset, uint amount) {
|
|
106
|
+
uint abs = consume(cur, Keys.Transaction, 128, 128);
|
|
107
|
+
assembly ("memory-safe") {
|
|
108
|
+
from := mload(abs)
|
|
109
|
+
to := mload(add(abs, 0x20))
|
|
110
|
+
asset := mload(add(abs, 0x40))
|
|
111
|
+
amount := mload(add(abs, 0x60))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -12,7 +12,7 @@ pragma solidity ^0.8.33;
|
|
|
12
12
|
// - an empty schema string means the block has no structured payload
|
|
13
13
|
// - commas separate siblings at every level
|
|
14
14
|
// - braces define the current block payload body
|
|
15
|
-
// - command
|
|
15
|
+
// - command inputs are a single run when the input schema is non-empty
|
|
16
16
|
// - command state is a single active state run without trailing globals
|
|
17
17
|
// - run items may repeat at top level for batching
|
|
18
18
|
// - `maybe #x` marks an optional block item
|
|
@@ -36,11 +36,11 @@ pragma solidity ^0.8.33;
|
|
|
36
36
|
// - generic lists use the stable key derived from `#list`
|
|
37
37
|
// - standard keys are derived from block aliases, e.g. bytes4(keccak256("#amount"))
|
|
38
38
|
// - custom keys are opaque bytes4 tags and only need to be unique in their
|
|
39
|
-
// active context; use `Schema(host,
|
|
39
|
+
// active context; use `Schema(host, spec, schema, name)` to publish their meaning
|
|
40
40
|
// - see `docs/Schema.md` for the full working spec
|
|
41
41
|
//
|
|
42
42
|
// Pipeline state:
|
|
43
|
-
// - command
|
|
43
|
+
// - command input and state streams are each a single run of blocks under the
|
|
44
44
|
// current protocol convention; the block format may support other shapes in
|
|
45
45
|
// future protocol surfaces
|
|
46
46
|
// - `balance(...)` and `custody(...)` are live, linear state in the active command pipeline
|
|
@@ -48,102 +48,60 @@ pragma solidity ^0.8.33;
|
|
|
48
48
|
// - while a balance or custody is in-flight as pipeline state, it is not simultaneously persisted
|
|
49
49
|
// in another ledger/store by this protocol
|
|
50
50
|
// - commands must preserve, transform, settle, or intentionally consume pipeline state
|
|
51
|
-
// -
|
|
51
|
+
// - input blocks such as `amount(...)`, `allocation(...)`, and `allowance(...)`
|
|
52
52
|
// express intent, constraints, or references
|
|
53
|
-
// -
|
|
53
|
+
// - input and value/response blocks are not live state
|
|
54
54
|
//
|
|
55
|
-
// Signed blocks:
|
|
56
|
-
// - an authenticated input segment ends with one trailing AUTH block
|
|
57
|
-
// - only the final AUTH is treated specially; earlier AUTH blocks remain ordinary signed bytes
|
|
58
|
-
// - the signed slice runs from the segment start through the AUTH head, excluding only AUTH proof bytes
|
|
59
|
-
// - `cid` binds the signature to one command; `deadline` acts as expiry and nonce
|
|
60
|
-
// - current helpers assume proof layout `[bytes20 signer][bytes65 sig]`
|
|
61
|
-
|
|
62
55
|
/// @title Schemas
|
|
63
56
|
/// @notice Human-readable schema string constants for each block type.
|
|
64
57
|
/// These strings describe payload layout for discovery events and docs; block
|
|
65
58
|
/// aliases map to standard keys by convention. Custom blocks may use any unique
|
|
66
59
|
/// bytes4 key in their active context.
|
|
67
60
|
library Schemas {
|
|
61
|
+
// Empty and reserved payloads
|
|
62
|
+
|
|
68
63
|
string constant Unit = "";
|
|
69
|
-
string constant Node = "{ uint id }";
|
|
70
|
-
string constant Account = "{ bytes32 account }";
|
|
71
|
-
string constant Asset = "{ bytes32 asset }";
|
|
72
|
-
string constant Amount = "{ bytes32 asset, uint amount }";
|
|
73
|
-
string constant Balance = "{ bytes32 asset, uint amount }";
|
|
74
|
-
string constant BalanceLimit = "{ bytes32 asset, uint min, uint max }";
|
|
75
|
-
string constant Custody = "{ uint host, bytes32 asset, uint amount }";
|
|
76
|
-
string constant CustodyLimit = "{ uint host, bytes32 asset, uint min, uint max }";
|
|
77
|
-
string constant Allocation = "{ uint host, bytes32 asset, uint amount }";
|
|
78
|
-
string constant Allowance = "{ uint host, bytes32 asset, uint amount }";
|
|
79
|
-
string constant Transaction = "{ bytes32 from, bytes32 to, bytes32 asset, uint amount }";
|
|
80
|
-
string constant Context = "{ bytes32 account, #bytes as state, #bytes as request }";
|
|
81
|
-
string constant Recover = "{ uint handler, uint resources, bytes32 key, #bytes as witness }";
|
|
82
|
-
string constant Call = "{ uint target, uint resources, #bytes as payload }";
|
|
83
|
-
string constant Step = "{ uint target, uint resources, #bytes as request }";
|
|
84
|
-
string constant Relay = "{ uint portal, uint resources, #bytes as request }";
|
|
85
|
-
string constant Dispatch = "{ uint portal, uint resources, #bytes as payload }";
|
|
86
|
-
string constant Bounty = "{ uint amount, bytes32 relayer }";
|
|
87
|
-
string constant Fee = "{ uint amount }";
|
|
88
|
-
string constant Auth = "{ uint cid, uint deadline, #bytes as proof }";
|
|
89
|
-
string constant Label = "{ uint id, bytes32 namespace, #string as name }";
|
|
90
|
-
string constant Schema = "{ bytes4 key, #string as body, bytes32 name }";
|
|
91
64
|
string constant Bytes = "";
|
|
92
65
|
string constant String = "";
|
|
93
66
|
string constant List = "";
|
|
94
67
|
string constant Evm = "";
|
|
95
|
-
}
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
69
|
+
// One-word payloads
|
|
70
|
+
|
|
71
|
+
string constant Node = "{ uint id }";
|
|
72
|
+
string constant Account = "{ bytes32 account }";
|
|
73
|
+
string constant Asset = "{ bytes32 asset }";
|
|
101
74
|
string constant Status = "{ uint code }";
|
|
102
|
-
|
|
75
|
+
|
|
76
|
+
// Two-word payloads
|
|
77
|
+
|
|
78
|
+
string constant Amount = "{ bytes32 asset, uint amount }";
|
|
79
|
+
string constant Balance = "{ bytes32 asset, uint amount }";
|
|
103
80
|
string constant AccountAsset = "{ bytes32 account, bytes32 asset }";
|
|
81
|
+
|
|
82
|
+
// Three-word payloads
|
|
83
|
+
|
|
84
|
+
string constant Allocation = "{ uint host, bytes32 asset, uint amount }";
|
|
85
|
+
string constant Allowance = "{ uint host, bytes32 asset, uint amount }";
|
|
86
|
+
string constant Custody = "{ uint host, bytes32 asset, uint amount }";
|
|
104
87
|
string constant AccountAmount = "{ bytes32 account, bytes32 asset, uint amount }";
|
|
105
88
|
string constant HostAmount = "{ uint host, bytes32 asset, uint amount }";
|
|
106
89
|
string constant HostAccountAsset = "{ uint host, bytes32 account, bytes32 asset }";
|
|
90
|
+
|
|
91
|
+
// Four-word payloads
|
|
92
|
+
|
|
93
|
+
string constant Transaction = "{ bytes32 from, bytes32 to, bytes32 asset, uint amount }";
|
|
107
94
|
string constant HostAccountAmount = "{ uint host, bytes32 account, bytes32 asset, uint amount }";
|
|
108
|
-
}
|
|
109
95
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
uint constant B64 = Header + 2 * Word;
|
|
121
|
-
/// @dev 8 header + 96 payload = 104 bytes total.
|
|
122
|
-
uint constant B96 = Header + 3 * Word;
|
|
123
|
-
/// @dev 8 header + 128 payload = 136 bytes total.
|
|
124
|
-
uint constant B128 = Header + 4 * Word;
|
|
125
|
-
/// @dev 8 header + 160 payload = 168 bytes total.
|
|
126
|
-
uint constant B160 = Header + 5 * Word;
|
|
127
|
-
/// @dev AUTH proof segment only: 20-byte signer + 65-byte signature = 85 bytes
|
|
128
|
-
uint constant Proof = 85;
|
|
129
|
-
/// @dev AUTH block: 8 header + 32 cid + 32 deadline + nested BYTES block with 85-byte proof = 165 bytes
|
|
130
|
-
uint constant Auth = B64 + Header + Proof;
|
|
131
|
-
/// @dev STATUS block: 8 header + 32 status code = 40 bytes
|
|
132
|
-
uint constant Status = B32;
|
|
133
|
-
/// @dev AMOUNT block: 8 header + 32 asset + 32 amount = 72 bytes
|
|
134
|
-
uint constant Amount = B64;
|
|
135
|
-
/// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
|
|
136
|
-
uint constant Balance = B64;
|
|
137
|
-
/// @dev BALANCE_LIMIT block: 8 header + 32 asset + 32 min + 32 max = 104 bytes
|
|
138
|
-
uint constant BalanceLimit = B96;
|
|
139
|
-
/// @dev FEE block: 8 header + 32 amount = 40 bytes
|
|
140
|
-
uint constant Fee = B32;
|
|
141
|
-
/// @dev BOUNTY block: 8 header + 32 amount + 32 relayer = 72 bytes
|
|
142
|
-
uint constant Bounty = B64;
|
|
143
|
-
/// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 amount = 104 bytes
|
|
144
|
-
uint constant HostAmount = B96;
|
|
145
|
-
/// @dev CUSTODY_LIMIT block: 8 header + 32 host + 32 asset + 32 min + 32 max = 136 bytes
|
|
146
|
-
uint constant CustodyLimit = B128;
|
|
147
|
-
/// @dev TRANSACTION block: 8 header + 32 from + 32 to + 32 asset + 32 amount = 136 bytes
|
|
148
|
-
uint constant Transaction = B128;
|
|
96
|
+
// Composite payloads
|
|
97
|
+
|
|
98
|
+
string constant Call = "{ uint target, uint resources, #bytes as payload }";
|
|
99
|
+
string constant Step = "{ uint cmd, uint resources, #bytes as input }";
|
|
100
|
+
string constant Relay = "{ uint portal, uint resources, #bytes as input }";
|
|
101
|
+
string constant Dispatch = "{ uint portal, uint resources, #bytes as payload }";
|
|
102
|
+
string constant Context = "{ bytes32 account, #bytes as state, #bytes as input }";
|
|
103
|
+
string constant Recover = "{ uint handler, uint resources, bytes32 key, #bytes as witness }";
|
|
104
|
+
string constant Label = "{ uint id, bytes32 namespace, #string as name }";
|
|
105
|
+
string constant Schema = "{ uint spec, #string as body, bytes32 name }";
|
|
149
106
|
}
|
|
107
|
+
|
package/codec/Specs.sol
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {Keys} from "./Keys.sol";
|
|
5
|
+
import {max24, replace8, replace32} from "../utils/Utils.sol";
|
|
6
|
+
|
|
7
|
+
/// @title Sizes
|
|
8
|
+
/// @notice Total byte sizes for fixed-width block types, including the 8-byte header (4-byte key + 4-byte payloadLen).
|
|
9
|
+
library Sizes {
|
|
10
|
+
/// @dev Shared block header size: 4-byte key + 4-byte payload length.
|
|
11
|
+
uint constant Header = 8;
|
|
12
|
+
/// @dev One fixed-width payload word.
|
|
13
|
+
uint constant Word = 32;
|
|
14
|
+
/// @dev 8 header + 32 payload = 40 bytes total.
|
|
15
|
+
uint constant B32 = Header + Word;
|
|
16
|
+
/// @dev 8 header + 64 payload = 72 bytes total.
|
|
17
|
+
uint constant B64 = Header + 2 * Word;
|
|
18
|
+
/// @dev 8 header + 96 payload = 104 bytes total.
|
|
19
|
+
uint constant B96 = Header + 3 * Word;
|
|
20
|
+
/// @dev 8 header + 128 payload = 136 bytes total.
|
|
21
|
+
uint constant B128 = Header + 4 * Word;
|
|
22
|
+
/// @dev 8 header + 160 payload = 168 bytes total.
|
|
23
|
+
uint constant B160 = Header + 5 * Word;
|
|
24
|
+
/// @dev STATUS block: 8 header + 32 status code = 40 bytes
|
|
25
|
+
uint constant Status = B32;
|
|
26
|
+
/// @dev AMOUNT block: 8 header + 32 asset + 32 amount = 72 bytes
|
|
27
|
+
uint constant Amount = B64;
|
|
28
|
+
/// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
|
|
29
|
+
uint constant Balance = B64;
|
|
30
|
+
/// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 amount = 104 bytes
|
|
31
|
+
uint constant HostAmount = B96;
|
|
32
|
+
/// @dev TRANSACTION block: 8 header + 32 from + 32 to + 32 asset + 32 amount = 136 bytes
|
|
33
|
+
uint constant Transaction = B128;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// @title Specs
|
|
37
|
+
/// @notice Word-aligned block specifications encoded as
|
|
38
|
+
/// `[key:4][min:4][max:4][hint:3][stride:1][container:4][reserved:12]`.
|
|
39
|
+
/// The upper eight bytes of a fixed-layout spec are its encoded block header,
|
|
40
|
+
/// allowing the entire spec word to be written directly as that header.
|
|
41
|
+
/// A maximum of zero means unbounded and requires a growable writer.
|
|
42
|
+
library Specs {
|
|
43
|
+
/// @dev A payload is incompatible with its block specification.
|
|
44
|
+
error InvalidSpec();
|
|
45
|
+
/// @dev A direct specification cannot contain a wrapper block.
|
|
46
|
+
error InvalidContainer();
|
|
47
|
+
|
|
48
|
+
uint private constant SizeFields = (uint(1) << 192) | (uint(1) << 160) | (uint(1) << 136);
|
|
49
|
+
|
|
50
|
+
// Reusable field shapes keep public specs readable while remaining valid
|
|
51
|
+
// compile-time constant expressions.
|
|
52
|
+
uint private constant Exact32 = 32 * SizeFields;
|
|
53
|
+
uint private constant Exact64 = 64 * SizeFields;
|
|
54
|
+
uint private constant Exact96 = 96 * SizeFields;
|
|
55
|
+
uint private constant Exact128 = 128 * SizeFields;
|
|
56
|
+
uint private constant UnboundedHint128 = uint(128) << 136;
|
|
57
|
+
uint private constant UnboundedMin72Hint256 = (uint(72) << 192) | (uint(256) << 136);
|
|
58
|
+
uint private constant UnboundedMin48Hint512 = (uint(48) << 192) | (uint(512) << 136);
|
|
59
|
+
uint private constant UnboundedMin104Hint256 = (uint(104) << 192) | (uint(256) << 136);
|
|
60
|
+
|
|
61
|
+
uint constant Empty = uint(bytes32(Keys.Empty));
|
|
62
|
+
uint constant Any = uint(bytes32(Keys.Any)) | UnboundedHint128;
|
|
63
|
+
uint constant Amount = uint(bytes32(Keys.Amount)) | Exact64;
|
|
64
|
+
uint constant Balance = uint(bytes32(Keys.Balance)) | Exact64;
|
|
65
|
+
uint constant Allocation = uint(bytes32(Keys.Allocation)) | Exact96;
|
|
66
|
+
uint constant Allowance = uint(bytes32(Keys.Allowance)) | Exact96;
|
|
67
|
+
uint constant Custody = uint(bytes32(Keys.Custody)) | Exact96;
|
|
68
|
+
uint constant List = uint(bytes32(Keys.List)) | UnboundedHint128;
|
|
69
|
+
uint constant Evm = uint(bytes32(Keys.Evm)) | UnboundedHint128;
|
|
70
|
+
uint constant Bytes = uint(bytes32(Keys.Bytes)) | UnboundedHint128;
|
|
71
|
+
uint constant String = uint(bytes32(Keys.String)) | UnboundedHint128;
|
|
72
|
+
uint constant Account = uint(bytes32(Keys.Account)) | Exact32;
|
|
73
|
+
uint constant Transaction = uint(bytes32(Keys.Transaction)) | Exact128;
|
|
74
|
+
uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin72Hint256;
|
|
75
|
+
uint constant Relay = uint(bytes32(Keys.Relay)) | UnboundedMin72Hint256;
|
|
76
|
+
uint constant Context = uint(bytes32(Keys.Context)) | UnboundedMin48Hint512;
|
|
77
|
+
uint constant Recover = uint(bytes32(Keys.Recover)) | UnboundedMin104Hint256;
|
|
78
|
+
uint constant Dispatch = uint(bytes32(Keys.Dispatch)) | UnboundedMin72Hint256;
|
|
79
|
+
uint constant Call = uint(bytes32(Keys.Call)) | UnboundedMin72Hint256;
|
|
80
|
+
uint constant Asset = uint(bytes32(Keys.Asset)) | Exact32;
|
|
81
|
+
uint constant Node = uint(bytes32(Keys.Node)) | Exact32;
|
|
82
|
+
uint constant Label = uint(bytes32(Keys.Label)) | UnboundedMin72Hint256;
|
|
83
|
+
uint constant Schema = uint(bytes32(Keys.Schema)) | UnboundedMin72Hint256;
|
|
84
|
+
|
|
85
|
+
uint constant Status = uint(bytes32(Keys.Status)) | Exact32;
|
|
86
|
+
uint constant AccountAsset = uint(bytes32(Keys.AccountAsset)) | Exact64;
|
|
87
|
+
uint constant AccountAmount = uint(bytes32(Keys.AccountAmount)) | Exact96;
|
|
88
|
+
uint constant HostAmount = uint(bytes32(Keys.HostAmount)) | Exact96;
|
|
89
|
+
uint constant HostAccountAsset = uint(bytes32(Keys.HostAccountAsset)) | Exact96;
|
|
90
|
+
uint constant HostAccountAmount = uint(bytes32(Keys.HostAccountAmount)) | Exact128;
|
|
91
|
+
|
|
92
|
+
/// @notice Construct a block specification from its encoded fields.
|
|
93
|
+
/// @param blockkey Encoded block key.
|
|
94
|
+
/// @param min Minimum accepted payload length.
|
|
95
|
+
/// @param max Maximum accepted payload length; zero means unbounded.
|
|
96
|
+
/// @param hint Initial per-block payload capacity.
|
|
97
|
+
/// @return spec Packed block specification.
|
|
98
|
+
function create(bytes4 blockkey, uint32 min, uint32 max, uint32 hint) internal pure returns (uint spec) {
|
|
99
|
+
spec |= uint(uint32(blockkey)) << 224;
|
|
100
|
+
spec |= uint(min) << 192;
|
|
101
|
+
spec |= uint(max) << 160;
|
|
102
|
+
spec |= uint(max24(hint)) << 136;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// @notice Decode the block key and accepted payload range from `spec`.
|
|
106
|
+
/// @param spec Packed block specification.
|
|
107
|
+
/// @return blockkey Encoded block key.
|
|
108
|
+
/// @return min Minimum accepted payload length.
|
|
109
|
+
/// @return max Maximum accepted payload length; zero means unbounded.
|
|
110
|
+
function decode(uint spec) internal pure returns (bytes4 blockkey, uint32 min, uint32 max) {
|
|
111
|
+
blockkey = bytes4(uint32(spec >> 224));
|
|
112
|
+
min = uint32(spec >> 192);
|
|
113
|
+
max = uint32(spec >> 160);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// @notice Return the block key encoded in `spec`.
|
|
117
|
+
/// @param spec Packed block specification.
|
|
118
|
+
/// @return Encoded block key.
|
|
119
|
+
function key(uint spec) internal pure returns (bytes4) {
|
|
120
|
+
return bytes4(uint32(spec >> 224));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// @notice Return the raw stride encoded in `spec`.
|
|
124
|
+
/// @dev Contextual defaults are applied by helpers such as `count`.
|
|
125
|
+
/// @param spec Packed block specification.
|
|
126
|
+
/// @return Encoded block stride.
|
|
127
|
+
function stride(uint spec) internal pure returns (uint8) {
|
|
128
|
+
return uint8(spec >> 128);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// @notice Return the canonical form of `spec` with implicit defaults resolved.
|
|
132
|
+
/// @param spec Packed block specification.
|
|
133
|
+
/// @param direct Whether the specification must not contain a wrapper block.
|
|
134
|
+
/// @dev A present spec with an encoded zero stride receives stride one;
|
|
135
|
+
/// the empty spec remains unchanged.
|
|
136
|
+
/// @return Canonical specification.
|
|
137
|
+
function normalize(uint spec, bool direct) internal pure returns (uint) {
|
|
138
|
+
if (direct && uint32(spec >> 96) != 0) revert InvalidContainer();
|
|
139
|
+
if (stride(spec) == 0 && key(spec) != bytes4(0)) spec |= uint(1) << 128;
|
|
140
|
+
return spec;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// @notice Return the effective outer block key and optional wrapped child key.
|
|
144
|
+
/// @dev A direct spec returns its own key as `outer` and a zero `child`.
|
|
145
|
+
/// @param spec Packed block specification.
|
|
146
|
+
/// @return outer Effective outer block key.
|
|
147
|
+
/// @return child Wrapped child key, or zero for a direct specification.
|
|
148
|
+
function keys(uint spec) internal pure returns (bytes4 outer, bytes4 child) {
|
|
149
|
+
child = key(spec);
|
|
150
|
+
outer = bytes4(uint32(spec >> 96));
|
|
151
|
+
|
|
152
|
+
if (outer == bytes4(0)) {
|
|
153
|
+
outer = child;
|
|
154
|
+
child = bytes4(0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// @notice Return whether a payload size lies within a specification's bounds.
|
|
159
|
+
/// @param spec Packed block specification.
|
|
160
|
+
/// @param size Payload length to test.
|
|
161
|
+
/// @return Whether the payload length is accepted.
|
|
162
|
+
function accepts(uint spec, uint size) internal pure returns (bool) {
|
|
163
|
+
uint32 min = uint32(spec >> 192);
|
|
164
|
+
uint32 max = uint32(spec >> 160);
|
|
165
|
+
return size >= min && (max == 0 || size <= max);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// @notice Validate a payload size against a specification's bounds.
|
|
169
|
+
/// @param spec Packed block specification.
|
|
170
|
+
/// @param size Payload length to validate.
|
|
171
|
+
function validate(uint spec, uint size) internal pure {
|
|
172
|
+
if (!accepts(spec, size)) revert InvalidSpec();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// @notice Return an exact payload size constrained to an accepted range.
|
|
176
|
+
/// @param spec Packed block specification.
|
|
177
|
+
/// @param min Smallest exact size accepted by the caller.
|
|
178
|
+
/// @param max Largest exact size accepted by the caller.
|
|
179
|
+
/// @return size Exact payload size encoded by the specification.
|
|
180
|
+
function exact(uint spec, uint min, uint max) internal pure returns (uint size) {
|
|
181
|
+
size = uint32(spec >> 192);
|
|
182
|
+
if (size != uint32(spec >> 160) || size < min || size > max) revert InvalidSpec();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// @notice Return the number of blocks represented by `groups`.
|
|
186
|
+
/// @param spec Packed block specification.
|
|
187
|
+
/// @param groups Number of groups.
|
|
188
|
+
/// @return Number of blocks across all groups.
|
|
189
|
+
function count(uint spec, uint groups) internal pure returns (uint) {
|
|
190
|
+
return groups * stride(normalize(spec, false));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// @notice Return the buffer configuration for `groups` of `spec`.
|
|
194
|
+
/// @dev Dynamic specs use their allocation hint and a maximum of zero means growable.
|
|
195
|
+
/// @param spec Packed block specification.
|
|
196
|
+
/// @param groups Number of groups to allocate.
|
|
197
|
+
/// @return capacity Initial encoded byte capacity.
|
|
198
|
+
/// @return growable Whether the buffer may grow beyond its initial capacity.
|
|
199
|
+
function allocation(uint spec, uint groups) internal pure returns (uint capacity, bool growable) {
|
|
200
|
+
capacity = count(spec, groups) * (Sizes.Header + uint24(spec >> 136));
|
|
201
|
+
growable = uint32(spec >> 160) == 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// @notice Return `spec` annotated as a generic LIST item.
|
|
205
|
+
/// @param spec Child block specification.
|
|
206
|
+
/// @return Specification wrapped in a LIST container.
|
|
207
|
+
function many(uint spec) internal pure returns (uint) {
|
|
208
|
+
return replace32(spec, 96, uint32(key(List)));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// @notice Return `spec` grouped with an explicit stride.
|
|
212
|
+
/// @param spec Packed block specification.
|
|
213
|
+
/// @param n Number of blocks per group.
|
|
214
|
+
/// @return Specification with its stride replaced by `n`.
|
|
215
|
+
function group(uint spec, uint8 n) internal pure returns (uint) {
|
|
216
|
+
return replace8(spec, 128, n);
|
|
217
|
+
}
|
|
218
|
+
}
|