@midnight-ntwrk/midnight-js-protocol 5.0.0-beta.7 → 5.0.0-beta.8-pre.765ca083
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/README.md +201 -2
- package/dist/engine.d.ts +367 -0
- package/dist/engine.js +519 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +467 -0
- package/dist/errors.js +595 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1254 -2
- package/dist/index.js +787 -2
- package/dist/index.js.map +1 -1
- package/dist/prove.d.ts +38 -0
- package/dist/prove.js +81 -0
- package/dist/prove.js.map +1 -0
- package/dist/shared/deploy-D6yyn9P7.js +428 -0
- package/dist/shared/deploy-D6yyn9P7.js.map +1 -0
- package/dist/shared/load-C0TZnEd2.js +39 -0
- package/dist/shared/load-C0TZnEd2.js.map +1 -0
- package/dist/v8.d.ts +1 -0
- package/dist/v8.js +3 -0
- package/dist/v8.js.map +1 -0
- package/dist/version.d.ts +97 -0
- package/dist/version.js +118 -0
- package/dist/version.js.map +1 -0
- package/package.json +30 -5
package/README.md
CHANGED
|
@@ -32,7 +32,11 @@ import { createPlatform } from '@midnight-ntwrk/midnight-js-protocol/platform-js
|
|
|
32
32
|
|
|
33
33
|
| Sub-path | Re-exports | Description |
|
|
34
34
|
| -------- | ---------- | ----------- |
|
|
35
|
+
| `./errors` | (own) | `PROTOCOL_ERROR_CODES` and every error class and error type this package raises — without pulling in the ledger/compact-js/onchain-runtime/platform namespaces. `protocol-acl.test.ts` pins the exact list |
|
|
36
|
+
| `./version` | (own) | `LEDGER_VERSIONS`, `protocolVersionToLedger`, `versionOfRecord`, `networkHeadVersion` and the types `LedgerVersion`, `ProtocolVersionSource`, `VersionedRecord` — same lightweight guarantee as `./errors` |
|
|
35
37
|
| `./ledger` | `@midnightntwrk/ledger-v9` | Ledger types and transaction primitives |
|
|
38
|
+
| `./v8` | `@midnightntwrk/ledger-v8` | Previous-era (v8) ledger — do not import at runtime; use `loadLedger8()` |
|
|
39
|
+
| `./engine` | (own) | Retained pre-fork execution engine — do not import at runtime; use `loadLedger8Engine()` |
|
|
36
40
|
| `./compact-runtime` | `@midnight-ntwrk/compact-runtime` | Compact contract runtime utilities |
|
|
37
41
|
| `./compact-js` | `@midnight-ntwrk/compact-js` | Compact JS bindings |
|
|
38
42
|
| `./compact-js/effect` | `@midnight-ntwrk/compact-js/effect` | Effect-based Compact bindings |
|
|
@@ -42,13 +46,208 @@ import { createPlatform } from '@midnight-ntwrk/midnight-js-protocol/platform-js
|
|
|
42
46
|
| `./platform-js/effect/Configuration` | `@midnight-ntwrk/platform-js/effect/Configuration` | Effect-based configuration |
|
|
43
47
|
| `./platform-js/effect/ContractAddress` | `@midnight-ntwrk/platform-js/effect/ContractAddress` | Effect-based contract address resolution |
|
|
44
48
|
|
|
49
|
+
## Source Layout
|
|
50
|
+
|
|
51
|
+
Nothing under `src/lib/` is a build entry, so this layout is internal and no consumer import path depends on it. Its job is to make one question answerable from a path alone: *which ledger does this touch?*
|
|
52
|
+
|
|
53
|
+
| Directory | Holds | Ledger reference |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `lib/v8/` | the retained pre-fork era: `load.ts`, `engine.ts`, `load-engine.ts`, `instance-guard.ts`, `down-convert.ts`, `execute.ts`, `compose.ts`, `deploy.ts`, `adapt.ts` | acquires `ledger-v8`, `onchain-runtime-v3` and the 0.16 glue, always through a dynamic import |
|
|
56
|
+
| `lib/v9/` | the current era's composition arms: `compose.ts`, `wrap.ts` | links `ledger-v9` statically |
|
|
57
|
+
| `lib/shared/` | what both arms run: `ledger-version.ts`, `verifier-keys.ts`, `compose-options.ts`, `assemble-call.ts`, `compose-types.ts`, `unshielded.ts`, `contract-state.ts` | era passed in as a `LedgerVersion` parameter, never chosen here |
|
|
58
|
+
| `lib/era/` | the facade and the dispatch: `load-era.ts`, `era.ts`, `envelope.ts` | reaches both, which is the point of the layer |
|
|
59
|
+
|
|
60
|
+
The directory names say what a module is **about**, not what it links. Three consequences a reader should not have to derive:
|
|
61
|
+
|
|
62
|
+
- **`lib/v8/compose.ts`, `deploy.ts` and `adapt.ts` link no v8 at all.** They take the acquired module as a `ProtocolV8` parameter, which is a type. That injection is what keeps the v8 WASM out of the eager graph, and the guarantee is enforced by `dist-laziness.test.ts` and `v8-surface.test.ts` — not by this layout. Read the tests, not the directory, for the bundle boundary.
|
|
63
|
+
- **`lib/shared/` is not free of vendors.** `assemble-call.ts` links `@midnight-ntwrk/compact-js`, a post-fork package, for `hashVerifierKey` and `encodeContractKeyLocation`; `contract-state.ts` links it for `hashVerifierKey`. Both are called by both arms, so their subject is shared even though their linkage is not. This is safe in one direction only: v9 is the eagerly-linked baseline, so a shared module reaching for it never wakes v8, while the reverse would.
|
|
64
|
+
- **`lib/v9/wrap.ts` type-imports `lib/v8/execute.ts`.** `TranscriptPojo` is the v8 engine's output and `wrapKeepStateCall` binds it onto v9. The cross-era edge is the operation's whole purpose, and it is type-only.
|
|
65
|
+
|
|
66
|
+
`compose-types.ts` and `era.ts` name their shared types through `@midnightntwrk/ledger-v9` because some vendor has to name them. `EncodedStateValue`, `Op`, `AlignedValue` and `Transcript` are pinned identical across `onchain-runtime-v3`, `ledger-v8` and `ledger-v9` by the compile-time assertions in `v8-down-convert.test.ts`; the import names one era, the type belongs to neither. Those assertions are evaluated by `yarn typecheck:tests` on the pre-push hook, not by CI — vitest transpiles test files without type-checking, so treat a failure there as the only signal you will get.
|
|
67
|
+
|
|
68
|
+
## Architecture Documents
|
|
69
|
+
|
|
70
|
+
The reasoning behind this package's shape lives in `docs/`, not in the source
|
|
71
|
+
docstrings. Each file is registered with TypeDoc through `projectDocuments`, so
|
|
72
|
+
it is a page in the generated API reference and `@see {@link Title}` in a
|
|
73
|
+
docstring resolves to it.
|
|
74
|
+
|
|
75
|
+
| Document | What it explains |
|
|
76
|
+
|---|---|
|
|
77
|
+
| [Era seam](./docs/era-seam.md) | Why only bytes and plain objects cross between the eras, how the eight operations are split between `LedgerEra` and `Ledger8Engine`, and the memoisation rules |
|
|
78
|
+
| [Retained-era execution](./docs/retained-era-execution.md) | The down-conversion stages, the Merkle rehash requirement, the era pin, and what a `TranscriptPojo` carries |
|
|
79
|
+
| [Dual-instantiation guard](./docs/dual-instantiation-guard.md) | What a duplicate WASM install does in the argument position versus the receiver position, and why the guard is a correctness requirement rather than a diagnostic |
|
|
80
|
+
| [Fail-closed decoding](./docs/fail-closed-decoding.md) | Why the envelope is the only authority over the bytes, and the division of labour between the three decode failures |
|
|
81
|
+
| [Compose refusal order](./docs/compose-refusal-order.md) | The order in which both era arms refuse compose options, and the one deliberate difference between them |
|
|
82
|
+
| [Verifier keys](./docs/verifier-keys.md) | Registration rules, the refusals that stop a deploy landing at an address the caller's artifacts do not describe, and why the address cannot be recomputed |
|
|
83
|
+
| [Module graph and lazy loading](./docs/module-graph-and-lazy-loading.md) | Build entries, the `./v8` and `./engine` chunks, the import cycle avoided by a leaf module, and why a vendor's types are named with `import type` |
|
|
84
|
+
| [Injected vendor slices](./docs/injected-vendor-slices.md) | How a seam names the vendor class it takes by injection — derived from the vendor's own class, declared structurally, or narrowed — and what each choice buys |
|
|
85
|
+
| [Shared table discipline](./docs/shared-table-discipline.md) | Why the shared tables are frozen and null-prototyped, and why exhaustiveness is enforced at compile time as well as at run time |
|
|
86
|
+
|
|
87
|
+
Docstrings in `src/` carry the API contract: what a symbol does, its
|
|
88
|
+
parameters, what it returns and what it throws. Anything that answers "why is
|
|
89
|
+
it built this way" belongs in a document above, stated once.
|
|
90
|
+
|
|
91
|
+
## Accessing the v8 Ledger Era
|
|
92
|
+
|
|
93
|
+
The `./v8` subpath re-exports the previous-era ledger (`@midnightntwrk/ledger-v8`), which carries its own WASM. To keep that WASM out of eagerly-loaded module graphs, runtime imports of `@midnight-ntwrk/midnight-js-protocol/v8` are blocked by ESLint everywhere outside this package. Use the lazy accessor instead:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { loadLedger8 } from '@midnight-ntwrk/midnight-js-protocol';
|
|
97
|
+
|
|
98
|
+
const v8 = await loadLedger8();
|
|
99
|
+
const transaction = v8.Transaction.deserialize(rawTransaction);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Type-only imports of the subpath are allowed:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import type { Transaction } from '@midnight-ntwrk/midnight-js-protocol/v8';
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If the v8 module cannot be loaded (usually a broken or partial install), `loadLedger8()` rejects with `Ledger8RuntimeMissingError` (code `MIDNIGHT_JS_P_LEDGER8_RUNTIME_MISSING`) carrying the original error as `cause`. Its `subpath` field is `'/v8'`, naming which chunk failed. The failed load is not memoised — the next call retries.
|
|
109
|
+
|
|
110
|
+
## Running Contracts on the Retained Pre-Fork Engine
|
|
111
|
+
|
|
112
|
+
Contracts compiled against the pre-fork toolchain keep executing on `compact-runtime@0.16` after the fork. That toolchain and its `onchain-runtime-v3` WASM live behind the `./engine` subpath, gated the same way as `./v8` and for the same reason. `loadLedger8Engine()` is the only sanctioned runtime path to it:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { loadLedger8Engine, loadLedgerEra, versionOfRecord } from '@midnight-ntwrk/midnight-js-protocol';
|
|
116
|
+
|
|
117
|
+
const engine = await loadLedger8Engine();
|
|
118
|
+
const era = await loadLedgerEra(versionOfRecord(indexerRecord));
|
|
119
|
+
|
|
120
|
+
const state = engine.downConvertForExecution(era.extractState(rawContractState));
|
|
121
|
+
const transcript = engine.executeCircuit({
|
|
122
|
+
contract,
|
|
123
|
+
circuitId: 'increment',
|
|
124
|
+
args: [],
|
|
125
|
+
state,
|
|
126
|
+
address: contractAddress,
|
|
127
|
+
coinPk: coinPublicKey,
|
|
128
|
+
privateState
|
|
129
|
+
});
|
|
130
|
+
const prototype = engine.wrapKeepStateCall({ transcript, contractAddress, contractState: migratedV9ContractState });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The engine exposes `downConvertForExecution`, `executeCircuit`, `executeConstructor` and `wrapKeepStateCall`, and they form a pipeline, each result being the next call's input. Reading a contract state and composing a call or a deploy are **not** here: both eras do those, so they live on the [ledger-era facade](#ledger-era-facade) instead.
|
|
134
|
+
|
|
135
|
+
`migratedV9ContractState` passed to `wrapKeepStateCall` must be the migrated v9 state **as read from chain**, which is not `rawContractState` above: it is where the deployed operation and its verifier key come from, and the key location the prototype carries is derived from that key. A blank or constructor-built state throws `ComposeFailedError` (code `MIDNIGHT_JS_P_COMPOSE_FAILED`) with `stage` naming which lookup failed and `version` naming the ledger era it was composing for.
|
|
136
|
+
|
|
137
|
+
Circuits with Zswap coin effects run on this leg like any other. The transcript carries `zswapLocalState` — the post-call Zswap local state, decoded into the runtime's public shape — which is what you turn into the transaction's segmented Zswap offer (`zswapStateToSegmentedOffer` in `@midnight-ntwrk/midnight-js-contracts`) and pass to `composeCallTx` as `guaranteedZswapOffer` / `fallibleZswapOffer`. Dropping it is what would leave you composing a transaction missing the coin movements the circuit recorded.
|
|
138
|
+
|
|
139
|
+
The transcript also carries `partitionContext` — the block, the starting effects and the commitment indices the pre-fork query context recorded while the circuit ran. Pass it on unchanged: a transcript composed without it is partitioned against a context the circuit never ran on, and a circuit that RECEIVED a coin in-contract cannot be partitioned at all, because the index its commitment was registered at lives only in that context. `wrapKeepStateCall` carries it for you; a hand-built call entry has to supply it. A context the target era cannot read throws `ComposeFailedError` with `stage: 'call-partition-context'`.
|
|
140
|
+
|
|
141
|
+
A failure to load the chunk itself rejects with `Ledger8RuntimeMissingError` whose `subpath` is `'/engine'`; read `cause` for which module actually failed to resolve.
|
|
142
|
+
|
|
143
|
+
## Ledger-Era Facade
|
|
144
|
+
|
|
145
|
+
Two ledger eras are live at once: `v8` backs the node 1.x line and `v9` the 2.x line. `loadLedgerEra` hands you one of them as a single object with the same methods on both, so code that has resolved which era a record belongs to does not then have to branch on it.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { loadLedgerEra, versionOfRecord } from '@midnight-ntwrk/midnight-js-protocol';
|
|
149
|
+
|
|
150
|
+
const era = await loadLedgerEra(versionOfRecord(indexerRecord));
|
|
151
|
+
|
|
152
|
+
const state = era.extractState(rawContractState);
|
|
153
|
+
const decoded = era.decodeContractState(rawContractState);
|
|
154
|
+
const callTx = era.composeCallTx({ calls, networkId, ttl });
|
|
155
|
+
const deploy = era.composeDeployTx({ contractState, verifierKeys, networkId, ttl });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Method | What it does |
|
|
159
|
+
| ------ | ------------ |
|
|
160
|
+
| `version` | The era this object is bound to — the value that was passed in |
|
|
161
|
+
| `extractState` | Reads the primary state out of a raw contract-state envelope |
|
|
162
|
+
| `decodeContractState` | Reads an envelope into its state plus the entry points it declares, each with its verifier key and that key's hash |
|
|
163
|
+
| `composeCallTx` | Composes an UNPROVEN call transaction and serializes it |
|
|
164
|
+
| `composeDeployTx` | Composes an UNPROVEN deploy and returns it with the address it will have and the initial state that address came from |
|
|
165
|
+
|
|
166
|
+
Derive the era with `versionOfRecord` or `networkHeadVersion` (see [Version Module](#version-module)) rather than writing the string by hand. An era string that is not `'v8'` or `'v9'` rejects with `UnknownLedgerVersionError`; the offending value is on the error's `requestedVersion` field, not in its message.
|
|
167
|
+
|
|
168
|
+
Each era is memoised, so the retained pre-fork WASM is instantiated at most once per process. Asking for `'v8'` is what acquires it — a consumer that only ever asks for `'v9'` never loads it at all, which is gated by `dist-laziness.test.ts`. A **failed** v8 acquisition is not memoised: the rejection propagates unchanged as `Ledger8RuntimeMissingError` and the next call retries.
|
|
169
|
+
|
|
170
|
+
### Only bytes and plain objects cross this boundary
|
|
171
|
+
|
|
172
|
+
Every value going in or coming out is plain data — `Uint8Array`s and plain objects — never a live WASM handle. A contract state goes in as the bytes it was serialized to and comes back as an `EncodedStateValue` plus plain entry-point records; a composed transaction comes back as bytes. So a result can be stored, compared across eras, or sent through a `structuredClone` or a worker boundary without the module that produced it. `era-parity.test.ts` asserts exactly that, on every method, for both eras.
|
|
173
|
+
|
|
174
|
+
Because the methods are synchronous, all the awaiting happens once, at `loadLedgerEra`.
|
|
175
|
+
|
|
176
|
+
### Where the two eras differ
|
|
177
|
+
|
|
178
|
+
The same method names mostly mean the same capabilities. One thing the v8 arm refuses that the v9 arm accepts:
|
|
179
|
+
|
|
180
|
+
- **A call tree.** The v8 arm composes exactly one call. A cross-contract call is a ledger-9-only feature a pre-fork contract cannot emit, so that era has no call tree to express: a `calls` list longer than one throws `ComposeOptionError` with `option: 'calls'` rather than composing the first entry and dropping the rest.
|
|
181
|
+
|
|
182
|
+
**A Zswap offer is not one of them.** Both eras read `guaranteedZswapOffer` / `fallibleZswapOffer` and carry the resulting offer into the transaction; both throw `ComposeOptionError` with `option: 'zswapOffer'` for bytes their own decoder rejects, with the decoder's failure on `cause`. A coin-moving call composes on either era.
|
|
183
|
+
|
|
184
|
+
The v8 arm also *requires* `verifierKeys` on `composeDeployTx`, where the v9 arm accepts its omission in one case. The retained deploy leg registers the compiled contract's keys onto the initial state itself, so it always needs the map; omitting it throws `ComposeOptionError` with `option: 'verifierKeys'`. The v9 arm allows the omission only for a state that ALREADY carries its keys, and checks rather than assumes it: a state still declaring a blank-keyed entry point throws the same `ComposeOptionError` with the same `option`. So the two arms agree on every input except one — a pre-keyed state, which deploys as-is on v9 and needs its keys supplied again on v8.
|
|
185
|
+
|
|
186
|
+
What is *not* asymmetric: a call's user-addressed unshielded payouts are aggregated onto the transaction's guaranteed and fallible offers on **both** eras. Attaching them on one era only would leave the other composing an unbalanced transaction the node rejects on submission, with nothing having reported a problem at composition time — so `era-parity.test.ts` asserts the payout each segment carries, per era.
|
|
187
|
+
|
|
188
|
+
### Errors
|
|
189
|
+
|
|
190
|
+
| Error | Code | Raised when |
|
|
191
|
+
| ----- | ---- | ----------- |
|
|
192
|
+
| `StateDecodeFailedError` | `MIDNIGHT_JS_P_STATE_DECODE_FAILED` | A contract-state envelope could not be read by the era it was requested for — most often a state written by the other era |
|
|
193
|
+
| `ComposeFailedError` | `MIDNIGHT_JS_P_COMPOSE_FAILED` | Something about a CALL or a DEPLOY could not be composed: an operation that is missing, unkeyed, or names a circuit the contract does not declare; an empty call list; a pre-call state or a recorded query context the era cannot bridge; a supplied transcript with neither half; a public transcript or a set of call inputs the ledger itself rejected; or a claimed payout the transaction cannot settle (dust, or a shielded token type). `stage` is a closed union naming which of those it was — see its own docs for the full list; `version` names the era |
|
|
194
|
+
| `ComposeOptionError` | `MIDNIGHT_JS_P_COMPOSE_OPTION_INVALID` | A transaction-wide OPTION cannot be used at all — an empty network id, an invalid ttl, a contract state whose envelope the era rejected, an offer the era's decoder rejected, a missing verifier-key map, or a call list longer than the era can compose. `option` names the field, `version` names the era |
|
|
195
|
+
| `UnknownLedgerVersionError` | `MIDNIGHT_JS_P_UNKNOWN_LEDGER_VERSION` | The requested era is not `'v8'` or `'v9'` |
|
|
196
|
+
|
|
197
|
+
Each names the era it was raised for — `version` on the first three, `requestedVersion` on `UnknownLedgerVersionError`, which also takes no `cause`. None renders hex or a byte dump of its own, and the first three preserve the underlying runtime failure on `cause` where there was one.
|
|
198
|
+
|
|
199
|
+
### Planned follow-ups
|
|
200
|
+
|
|
201
|
+
Recorded here so the reasoning is not lost, and deliberately NOT done in the change that introduced this facade:
|
|
202
|
+
|
|
203
|
+
- **Collapse the version dispatch in `lib/era/envelope.ts`.** `extractEncodedStateValue` has one production caller, which passes the literal `'v8'`; the v9 arm calls `extractV9EncodedStateValue` directly. The decoder table, the unknown-version guard and the null-prototype defence are therefore only reachable from tests, and the per-file 100% floor keeps the tests that reach them alive. Collapsing it to a `extractV8EncodedStateValue` beside the v9 one deletes real tests, which belongs in its own change.
|
|
204
|
+
- **Give `StateDecodeFailedError` a `stage`.** `decodeContractStateWith` wraps the whole read, so a state that decoded fine but declares an entry point resolving to no operation is reported with the same code and the same "resolve the era and check the bytes" remediation as an envelope written by the other era. A discriminator would separate them; it is a public error-shape change.
|
|
205
|
+
- **Give `ComposeOptionError` a `circuitId`.** The v9 blank-key refusal knows which entry point was blank and cannot say so, because the field does not exist. Adding it would let that refusal name the slot without breaking the class parity the two arms currently have.
|
|
206
|
+
|
|
207
|
+
## Version Module
|
|
208
|
+
|
|
209
|
+
Mapping a raw `protocolVersion` integer onto the ledger runtime it corresponds to. Reachable from the root barrel and, without loading the ledger/compact-js/onchain-runtime/platform namespaces, from the `./version` subpath. The error it throws is reachable from `./errors` on the same terms.
|
|
210
|
+
|
|
211
|
+
The `protocolVersion` integer encodes the **node** version as `major * 1_000_000 + minor * 1_000 + patch`, so a whole node major occupies a 1_000_000-wide range:
|
|
212
|
+
|
|
213
|
+
| protocolVersion range | node version | ledger |
|
|
214
|
+
| --------------------- | ------------ | ------ |
|
|
215
|
+
| 1_000_000 – 1_999_999 | 1.x | v8 |
|
|
216
|
+
| 2_000_000 – 2_999_999 | 2.x | v9 |
|
|
217
|
+
|
|
218
|
+
Anything outside those ranges throws rather than guessing. Node 0.x is deliberately absent: the indexer's own table does map it, but midnight-js meets only node 1.x or 2.x, so a 0.x `protocolVersion` is reported as unknown rather than silently resolved.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import {
|
|
222
|
+
LEDGER_VERSIONS, // readonly ['v8', 'v9']
|
|
223
|
+
protocolVersionToLedger, // (protocolVersion: number, path?: 'read' | 'construct') => 'v8' | 'v9' (path defaults to 'construct')
|
|
224
|
+
versionOfRecord, // (record: { protocolVersion: number }) => 'v8' | 'v9'
|
|
225
|
+
networkHeadVersion // (source: { queryLatestProtocolVersion(): Promise<number> }) => Promise<'v8' | 'v9'>
|
|
226
|
+
} from '@midnight-ntwrk/midnight-js-protocol/version';
|
|
227
|
+
import { UnknownProtocolVersionError } from '@midnight-ntwrk/midnight-js-protocol/errors';
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Prefer `versionOfRecord` for a `protocolVersion` already read off an indexer/node record, and `networkHeadVersion` for the network's current head version — both tag any error with the correct path automatically. `UnknownProtocolVersionError` carries a `reason` of `'malformed'` (the input was not a non-negative integer) or `'unknown'` (a well-formed integer outside every mapped range), so callers can distinguish "bad input" from "genuinely unsupported protocol version".
|
|
231
|
+
|
|
45
232
|
## ESLint Enforcement
|
|
46
233
|
|
|
47
|
-
|
|
234
|
+
Three rules in the repo's `eslint.config.mjs` govern how other packages reach protocol internals.
|
|
235
|
+
|
|
236
|
+
**1. The protocol ACL** — a `no-restricted-imports` rule prevents direct imports of the underlying protocol packages outside of this package. If you see an error like:
|
|
48
237
|
|
|
49
238
|
> Import from `@midnight-ntwrk/midnight-js-protocol/ledger` instead.
|
|
50
239
|
|
|
51
|
-
Replace the direct protocol import with the corresponding subpath from this package.
|
|
240
|
+
Replace the direct protocol import with the corresponding subpath from this package. For the v8 era specifically, the error
|
|
241
|
+
|
|
242
|
+
> Runtime v8 access only via loadLedger8() from @midnight-ntwrk/midnight-js-protocol.
|
|
243
|
+
|
|
244
|
+
means: replace the direct `./v8` runtime import with `loadLedger8()` (type-only imports stay as they are).
|
|
245
|
+
|
|
246
|
+
**2. The v8 static-import gate** — `@typescript-eslint/no-restricted-imports` blocks runtime imports of `@midnight-ntwrk/midnight-js-protocol/v8` outside `packages/protocol/src/`. Type-only imports (`import type`) are allowed.
|
|
247
|
+
|
|
248
|
+
**3. The v8 dynamic-import gate** — `no-restricted-syntax` selectors block `import('@midnight-ntwrk/midnight-js-protocol/v8')` in the same scopes. An interpolated template literal cannot be matched statically and is not covered.
|
|
249
|
+
|
|
250
|
+
Both v8 gates point at `loadLedger8()`, the accessor on the root barrel — the only sanctioned runtime path to the v8 era.
|
|
52
251
|
|
|
53
252
|
## Resources
|
|
54
253
|
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import * as ledgerV9 from '@midnightntwrk/ledger-v9';
|
|
2
|
+
import { CallContext, Effects, CoinCommitment, EncodedStateValue, ContractCallPrototype } from '@midnightntwrk/ledger-v9';
|
|
3
|
+
export { EncodedStateValue } from '@midnightntwrk/ledger-v9';
|
|
4
|
+
import * as OnchainRuntimeV3 from '@midnight-ntwrk/onchain-runtime-v3';
|
|
5
|
+
import { EncodedZswapLocalState, ProofData, ZswapLocalState } from 'compact-runtime-ledger8';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One entry point a contract state declares, with the verifier key registered
|
|
9
|
+
* against it if there is one.
|
|
10
|
+
*
|
|
11
|
+
* `verifierKey` and `verifierKeyHash` are both absent for a blank slot — the
|
|
12
|
+
* shape a constructor-built state has before a deploy fills it in.
|
|
13
|
+
*
|
|
14
|
+
* @see {@link FailClosedDecoding} for why they are absent rather than
|
|
15
|
+
* zero-length or a hash of nothing.
|
|
16
|
+
*/
|
|
17
|
+
interface ContractEntryPointPojo {
|
|
18
|
+
readonly circuitId: string;
|
|
19
|
+
readonly verifierKey: Uint8Array | undefined;
|
|
20
|
+
readonly verifierKeyHash: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The query-context state a call recorded while it ran, which its pre-call
|
|
25
|
+
* state bytes do not carry.
|
|
26
|
+
*
|
|
27
|
+
* `block` and `effects` are the PRE-call values; `comIndices` is the POST-call
|
|
28
|
+
* map. Plain data on every member.
|
|
29
|
+
*
|
|
30
|
+
* @see {@link ComposeRefusalOrder} for why partitioning needs the context the
|
|
31
|
+
* circuit actually ran on, and why `CallContext` and `Effects` are declared
|
|
32
|
+
* once against ledger-v9.
|
|
33
|
+
* @see {@link RetainedEraExecution} for why each member is read off the
|
|
34
|
+
* context it is.
|
|
35
|
+
* @see {@link EraSeam}
|
|
36
|
+
*/
|
|
37
|
+
interface PartitionContext {
|
|
38
|
+
readonly block: CallContext;
|
|
39
|
+
readonly effects: Effects;
|
|
40
|
+
/** Commitment -> the index the runtime recorded it at. Empty for a call that received no coin. */
|
|
41
|
+
readonly comIndices: ReadonlyMap<CoinCommitment, bigint>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The sentinel that asks for the ledger's own INITIAL cost model instead of the chain's.
|
|
45
|
+
*
|
|
46
|
+
* Spelled out as a value a caller has to name, because the thing it selects is wrong for any chain
|
|
47
|
+
* that has been running: the initial parameters are the model the chain started with, and prices
|
|
48
|
+
* adjust per block. Partitioning against them draws the guaranteed/fallible boundary in the wrong
|
|
49
|
+
* place and the node then refuses the guaranteed segment with `Transcript(Execution(OutOfGas))` --
|
|
50
|
+
* after the caller has already paid to prove it.
|
|
51
|
+
*/
|
|
52
|
+
declare const INITIAL_LEDGER_PARAMETERS = "initial";
|
|
53
|
+
/**
|
|
54
|
+
* What a composer accepts for the block's ledger parameters: the chain's own serialized parameters,
|
|
55
|
+
* or {@link INITIAL_LEDGER_PARAMETERS}.
|
|
56
|
+
*
|
|
57
|
+
* There is deliberately no third option. This used to be optional, and omitting it fell back to the
|
|
58
|
+
* initial parameters silently -- so a caller that simply forgot got the wrong cost model with no
|
|
59
|
+
* signal, and a `PublicDataProvider` that does not serve `ledgerParameters` (the field is optional)
|
|
60
|
+
* degraded every call built through it. Requiring the option keeps the compatibility path reachable
|
|
61
|
+
* only as a decision, never as an oversight.
|
|
62
|
+
*
|
|
63
|
+
* A caller that reads the chain must pass the bytes from the SAME read as the contract state: the
|
|
64
|
+
* parameters are era-tagged and dated per block, so a second read could answer for another block.
|
|
65
|
+
*
|
|
66
|
+
* @see {@link EraSeam}
|
|
67
|
+
*/
|
|
68
|
+
type LedgerParametersOption = Uint8Array | typeof INITIAL_LEDGER_PARAMETERS;
|
|
69
|
+
|
|
70
|
+
type ChargedState = OnchainRuntimeV3.ChargedState;
|
|
71
|
+
type StateValue = OnchainRuntimeV3.StateValue;
|
|
72
|
+
/**
|
|
73
|
+
* The retained runtime's state handle types, under names a consumer can write.
|
|
74
|
+
*
|
|
75
|
+
* `DownConvertedState.data` is an `onchain-runtime-v3` `ChargedState` and
|
|
76
|
+
* `.data.state` a `StateValue`, and neither was nameable outside this package:
|
|
77
|
+
* the aliases above are local, and the vendor package publishes no subpath to
|
|
78
|
+
* import them from. Published because results now carry these handles.
|
|
79
|
+
*
|
|
80
|
+
* TYPE-ONLY, deliberately. `import type` is erased at compile time, so nothing
|
|
81
|
+
* here puts a second copy of the retained runtime on any module graph — the
|
|
82
|
+
* loaders remain the only runtime path to it.
|
|
83
|
+
*/
|
|
84
|
+
type Ledger8ChargedState = ChargedState;
|
|
85
|
+
/** See {@link Ledger8ChargedState}. */
|
|
86
|
+
type Ledger8StateValue = StateValue;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The result of a down-convert: only the primary state data a pre-fork
|
|
90
|
+
* circuit reads during execution.
|
|
91
|
+
*
|
|
92
|
+
* Deliberately not a full pre-fork `ContractState`: it carries no
|
|
93
|
+
* `.operations`, `.maintenanceAuthority` or `.balance`, which remain the
|
|
94
|
+
* caller's to carry.
|
|
95
|
+
*
|
|
96
|
+
* @see {@link RetainedEraExecution}
|
|
97
|
+
*/
|
|
98
|
+
interface DownConvertedState {
|
|
99
|
+
readonly data: ChargedState;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type AlignedValue = OnchainRuntimeV3.AlignedValue;
|
|
103
|
+
type Op<T> = OnchainRuntimeV3.Op<T>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The `QueryContext` slice {@link executeCircuit} reads off a circuit's
|
|
107
|
+
* context: the primary state, ready to be wrapped back into a
|
|
108
|
+
* {@link DownConvertedState}, plus the three members a composition leg needs to
|
|
109
|
+
* partition the call's public transcript against the context it really ran on
|
|
110
|
+
* (see {@link PartitionContext}).
|
|
111
|
+
*
|
|
112
|
+
* A `Pick` of the vendor's own class, not a restatement of it: the member names
|
|
113
|
+
* and their types come from onchain-runtime-v3, so a rename there fails this
|
|
114
|
+
* build instead of leaving a mirror that describes a property the runtime no
|
|
115
|
+
* longer has. It stays a narrowing rather than the whole class because
|
|
116
|
+
* `QueryContext` is a WASM class with dozens of members, and this seam reads
|
|
117
|
+
* four — the narrowing is what lets the execution tests hand `executeCircuit` a
|
|
118
|
+
* plain object double instead of standing up real WASM.
|
|
119
|
+
*
|
|
120
|
+
* @see {@link RetainedEraExecution}
|
|
121
|
+
*/
|
|
122
|
+
type Ledger8QueryContext = Pick<OnchainRuntimeV3.QueryContext, 'state' | 'block' | 'effects' | 'comIndices'>;
|
|
123
|
+
/**
|
|
124
|
+
* The circuit-context slice {@link executeCircuit} needs from a pre-fork
|
|
125
|
+
* (`compact-runtime@0.16`) `createCircuitContext` call and a circuit's
|
|
126
|
+
* updated context after it runs.
|
|
127
|
+
*
|
|
128
|
+
* Not the glue's `CircuitContext` itself: that one carries a real
|
|
129
|
+
* `QueryContext`, a WASM class no test double can satisfy, and this seam reads
|
|
130
|
+
* a handful of properties off it. The narrowing is the difference between
|
|
131
|
+
* execution tests that check plumbing with a literal and tests that must stand
|
|
132
|
+
* up WASM to do it — see {@link Ledger8QueryContext}, which derives those
|
|
133
|
+
* properties from the vendor's class so the narrowing cannot drift from it.
|
|
134
|
+
*
|
|
135
|
+
* @see {@link RetainedEraExecution}
|
|
136
|
+
*/
|
|
137
|
+
interface Ledger8CircuitContext {
|
|
138
|
+
readonly currentQueryContext: Ledger8QueryContext;
|
|
139
|
+
readonly currentPrivateState: unknown;
|
|
140
|
+
readonly currentZswapLocalState: EncodedZswapLocalState;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The proof-data slice of a pre-fork {@link Ledger8CircuitResult}.
|
|
144
|
+
*
|
|
145
|
+
* The vendor's own `ProofData`, not a copy of its four members: it is already
|
|
146
|
+
* plain data with no WASM handle in it, so there was nothing for a mirror to
|
|
147
|
+
* narrow — only a shape to drift out of sync. `Readonly` is this package's own
|
|
148
|
+
* addition, and the only one.
|
|
149
|
+
*
|
|
150
|
+
* @see {@link EraSeam}
|
|
151
|
+
*/
|
|
152
|
+
type Ledger8ProofData = Readonly<ProofData>;
|
|
153
|
+
/**
|
|
154
|
+
* What a pre-fork `impureCircuits[id](ctx, ...args)` call returns.
|
|
155
|
+
*
|
|
156
|
+
* Tracks the glue's `CircuitResults` but is not derived from it: `context` is
|
|
157
|
+
* the narrowed {@link Ledger8CircuitContext} for the reason given there, and
|
|
158
|
+
* `proofData` IS the vendor's own type — see {@link Ledger8ProofData}.
|
|
159
|
+
*
|
|
160
|
+
* @see {@link RetainedEraExecution}
|
|
161
|
+
*/
|
|
162
|
+
interface Ledger8CircuitResult {
|
|
163
|
+
readonly result: unknown;
|
|
164
|
+
readonly proofData: Ledger8ProofData;
|
|
165
|
+
readonly context: Ledger8CircuitContext;
|
|
166
|
+
}
|
|
167
|
+
/** One callable entry point on a compiled pre-fork contract's `impureCircuits` map. */
|
|
168
|
+
type Ledger8ImpureCircuit = (ctx: Ledger8CircuitContext, ...args: readonly unknown[]) => Ledger8CircuitResult;
|
|
169
|
+
/**
|
|
170
|
+
* The subset of a compiled pre-fork (`compact-runtime@0.16`) contract module
|
|
171
|
+
* {@link executeCircuit} needs: only `impureCircuits`, the map every
|
|
172
|
+
* generated contract exposes its callable entry points under.
|
|
173
|
+
*
|
|
174
|
+
* @see {@link RetainedEraExecution}
|
|
175
|
+
*/
|
|
176
|
+
interface Ledger8ContractLike {
|
|
177
|
+
readonly impureCircuits: Readonly<Record<string, Ledger8ImpureCircuit>>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The result of one impure circuit's invocation on a pre-fork
|
|
181
|
+
* (`compact-runtime@0.16`) contract instance: the primary result plus every
|
|
182
|
+
* artifact {@link wrapKeepStateCall} (`../v9/wrap.ts`) needs to assemble a
|
|
183
|
+
* v9-native `ContractCallPrototype`.
|
|
184
|
+
*
|
|
185
|
+
* `preContractState`/`postContractState` are {@link DownConvertedState}s, not
|
|
186
|
+
* full pre-fork `ContractState`s: they carry only `.data`. They are also the
|
|
187
|
+
* only members here that are LIVE HANDLES -- every other member of this type is
|
|
188
|
+
* plain data. `postContractStateEncoded` is the post-state's encoded form,
|
|
189
|
+
* carried beside the handle so a caller has a value that outlives the runtime
|
|
190
|
+
* instance; the pre-state's encoded form is the caller's own input, which
|
|
191
|
+
* `downConvertForExecution` already proves round-trips to it.
|
|
192
|
+
*
|
|
193
|
+
* `partitionContext` is the query-context state the call ran with, which the
|
|
194
|
+
* carried state bytes do not hold — see {@link PartitionContext}. A composition
|
|
195
|
+
* leg needs it to partition the call's transcript.
|
|
196
|
+
*
|
|
197
|
+
* `zswapLocalState` is the post-call Zswap local state, DECODED into the
|
|
198
|
+
* runtime's public shape: the coins the circuit spent and produced. A caller
|
|
199
|
+
* turns it into the transaction's segmented Zswap offer
|
|
200
|
+
* (`zswapStateToSegmentedOffer`, `packages/contracts/src/internal/utils/zswap-utils.ts`)
|
|
201
|
+
* and hands that offer to whichever composition leg it targets.
|
|
202
|
+
*
|
|
203
|
+
* @see {@link RetainedEraExecution}
|
|
204
|
+
*/
|
|
205
|
+
interface TranscriptPojo {
|
|
206
|
+
readonly circuitId: string;
|
|
207
|
+
readonly result: unknown;
|
|
208
|
+
readonly input: AlignedValue;
|
|
209
|
+
readonly output: AlignedValue;
|
|
210
|
+
readonly publicTranscript: Op<AlignedValue>[];
|
|
211
|
+
readonly privateTranscriptOutputs: AlignedValue[];
|
|
212
|
+
readonly preContractState: DownConvertedState;
|
|
213
|
+
readonly postContractState: DownConvertedState;
|
|
214
|
+
/**
|
|
215
|
+
* The post-call state as an {@link EncodedStateValue}: the same value
|
|
216
|
+
* {@link postContractState} holds, in the form that survives this process.
|
|
217
|
+
*
|
|
218
|
+
* `EncodedStateValue` is pinned identical across `onchain-runtime-v3`,
|
|
219
|
+
* `ledger-v8` and `ledger-v9` -- see this package's README and the
|
|
220
|
+
* cross-runtime assertions in `src/test/v8-down-convert.test.ts` -- so this is
|
|
221
|
+
* the member era-agnostic code reads, and the one that can be persisted,
|
|
222
|
+
* cloned or sent to a worker.
|
|
223
|
+
*/
|
|
224
|
+
readonly postContractStateEncoded: EncodedStateValue;
|
|
225
|
+
readonly privateStateAfter: unknown;
|
|
226
|
+
readonly partitionContext: PartitionContext;
|
|
227
|
+
readonly zswapLocalState: ZswapLocalState;
|
|
228
|
+
}
|
|
229
|
+
/** Everything {@link executeCircuit} needs to run one impure circuit call. */
|
|
230
|
+
interface ExecuteCircuitOptions {
|
|
231
|
+
readonly contract: Ledger8ContractLike;
|
|
232
|
+
readonly circuitId: string;
|
|
233
|
+
readonly args: readonly unknown[];
|
|
234
|
+
readonly state: DownConvertedState;
|
|
235
|
+
readonly address: string;
|
|
236
|
+
readonly coinPk: string;
|
|
237
|
+
readonly privateState: unknown;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Everything {@link wrapKeepStateCall} needs to wrap one keep-state call.
|
|
242
|
+
* `contractState` is the migrated, post-fork v9 `ContractState` — read from
|
|
243
|
+
* chain, or otherwise carrying the contract's real registered operations —
|
|
244
|
+
* used only to look up the `ContractOperation` for `transcript.circuitId`
|
|
245
|
+
* (mirrors `ComposeV8CallOptions`'s `contractState` parameter in
|
|
246
|
+
* `../v8/compose.ts`).
|
|
247
|
+
*/
|
|
248
|
+
interface WrapKeepStateCallOptions {
|
|
249
|
+
readonly transcript: TranscriptPojo;
|
|
250
|
+
readonly contractAddress: string;
|
|
251
|
+
readonly contractState: ledgerV9.ContractState;
|
|
252
|
+
/**
|
|
253
|
+
* The ledger parameters of the block this call is built against, or
|
|
254
|
+
* {@link INITIAL_LEDGER_PARAMETERS} to accept the initial cost model.
|
|
255
|
+
*
|
|
256
|
+
* Required rather than defaulted, and not hard-coded to the initial parameters here: the
|
|
257
|
+
* transcript crosses as `'unpartitioned'`, so the partitioner DOES run and the cost model it uses
|
|
258
|
+
* is whatever this supplies. Defaulting it would make this function the silent
|
|
259
|
+
* wrong-cost-model path the option exists to close. See {@link LedgerParametersOption}.
|
|
260
|
+
*/
|
|
261
|
+
readonly ledgerParameters: LedgerParametersOption;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The minimal shape a pre-fork (`compact-runtime@0.16`) `ContractState` is used
|
|
266
|
+
* through here: just `.serialize()`. It is what {@link executeConstructor}
|
|
267
|
+
* returns on its result, and `.serialize()` is how a caller turns that handle
|
|
268
|
+
* into the bytes every deploy leg takes.
|
|
269
|
+
*
|
|
270
|
+
* Crosses the era boundary by bytes, not by handle.
|
|
271
|
+
*
|
|
272
|
+
* @see {@link DualInstantiationGuard} for why that crossing is the one a
|
|
273
|
+
* duplicate install cannot affect
|
|
274
|
+
* @see {@link EraSeam}
|
|
275
|
+
*/
|
|
276
|
+
type Ledger8DeployableContractState = Pick<OnchainRuntimeV3.ContractState, 'serialize'>;
|
|
277
|
+
/** What a pre-fork `contract.initialState(constructorContext, ...args)` call returns. */
|
|
278
|
+
interface Ledger8ConstructorResult {
|
|
279
|
+
readonly currentContractState: Ledger8DeployableContractState;
|
|
280
|
+
readonly currentPrivateState: unknown;
|
|
281
|
+
/**
|
|
282
|
+
* The Zswap local state the constructor ended on, still ENCODED. A
|
|
283
|
+
* constructor that mints a coin records it here, and a deploy that drops it
|
|
284
|
+
* composes a transaction the ledger cannot balance.
|
|
285
|
+
*/
|
|
286
|
+
readonly currentZswapLocalState: EncodedZswapLocalState;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The subset of a compiled pre-fork (`compact-runtime@0.16`) contract module
|
|
290
|
+
* {@link executeConstructor} needs: only `initialState`, the constructor
|
|
291
|
+
* every generated contract exposes to build its initial ledger state.
|
|
292
|
+
*/
|
|
293
|
+
interface Ledger8ConstructorContractLike {
|
|
294
|
+
readonly initialState: (constructorContext: unknown, ...args: readonly unknown[]) => Ledger8ConstructorResult;
|
|
295
|
+
}
|
|
296
|
+
/** Everything {@link executeConstructor} needs to run one contract constructor. */
|
|
297
|
+
interface ExecuteConstructorOptions {
|
|
298
|
+
readonly contract: Ledger8ConstructorContractLike;
|
|
299
|
+
readonly args: readonly unknown[];
|
|
300
|
+
readonly privateState: unknown;
|
|
301
|
+
readonly coinPk: string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* The result of running a pre-fork constructor: the freshly built contract
|
|
305
|
+
* state (still carrying blank verifier keys on every operation slot — see
|
|
306
|
+
* {@link composeV8DeployTx}) and the resulting private state.
|
|
307
|
+
*/
|
|
308
|
+
interface ConstructorResultPojo {
|
|
309
|
+
readonly contractState: Ledger8DeployableContractState;
|
|
310
|
+
readonly privateState: unknown;
|
|
311
|
+
/**
|
|
312
|
+
* The constructor's own Zswap local state, DECODED — plain data in both
|
|
313
|
+
* runtimes. Empty for the ordinary constructor that mints nothing; carries
|
|
314
|
+
* the outputs for one that does, which is what lets the deploy be balanced.
|
|
315
|
+
*/
|
|
316
|
+
readonly zswapLocalState: ZswapLocalState;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The public surface {@link createLedger8Engine} builds: the retained pre-fork
|
|
321
|
+
* EXECUTION capabilities, with the 0.16 runtime instance already captured in
|
|
322
|
+
* closure — no method here takes a runtime or module parameter.
|
|
323
|
+
*
|
|
324
|
+
* Every method is synchronous: this object is handed over only after the
|
|
325
|
+
* retained toolchain has been acquired.
|
|
326
|
+
*
|
|
327
|
+
* @see {@link EraSeam}
|
|
328
|
+
*/
|
|
329
|
+
interface Ledger8Engine {
|
|
330
|
+
downConvertForExecution(state: EncodedStateValue): DownConvertedState;
|
|
331
|
+
executeCircuit(options: ExecuteCircuitOptions): TranscriptPojo;
|
|
332
|
+
wrapKeepStateCall(options: WrapKeepStateCallOptions): ContractCallPrototype;
|
|
333
|
+
executeConstructor(options: ExecuteConstructorOptions): ConstructorResultPojo;
|
|
334
|
+
/**
|
|
335
|
+
* Re-expresses a retained-era contract's entry points as a current-era contract state, so a
|
|
336
|
+
* keep-state call has an operation registry the current composer can read.
|
|
337
|
+
*
|
|
338
|
+
* Fork-crossing work, which is why it sits here rather than on either era facade: the input is
|
|
339
|
+
* what the retained decoder read off the chain, and the output is for the current ledger.
|
|
340
|
+
*/
|
|
341
|
+
reexpressOperationsForCurrentEra(entryPoints: readonly ContractEntryPointPojo[]): Uint8Array;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Acquires the retained pre-fork toolchain — the `compact-runtime@0.16` glue
|
|
345
|
+
* and `@midnight-ntwrk/onchain-runtime-v3` — and builds a {@link Ledger8Engine}
|
|
346
|
+
* bound to it.
|
|
347
|
+
*
|
|
348
|
+
* Runs {@link assertSharedLedger8Instance} exactly once, on the
|
|
349
|
+
* `onchain-runtime-v3` axis. Any acquisition failure surfaces through the
|
|
350
|
+
* facade (`lib/v8/load-engine.ts`) as `Ledger8RuntimeMissingError`
|
|
351
|
+
* (`../../errors.ts`).
|
|
352
|
+
*
|
|
353
|
+
* Does NOT acquire the v8 ledger module: a consumer that only executes
|
|
354
|
+
* circuits and binds them onto v9 never instantiates the multi-megabyte v8
|
|
355
|
+
* WASM, and never hard-depends on ledger-v8 resolving.
|
|
356
|
+
*
|
|
357
|
+
* @returns The engine surface, with the acquired runtime captured in closure.
|
|
358
|
+
* @throws Ledger8InstanceMismatchError If `onchain-runtime-v3` resolved to two
|
|
359
|
+
* physically distinct copies in this process.
|
|
360
|
+
* @see {@link EraSeam}
|
|
361
|
+
* @see {@link DualInstantiationGuard}
|
|
362
|
+
* @see {@link ModuleGraphAndLazyLoading}
|
|
363
|
+
*/
|
|
364
|
+
declare const createLedger8Engine: () => Promise<Ledger8Engine>;
|
|
365
|
+
|
|
366
|
+
export { createLedger8Engine };
|
|
367
|
+
export type { ConstructorResultPojo, ContractEntryPointPojo, DownConvertedState, ExecuteCircuitOptions, ExecuteConstructorOptions, Ledger8ChargedState, Ledger8DeployableContractState, Ledger8Engine, Ledger8StateValue, TranscriptPojo, WrapKeepStateCallOptions };
|