@midnight-ntwrk/wallet-sdk-capabilities 4.0.0-beta.2 → 4.0.0-beta.3
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 +27 -3
- package/dist/chainVersion/chainVersionProbe.d.ts +95 -0
- package/dist/chainVersion/chainVersionProbe.js +83 -0
- package/dist/chainVersion/index.d.ts +1 -0
- package/dist/chainVersion/index.js +13 -0
- package/dist/codecs/index.d.ts +1 -0
- package/dist/codecs/index.js +13 -0
- package/dist/codecs/ledgerParameters.d.ts +81 -0
- package/dist/codecs/ledgerParameters.js +63 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/pendingTransactions/pendingTransactions.d.ts +113 -9
- package/dist/pendingTransactions/pendingTransactions.js +147 -30
- package/dist/pendingTransactions/pendingTransactionsService.d.ts +25 -9
- package/dist/pendingTransactions/pendingTransactionsService.js +33 -21
- package/dist/proving/index.d.ts +2 -0
- package/dist/proving/index.js +2 -0
- package/dist/proving/provingService.d.ts +175 -15
- package/dist/proving/provingService.js +104 -11
- package/dist/proving/v8ProvingService.d.ts +53 -0
- package/dist/proving/v8ProvingService.js +71 -0
- package/dist/proving/versionedProving.d.ts +42 -0
- package/dist/proving/versionedProving.js +103 -0
- package/dist/signatures/index.d.ts +2 -0
- package/dist/signatures/index.js +14 -0
- package/dist/signatures/signing.d.ts +37 -0
- package/dist/signatures/signing.js +13 -0
- package/dist/signatures/v8Signatures.d.ts +54 -0
- package/dist/signatures/v8Signatures.js +62 -0
- package/dist/simulation/ForkSimulator.d.ts +114 -0
- package/dist/simulation/ForkSimulator.js +209 -0
- package/dist/simulation/LedgerTranslation.d.ts +53 -0
- package/dist/simulation/LedgerTranslation.js +56 -0
- package/dist/simulation/core/VersionTimeline.d.ts +54 -0
- package/dist/simulation/core/VersionTimeline.js +56 -0
- package/dist/simulation/core/blocks.d.ts +38 -0
- package/dist/simulation/core/blocks.js +52 -0
- package/dist/simulation/core/index.d.ts +13 -0
- package/dist/simulation/core/index.js +25 -0
- package/dist/simulation/core/strictness.d.ts +29 -0
- package/dist/simulation/core/strictness.js +39 -0
- package/dist/simulation/index.d.ts +22 -2
- package/dist/simulation/index.js +25 -14
- package/dist/simulation/v8/Simulator.d.ts +231 -0
- package/dist/simulation/v8/Simulator.js +503 -0
- package/dist/simulation/{SimulatorState.d.ts → v8/SimulatorState.d.ts} +31 -44
- package/dist/simulation/v8/SimulatorState.js +290 -0
- package/dist/simulation/v8/index.d.ts +2 -0
- package/dist/simulation/v8/index.js +26 -0
- package/dist/simulation/{Simulator.d.ts → v9/Simulator.d.ts} +57 -6
- package/dist/simulation/{Simulator.js → v9/Simulator.js} +68 -18
- package/dist/simulation/v9/SimulatorState.d.ts +336 -0
- package/dist/simulation/{SimulatorState.js → v9/SimulatorState.js} +34 -67
- package/dist/simulation/v9/index.d.ts +2 -0
- package/dist/simulation/v9/index.js +26 -0
- package/dist/submission/submissionService.d.ts +2 -1
- package/dist/submission/submissionService.js +1 -1
- package/dist/validation/blockData.d.ts +42 -2
- package/dist/validation/blockData.js +55 -8
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/v8ValidationService.d.ts +29 -0
- package/dist/validation/v8ValidationService.js +48 -0
- package/dist/validation/validationService.d.ts +132 -17
- package/dist/validation/validationService.js +88 -42
- package/dist/validation/versionedValidation.d.ts +46 -0
- package/dist/validation/versionedValidation.js +55 -0
- package/package.json +23 -9
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
/**
|
|
14
|
+
* SimulatorState types and pure functions for state manipulation.
|
|
15
|
+
*
|
|
16
|
+
* This module contains the core data types and pure functions for working with simulator state. All functions are
|
|
17
|
+
* synchronous and side-effect free.
|
|
18
|
+
*/
|
|
19
|
+
import { Either, Function as EFunction, Array as EArray } from 'effect';
|
|
20
|
+
import { LedgerState, WellFormedStrictness, TransactionContext, } from '@midnight-ntwrk/ledger-v8';
|
|
21
|
+
import { DateOps, LedgerOps } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
22
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
23
|
+
import { blockHash, protocolVersionAt, } from '../core/index.js';
|
|
24
|
+
// Version-agnostic simulator internals, re-exported so each version's barrel presents one complete surface.
|
|
25
|
+
export { blockHash, defaultStrictness, genesisStrictness, getProtocolVersion, nextBlockContextFromBlock, protocolVersionAt, scheduleFork, setProtocolVersion, } from '../core/index.js';
|
|
26
|
+
/**
|
|
27
|
+
* Assign strictness to a pending transaction, creating a ready transaction. If the pending transaction already has
|
|
28
|
+
* strictness, use it; otherwise use the provided default.
|
|
29
|
+
*/
|
|
30
|
+
export const assignStrictness = (pendingTx, defaultStrictness) => ({
|
|
31
|
+
tx: pendingTx.tx,
|
|
32
|
+
strictness: pendingTx.strictness ?? defaultStrictness,
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Assign strictness to all pending transactions, creating ready transactions. Transactions with existing strictness
|
|
36
|
+
* keep their strictness; others get the default.
|
|
37
|
+
*/
|
|
38
|
+
export const assignStrictnessToAll = (transactions, defaultStrictness) => transactions.map((tx) => assignStrictness(tx, defaultStrictness));
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// State Accessors (Pure Functions)
|
|
41
|
+
// =============================================================================
|
|
42
|
+
/** Get the last produced block, or undefined if no blocks yet. */
|
|
43
|
+
export const getLastBlock = (state) => EArray.lastNonEmpty(state.blocks);
|
|
44
|
+
/** Get the current block number (height of the last block, or 0 if no blocks). */
|
|
45
|
+
export const getCurrentBlockNumber = (state) => getLastBlock(state)?.number;
|
|
46
|
+
/** Get a block by its number. */
|
|
47
|
+
export const getBlockByNumber = EFunction.dual(2, (state, number) => state.blocks.find((b) => b.number === number));
|
|
48
|
+
/** Get all transaction results from the last block. */
|
|
49
|
+
export const getLastBlockResults = (state) => getLastBlock(state)?.transactions.map((t) => t.result) ?? [];
|
|
50
|
+
/** Get all events from the last block (flattened from all transactions). */
|
|
51
|
+
export const getLastBlockEvents = (state) => getLastBlockResults(state).flatMap((r) => r.events);
|
|
52
|
+
/**
|
|
53
|
+
* Get all events from blocks with number >= fromBlockNumber. Returns events ordered by block number, with each block's
|
|
54
|
+
* transactions flattened.
|
|
55
|
+
*
|
|
56
|
+
* Use this with the wallet's next-to-process index: `appliedIndex` after processing should be set to `lastBlockNumber +
|
|
57
|
+
* 1`, not `lastBlockNumber`.
|
|
58
|
+
*/
|
|
59
|
+
export const getBlockEventsFrom = EFunction.dual(2, (state, fromBlockNumber) => state.blocks
|
|
60
|
+
.filter((b) => b.number >= fromBlockNumber)
|
|
61
|
+
.flatMap((b) => b.transactions.flatMap((t) => t.result.events)));
|
|
62
|
+
/**
|
|
63
|
+
* @deprecated Use getBlockEventsFrom instead with proper appliedIndex semantics Get all events from blocks with number
|
|
64
|
+
*
|
|
65
|
+
* > AfterBlockNumber.
|
|
66
|
+
*/
|
|
67
|
+
export const getBlockEventsSince = EFunction.dual(2, (state, afterBlockNumber) => state.blocks
|
|
68
|
+
.filter((b) => b.number > afterBlockNumber)
|
|
69
|
+
.flatMap((b) => b.transactions.flatMap((t) => t.result.events)));
|
|
70
|
+
/** Check if there are pending transactions in the mempool. */
|
|
71
|
+
export const hasPendingTransactions = (state) => state.mempool.length > 0;
|
|
72
|
+
/** Get the current simulator time. */
|
|
73
|
+
export const getCurrentTime = (state) => state.currentTime;
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// State Transformations (Pure Functions)
|
|
76
|
+
// =============================================================================
|
|
77
|
+
/** Resolve fullness from spec and state. */
|
|
78
|
+
export const resolveFullness = (spec, state) => typeof spec === 'function' ? spec(state) : spec;
|
|
79
|
+
/** Create a block production request that includes all mempool transactions. */
|
|
80
|
+
export const allMempoolTransactions = (state, fullness, defaultStrictness) => ({
|
|
81
|
+
transactions: assignStrictnessToAll(state.mempool, defaultStrictness),
|
|
82
|
+
fullness,
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Create a blank initial state.
|
|
86
|
+
*
|
|
87
|
+
* @param networkId - Network identifier
|
|
88
|
+
* @param options - Optional genesis parameters
|
|
89
|
+
* @param options.protocolVersion - Version the chain starts on (defaults to the minimum supported version)
|
|
90
|
+
* @param options.genesisBlockNumber - Height of the genesis block (defaults to 0; a ledger-v9 chain continues numbering
|
|
91
|
+
* from the fork height instead)
|
|
92
|
+
* @param options.genesisTime - Timestamp of the genesis block (defaults to the epoch)
|
|
93
|
+
*/
|
|
94
|
+
export const blankState = async (networkId, options) => {
|
|
95
|
+
const protocolVersion = options?.protocolVersion ?? ProtocolVersion.MinSupportedVersion;
|
|
96
|
+
const genesisBlockNumber = options?.genesisBlockNumber ?? 0n;
|
|
97
|
+
const genesisTime = options?.genesisTime ?? new Date(0);
|
|
98
|
+
const blankGenesis = {
|
|
99
|
+
number: genesisBlockNumber,
|
|
100
|
+
hash: await blockHash(genesisBlockNumber),
|
|
101
|
+
timestamp: genesisTime,
|
|
102
|
+
transactions: [],
|
|
103
|
+
protocolVersion,
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
networkId,
|
|
107
|
+
ledger: LedgerState.blank(networkId),
|
|
108
|
+
blocks: [blankGenesis],
|
|
109
|
+
mempool: [],
|
|
110
|
+
currentTime: genesisTime,
|
|
111
|
+
protocolVersion,
|
|
112
|
+
scheduledForks: [],
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
/** Add a pending transaction to the mempool. */
|
|
116
|
+
export const addToMempool = (state, pendingTx) => ({
|
|
117
|
+
...state,
|
|
118
|
+
mempool: [...state.mempool, pendingTx],
|
|
119
|
+
});
|
|
120
|
+
/** Remove transactions from the mempool. */
|
|
121
|
+
export const removeFromMempool = (state, transactions) => {
|
|
122
|
+
const txsToRemove = new Set(transactions.map((t) => t.tx));
|
|
123
|
+
return {
|
|
124
|
+
...state,
|
|
125
|
+
mempool: state.mempool.filter((pending) => !txsToRemove.has(pending.tx)),
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/** Advance the simulator time by the given number of seconds. */
|
|
129
|
+
export const advanceTime = (state, seconds) => ({
|
|
130
|
+
...state,
|
|
131
|
+
currentTime: DateOps.addSeconds(state.currentTime, seconds),
|
|
132
|
+
});
|
|
133
|
+
/** Update the ledger state. */
|
|
134
|
+
export const updateLedger = (state, ledger) => ({
|
|
135
|
+
...state,
|
|
136
|
+
ledger,
|
|
137
|
+
});
|
|
138
|
+
/** Append a block to the state and update time. */
|
|
139
|
+
export const appendBlock = (state, block, newLedger) => ({
|
|
140
|
+
...state,
|
|
141
|
+
ledger: newLedger,
|
|
142
|
+
blocks: [...state.blocks, block],
|
|
143
|
+
currentTime: block.timestamp,
|
|
144
|
+
});
|
|
145
|
+
// =============================================================================
|
|
146
|
+
// Transaction Application (Pure Function)
|
|
147
|
+
// =============================================================================
|
|
148
|
+
/**
|
|
149
|
+
* Pure state transition: apply a transaction to the simulator state. Returns Either with the new state or an error.
|
|
150
|
+
*
|
|
151
|
+
* @param state - Current simulator state
|
|
152
|
+
* @param tx - Transaction to apply
|
|
153
|
+
* @param strictness - Well-formedness strictness options
|
|
154
|
+
* @param blockContext - Block context for the transaction
|
|
155
|
+
* @param options - Optional parameters
|
|
156
|
+
* @param options.blockNumber - Override block number (defaults to last block + 1)
|
|
157
|
+
* @param options.blockFullness - Override detailed block fullness (SyntheticCost)
|
|
158
|
+
* @param options.overallBlockFullness - Override overall block fullness (0-1 value)
|
|
159
|
+
*/
|
|
160
|
+
export const applyTransaction = (state, tx, strictness, blockContext, options) => {
|
|
161
|
+
return LedgerOps.ledgerTry(() => {
|
|
162
|
+
const computedFullness = options?.blockFullness ?? tx.cost(state.ledger.parameters);
|
|
163
|
+
const detailedBlockFullness = state.ledger.parameters.normalizeFullness(computedFullness);
|
|
164
|
+
const computedBlockFullness = options?.overallBlockFullness ??
|
|
165
|
+
Math.max(detailedBlockFullness.readTime, detailedBlockFullness.computeTime, detailedBlockFullness.blockUsage, detailedBlockFullness.bytesWritten, detailedBlockFullness.bytesChurned);
|
|
166
|
+
const blockNumber = options?.blockNumber ?? getCurrentBlockNumber(state) + 1n;
|
|
167
|
+
const protocolVersion = protocolVersionAt(state, blockNumber);
|
|
168
|
+
const blockTime = state.currentTime;
|
|
169
|
+
const verifiedTransaction = tx.wellFormed(state.ledger, strictness, blockTime);
|
|
170
|
+
const transactionContext = new TransactionContext(state.ledger, blockContext);
|
|
171
|
+
const [newLedgerState, txResult] = state.ledger.apply(verifiedTransaction, transactionContext);
|
|
172
|
+
const newBlock = {
|
|
173
|
+
number: blockNumber,
|
|
174
|
+
hash: blockContext.parentBlockHash,
|
|
175
|
+
timestamp: blockTime,
|
|
176
|
+
transactions: [{ tx, result: txResult }],
|
|
177
|
+
protocolVersion,
|
|
178
|
+
};
|
|
179
|
+
const newState = {
|
|
180
|
+
...state,
|
|
181
|
+
ledger: newLedgerState.postBlockUpdate(blockTime, detailedBlockFullness, computedBlockFullness),
|
|
182
|
+
blocks: [...state.blocks, newBlock],
|
|
183
|
+
protocolVersion,
|
|
184
|
+
};
|
|
185
|
+
return [newBlock, newState];
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Process a single pending transaction against the latest ledger state. Returns Either with the processing result or an
|
|
190
|
+
* error.
|
|
191
|
+
*
|
|
192
|
+
* @param ledger - The ledger state to process against
|
|
193
|
+
* @param readyTx - Transaction to process
|
|
194
|
+
* @param blockTime - Block timestamp
|
|
195
|
+
* @param blockContext - Block context
|
|
196
|
+
* @param minFullness - Minimum block fullness to use
|
|
197
|
+
*/
|
|
198
|
+
export const processTransaction = (ledger, readyTx, blockTime, blockContext, minFullness) => {
|
|
199
|
+
return LedgerOps.ledgerTry(() => {
|
|
200
|
+
const computedFullness = readyTx.tx.cost(ledger.parameters);
|
|
201
|
+
const detailedBlockFullness = ledger.parameters.normalizeFullness(computedFullness);
|
|
202
|
+
const computedBlockFullness = Math.max(minFullness, detailedBlockFullness.readTime, detailedBlockFullness.computeTime, detailedBlockFullness.blockUsage, detailedBlockFullness.bytesWritten, detailedBlockFullness.bytesChurned);
|
|
203
|
+
// Use the assigned strictness
|
|
204
|
+
const verifiedTransaction = readyTx.tx.wellFormed(ledger, readyTx.strictness, blockTime);
|
|
205
|
+
const transactionContext = new TransactionContext(ledger, blockContext);
|
|
206
|
+
const [newLedgerState, txResult] = ledger.apply(verifiedTransaction, transactionContext);
|
|
207
|
+
const postBlockLedger = newLedgerState.postBlockUpdate(blockTime, detailedBlockFullness, computedBlockFullness);
|
|
208
|
+
return {
|
|
209
|
+
tx: readyTx.tx,
|
|
210
|
+
result: txResult,
|
|
211
|
+
newLedger: postBlockLedger,
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Process multiple transactions in sequence, accumulating results. Returns Either with all results and final ledger, or
|
|
217
|
+
* first error. Each transaction uses its assigned strictness (from ReadyTransaction).
|
|
218
|
+
*
|
|
219
|
+
* @param ledger - Initial ledger state
|
|
220
|
+
* @param transactions - Transactions to process (each with assigned strictness)
|
|
221
|
+
* @param blockTime - Block timestamp
|
|
222
|
+
* @param blockContext - Block context
|
|
223
|
+
* @param fullness - Block fullness (0-1)
|
|
224
|
+
*/
|
|
225
|
+
export const processTransactions = (ledger, transactions, blockTime, blockContext, fullness) => transactions.reduce((acc, readyTx) => Either.flatMap(acc, ({ blockTransactions, finalLedger }) => Either.map(processTransaction(finalLedger, readyTx, blockTime, blockContext, fullness), (result) => ({
|
|
226
|
+
blockTransactions: [...blockTransactions, { tx: result.tx, result: result.result }],
|
|
227
|
+
finalLedger: result.newLedger,
|
|
228
|
+
}))), Either.right({ blockTransactions: [], finalLedger: ledger }));
|
|
229
|
+
/**
|
|
230
|
+
* Create a block from processed transactions and update state. Pure function that takes pre-computed block hash.
|
|
231
|
+
*
|
|
232
|
+
* @param state - Current simulator state
|
|
233
|
+
* @param blockTransactions - Processed transactions to include
|
|
234
|
+
* @param blockHash - Pre-computed block hash
|
|
235
|
+
* @param blockTime - Block timestamp
|
|
236
|
+
* @param newLedger - New ledger state after processing transactions
|
|
237
|
+
* @param processedTxs - Ready transactions to remove from mempool
|
|
238
|
+
*/
|
|
239
|
+
export const createBlock = (state, blockTransactions, blockHashValue, blockTime, newLedger, processedTxs) => {
|
|
240
|
+
const nextBlockNumber = getCurrentBlockNumber(state) + 1n;
|
|
241
|
+
// Resolved once, at production time: the block and the chain's current version cannot disagree.
|
|
242
|
+
const protocolVersion = protocolVersionAt(state, nextBlockNumber);
|
|
243
|
+
const block = {
|
|
244
|
+
number: nextBlockNumber,
|
|
245
|
+
hash: blockHashValue,
|
|
246
|
+
timestamp: blockTime,
|
|
247
|
+
transactions: blockTransactions,
|
|
248
|
+
protocolVersion,
|
|
249
|
+
};
|
|
250
|
+
const txsToRemove = new Set(processedTxs.map((t) => t.tx));
|
|
251
|
+
const newState = {
|
|
252
|
+
...state,
|
|
253
|
+
ledger: newLedger,
|
|
254
|
+
blocks: [...state.blocks, block],
|
|
255
|
+
mempool: state.mempool.filter((pending) => !txsToRemove.has(pending.tx)),
|
|
256
|
+
currentTime: blockTime,
|
|
257
|
+
protocolVersion,
|
|
258
|
+
};
|
|
259
|
+
return [block, newState];
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Create an empty block (no transactions) and update state.
|
|
263
|
+
*
|
|
264
|
+
* @param state - Current simulator state
|
|
265
|
+
* @param blockHashValue - Pre-computed block hash
|
|
266
|
+
* @param blockTime - Block timestamp
|
|
267
|
+
* @param processedTxs - Original pending transactions to remove from mempool (if any)
|
|
268
|
+
*/
|
|
269
|
+
export const createEmptyBlock = (state, blockHashValue, blockTime, processedTxs = []) => {
|
|
270
|
+
return createBlock(state, [], blockHashValue, blockTime, state.ledger, processedTxs);
|
|
271
|
+
};
|
|
272
|
+
// =============================================================================
|
|
273
|
+
// Helper Functions
|
|
274
|
+
// =============================================================================
|
|
275
|
+
/**
|
|
276
|
+
* Create a WellFormedStrictness instance with configurable options. All options default to false for maximum testing
|
|
277
|
+
* flexibility.
|
|
278
|
+
*
|
|
279
|
+
* Note: WellFormedStrictness is a class from the ledger library that requires mutation to configure. This is
|
|
280
|
+
* unavoidable given the external API design.
|
|
281
|
+
*/
|
|
282
|
+
export const createStrictness = (config = {}) => {
|
|
283
|
+
const strictness = new WellFormedStrictness();
|
|
284
|
+
strictness.enforceBalancing = config.enforceBalancing ?? false;
|
|
285
|
+
strictness.verifyNativeProofs = config.verifyNativeProofs ?? false;
|
|
286
|
+
strictness.verifyContractProofs = config.verifyContractProofs ?? false;
|
|
287
|
+
strictness.enforceLimits = config.enforceLimits ?? false;
|
|
288
|
+
strictness.verifySignatures = config.verifySignatures ?? false;
|
|
289
|
+
return strictness;
|
|
290
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { getLastBlock, getCurrentBlockNumber, getCurrentTime, getProtocolVersion, protocolVersionAt, getBlockByNumber, getLastBlockResults, getLastBlockEvents, getBlockEventsFrom, getBlockEventsSince, hasPendingTransactions, resolveFullness, allMempoolTransactions, blankState, addToMempool, removeFromMempool, advanceTime, updateLedger, appendBlock, applyTransaction, setProtocolVersion, scheduleFork, processTransaction, processTransactions, createBlock, createEmptyBlock, type TransactionProcessingResult, createStrictness, blockHash, assignStrictness, assignStrictnessToAll, defaultStrictness, genesisStrictness, type SimulatorState, type Block, type BlockTransaction, type BlockInfo, type PendingTransaction, type ReadyTransaction, type BlockProductionRequest, type BlockProducer, type FullnessSpec, type GenesisMint, type ScheduledFork, type StrictnessConfig, } from './SimulatorState.js';
|
|
2
|
+
export { Simulator, immediateBlockProducer, type SimulatorConfig } from './Simulator.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
// Re-export everything from SimulatorState
|
|
14
|
+
export {
|
|
15
|
+
// State accessor functions (composable with simulator.query())
|
|
16
|
+
getLastBlock, getCurrentBlockNumber, getCurrentTime, getProtocolVersion, protocolVersionAt, getBlockByNumber, getLastBlockResults, getLastBlockEvents, getBlockEventsFrom, getBlockEventsSince, hasPendingTransactions,
|
|
17
|
+
// State transformation functions
|
|
18
|
+
resolveFullness, allMempoolTransactions, blankState, addToMempool, removeFromMempool, advanceTime, updateLedger, appendBlock, applyTransaction, setProtocolVersion, scheduleFork,
|
|
19
|
+
// Block production functions
|
|
20
|
+
processTransaction, processTransactions, createBlock, createEmptyBlock,
|
|
21
|
+
// Helper functions
|
|
22
|
+
createStrictness, blockHash, assignStrictness, assignStrictnessToAll,
|
|
23
|
+
// Strictness constants
|
|
24
|
+
defaultStrictness, genesisStrictness, } from './SimulatorState.js';
|
|
25
|
+
// Re-export from Simulator
|
|
26
|
+
export { Simulator, immediateBlockProducer } from './Simulator.js';
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
import { type Array as Arr, Effect, type Scope, Stream, SubscriptionRef } from 'effect';
|
|
13
13
|
import { type ProofErasedTransaction, type SignatureVerifyingKey } from '@midnightntwrk/ledger-v9';
|
|
14
14
|
import { LedgerOps } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
15
|
-
import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
15
|
+
import { NetworkId, ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
16
16
|
import { type Block, type BlockProducer, type FullnessSpec, type GenesisMint, type SimulatorState, type StrictnessConfig } from './SimulatorState.js';
|
|
17
|
-
export type { SimulatorState, Block, BlockTransaction, PendingTransaction, BlockInfo, BlockProductionRequest, BlockProducer, FullnessSpec, GenesisMint, StrictnessConfig, } from './SimulatorState.js';
|
|
18
|
-
export { getLastBlock, getCurrentBlockNumber, getBlockByNumber, getLastBlockResults, getLastBlockEvents, hasPendingTransactions, getCurrentTime, applyTransaction, defaultStrictness, genesisStrictness, createStrictness, } from './SimulatorState.js';
|
|
17
|
+
export type { SimulatorState, Block, BlockTransaction, PendingTransaction, BlockInfo, BlockProductionRequest, BlockProducer, FullnessSpec, GenesisMint, ScheduledFork, StrictnessConfig, } from './SimulatorState.js';
|
|
18
|
+
export { getLastBlock, getCurrentBlockNumber, getBlockByNumber, getLastBlockResults, getLastBlockEvents, hasPendingTransactions, getCurrentTime, getProtocolVersion, protocolVersionAt, applyTransaction, defaultStrictness, genesisStrictness, createStrictness, } from './SimulatorState.js';
|
|
19
19
|
/**
|
|
20
20
|
* Default block producer: produces a block for each state change with non-empty mempool.
|
|
21
21
|
*
|
|
@@ -37,6 +37,25 @@ export type SimulatorConfig = Readonly<{
|
|
|
37
37
|
networkId?: NetworkId.NetworkId;
|
|
38
38
|
/** Custom block producer. Defaults to immediateBlockProducer(). */
|
|
39
39
|
blockProducer?: BlockProducer;
|
|
40
|
+
/**
|
|
41
|
+
* Protocol version the chain starts on. Defaults to {@link ProtocolVersion.MinSupportedVersion}.
|
|
42
|
+
*
|
|
43
|
+
* The version a fork activates is never built in — schedule it with {@link Simulator.scheduleFork} or switch to it
|
|
44
|
+
* with {@link Simulator.setProtocolVersion}.
|
|
45
|
+
*/
|
|
46
|
+
protocolVersion?: ProtocolVersion.ProtocolVersion;
|
|
47
|
+
/**
|
|
48
|
+
* Height of the genesis block. Defaults to 0.
|
|
49
|
+
*
|
|
50
|
+
* Set this to continue an existing chain's numbering, as a ledger-v9 chain does from the fork height.
|
|
51
|
+
*/
|
|
52
|
+
genesisBlockNumber?: bigint;
|
|
53
|
+
/**
|
|
54
|
+
* Timestamp of the genesis block, from which simulator time advances. Defaults to the epoch.
|
|
55
|
+
*
|
|
56
|
+
* Set this to continue an existing chain's timeline, so that time-sensitive behaviour does not restart at the epoch.
|
|
57
|
+
*/
|
|
58
|
+
genesisTime?: Date;
|
|
40
59
|
}>;
|
|
41
60
|
/**
|
|
42
61
|
* Unified simulator for wallet testing.
|
|
@@ -85,11 +104,19 @@ export declare class Simulator {
|
|
|
85
104
|
private static initBlank;
|
|
86
105
|
/**
|
|
87
106
|
* Initialize simulator with genesis mints (pre-funded accounts). Supports shielded and unshielded token mints. Night
|
|
88
|
-
* tokens are auto-detected by comparing tokenType with
|
|
107
|
+
* tokens are auto-detected by comparing tokenType with `Token.night`.
|
|
89
108
|
*/
|
|
90
109
|
private static initWithGenesis;
|
|
91
|
-
/**
|
|
92
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Create a Simulator from an initial state with proper stream setup.
|
|
112
|
+
*
|
|
113
|
+
* The low-level constructor behind {@link Simulator.init}. Use it when the initial state cannot be expressed as a
|
|
114
|
+
* config — most importantly a ledger-v9 chain, whose ledger is handed over from the chain before it.
|
|
115
|
+
*
|
|
116
|
+
* @param initialState - State the simulator starts from
|
|
117
|
+
* @param blockProducer - Custom block producer (defaults to immediateBlockProducer())
|
|
118
|
+
*/
|
|
119
|
+
static fromState(initialState: SimulatorState, blockProducer?: BlockProducer): Effect.Effect<Simulator, never, Scope.Scope>;
|
|
93
120
|
/** Observable stream of simulator state changes. */
|
|
94
121
|
readonly state$: Stream.Stream<SimulatorState>;
|
|
95
122
|
constructor(stateRef: SubscriptionRef.SubscriptionRef<SimulatorState>, state$: Stream.Stream<SimulatorState>);
|
|
@@ -141,6 +168,30 @@ export declare class Simulator {
|
|
|
141
168
|
submitAndForget(tx: ProofErasedTransaction, options?: {
|
|
142
169
|
strictness?: StrictnessConfig;
|
|
143
170
|
}): Effect.Effect<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Produce a block with no transactions, advancing the chain by one height.
|
|
173
|
+
*
|
|
174
|
+
* Bypasses the configured block producer, which only produces when there is something in the mempool. Use it to reach
|
|
175
|
+
* a given height without traffic — a fork happens at a height whether or not transactions were submitted.
|
|
176
|
+
*
|
|
177
|
+
* @returns The produced block
|
|
178
|
+
*/
|
|
179
|
+
produceEmptyBlock(): Effect.Effect<Block, LedgerOps.LedgerError>;
|
|
180
|
+
/**
|
|
181
|
+
* Switch the chain to a protocol version, effective for blocks produced from now on. Already-produced blocks keep the
|
|
182
|
+
* version they were produced under.
|
|
183
|
+
*
|
|
184
|
+
* @param version - Version to switch the chain to
|
|
185
|
+
*/
|
|
186
|
+
setProtocolVersion(version: ProtocolVersion.ProtocolVersion): Effect.Effect<void>;
|
|
187
|
+
/**
|
|
188
|
+
* Schedule a protocol version to activate at a block height — a fork on the simulated chain. The block produced at
|
|
189
|
+
* that height, and every block after it, is stamped with `version`.
|
|
190
|
+
*
|
|
191
|
+
* @param atBlock - Height of the first block produced under `version`
|
|
192
|
+
* @param version - Version that activates at `atBlock`
|
|
193
|
+
*/
|
|
194
|
+
scheduleFork(atBlock: bigint, version: ProtocolVersion.ProtocolVersion): Effect.Effect<void>;
|
|
144
195
|
/**
|
|
145
196
|
* Fast-forward the simulator time by the given number of seconds. Does not produce a block - only advances the
|
|
146
197
|
* internal clock. Useful for testing time-sensitive functionality like TTL.
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
* - Time advancement for TTL and time-sensitive tests
|
|
23
23
|
*/
|
|
24
24
|
import { Effect, Either, pipe, Stream, SubscriptionRef } from 'effect';
|
|
25
|
-
import { addressFromKey, ClaimRewardsTransaction, createShieldedCoinInfo, Intent, LedgerState,
|
|
25
|
+
import { addressFromKey, ClaimRewardsTransaction, createShieldedCoinInfo, Intent, LedgerState, SignatureErased, Transaction, TransactionContext, UnshieldedOffer, ZswapOffer, ZswapOutput, } from '@midnightntwrk/ledger-v9';
|
|
26
26
|
import { DateOps, LedgerOps } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
27
|
-
import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
28
|
-
import { addToMempool, allMempoolTransactions, blankState, blockHash, createBlock, createEmptyBlock, createStrictness, defaultStrictness, genesisStrictness, getLastBlock, hasPendingTransactions, nextBlockContextFromBlock, processTransactions, removeFromMempool, resolveFullness, } from './SimulatorState.js';
|
|
27
|
+
import { NetworkId, ProtocolVersion, Token } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
28
|
+
import { addToMempool, allMempoolTransactions, blankState, blockHash, createBlock, createEmptyBlock, createStrictness, defaultStrictness, genesisStrictness, getLastBlock, hasPendingTransactions, nextBlockContextFromBlock, processTransactions, removeFromMempool, resolveFullness, scheduleFork, setProtocolVersion, } from './SimulatorState.js';
|
|
29
29
|
// =============================================================================
|
|
30
30
|
// Type Guards and Helpers for Genesis Mints
|
|
31
31
|
// =============================================================================
|
|
@@ -34,12 +34,12 @@ const isShieldedMint = (mint) => mint.type === 'shielded';
|
|
|
34
34
|
/** Type guard for unshielded genesis mint. */
|
|
35
35
|
const isUnshieldedMint = (mint) => mint.type === 'unshielded';
|
|
36
36
|
/**
|
|
37
|
-
* Check if an unshielded mint is for the native Night token. Night is auto-detected by comparing tokenType with
|
|
38
|
-
* ledger.
|
|
37
|
+
* Check if an unshielded mint is for the native Night token. Night is auto-detected by comparing tokenType with the
|
|
38
|
+
* SDK's own `Token.night`, which is what both ledger versions call the native token.
|
|
39
39
|
*/
|
|
40
|
-
const isNightToken = (tokenType) => tokenType ===
|
|
40
|
+
const isNightToken = (tokenType) => tokenType === Token.night;
|
|
41
41
|
// Re-export state accessors for backward compatibility
|
|
42
|
-
export { getLastBlock, getCurrentBlockNumber, getBlockByNumber, getLastBlockResults, getLastBlockEvents, hasPendingTransactions, getCurrentTime, applyTransaction, defaultStrictness, genesisStrictness, createStrictness, } from './SimulatorState.js';
|
|
42
|
+
export { getLastBlock, getCurrentBlockNumber, getBlockByNumber, getLastBlockResults, getLastBlockEvents, hasPendingTransactions, getCurrentTime, getProtocolVersion, protocolVersionAt, applyTransaction, defaultStrictness, genesisStrictness, createStrictness, } from './SimulatorState.js';
|
|
43
43
|
// =============================================================================
|
|
44
44
|
// Block Producers
|
|
45
45
|
// =============================================================================
|
|
@@ -102,19 +102,28 @@ export class Simulator {
|
|
|
102
102
|
*/
|
|
103
103
|
static init(config = {}) {
|
|
104
104
|
const networkId = config.networkId ?? NetworkId.NetworkId.Undeployed;
|
|
105
|
+
const genesis = {
|
|
106
|
+
protocolVersion: config.protocolVersion ?? ProtocolVersion.MinSupportedVersion,
|
|
107
|
+
blockNumber: config.genesisBlockNumber ?? 0n,
|
|
108
|
+
time: config.genesisTime ?? new Date(0),
|
|
109
|
+
};
|
|
105
110
|
return config.genesisMints !== undefined && config.genesisMints.length > 0
|
|
106
|
-
? Simulator.initWithGenesis(config.genesisMints, networkId, config.blockProducer)
|
|
107
|
-
: Simulator.initBlank(networkId, config.blockProducer);
|
|
111
|
+
? Simulator.initWithGenesis(config.genesisMints, networkId, genesis, config.blockProducer)
|
|
112
|
+
: Simulator.initBlank(networkId, genesis, config.blockProducer);
|
|
108
113
|
}
|
|
109
114
|
/** Initialize simulator with blank ledger state. */
|
|
110
|
-
static initBlank(networkId, blockProducer) {
|
|
111
|
-
return pipe(Effect.promise(() => blankState(networkId
|
|
115
|
+
static initBlank(networkId, genesis, blockProducer) {
|
|
116
|
+
return pipe(Effect.promise(() => blankState(networkId, {
|
|
117
|
+
protocolVersion: genesis.protocolVersion,
|
|
118
|
+
genesisBlockNumber: genesis.blockNumber,
|
|
119
|
+
genesisTime: genesis.time,
|
|
120
|
+
})), Effect.flatMap((state) => Simulator.fromState(state, blockProducer)));
|
|
112
121
|
}
|
|
113
122
|
/**
|
|
114
123
|
* Initialize simulator with genesis mints (pre-funded accounts). Supports shielded and unshielded token mints. Night
|
|
115
|
-
* tokens are auto-detected by comparing tokenType with
|
|
124
|
+
* tokens are auto-detected by comparing tokenType with `Token.night`.
|
|
116
125
|
*/
|
|
117
|
-
static initWithGenesis(genesisMints, networkId = NetworkId.NetworkId.Undeployed, blockProducer) {
|
|
126
|
+
static initWithGenesis(genesisMints, networkId = NetworkId.NetworkId.Undeployed, genesis, blockProducer) {
|
|
118
127
|
const noStrictness = createStrictness();
|
|
119
128
|
// Pure function to extract shielded mint data (returns array for flatMap)
|
|
120
129
|
const toShieldedMint = (mint) => isShieldedMint(mint) ? [{ tokenType: mint.tokenType, amount: mint.amount, keys: mint.recipient }] : [];
|
|
@@ -124,7 +133,7 @@ export class Simulator {
|
|
|
124
133
|
? [{ tokenType: mint.tokenType, amount: mint.amount, recipient: mint.recipient }]
|
|
125
134
|
: [];
|
|
126
135
|
// Pure function to extract Night mint data from unshielded mints (returns array for flatMap)
|
|
127
|
-
// Night is auto-detected by comparing tokenType with
|
|
136
|
+
// Night is auto-detected by comparing tokenType with `Token.night`
|
|
128
137
|
const toNightMint = (mint) => isUnshieldedMint(mint) && isNightToken(mint.tokenType) && mint.verifyingKey !== undefined
|
|
129
138
|
? [{ amount: mint.amount, recipient: mint.recipient, verifyingKey: mint.verifyingKey }]
|
|
130
139
|
: [];
|
|
@@ -185,9 +194,9 @@ export class Simulator {
|
|
|
185
194
|
return { ledgerState: newState, tx: proofErasedTx, result };
|
|
186
195
|
};
|
|
187
196
|
return Effect.gen(function* () {
|
|
188
|
-
const genesisTime =
|
|
197
|
+
const genesisTime = genesis.time;
|
|
189
198
|
const emptyState = LedgerState.blank(networkId);
|
|
190
|
-
const context = yield* Effect.promise(() => nextBlockContextFromBlock(undefined, genesisTime));
|
|
199
|
+
const context = yield* Effect.promise(() => nextBlockContextFromBlock(undefined, genesisTime, genesis.blockNumber));
|
|
191
200
|
// Process shielded and unshielded mints first
|
|
192
201
|
const init = makeInitialTransactions(emptyState, context, genesisTime);
|
|
193
202
|
// Apply post-block update before processing Night mints
|
|
@@ -211,10 +220,11 @@ export class Simulator {
|
|
|
211
220
|
const allTransactions = [...init.transactions, ...nightResults.transactions];
|
|
212
221
|
// Create genesis block with all transactions
|
|
213
222
|
const genesisBlock = {
|
|
214
|
-
number:
|
|
223
|
+
number: genesis.blockNumber,
|
|
215
224
|
hash: context.parentBlockHash,
|
|
216
225
|
timestamp: genesisTime,
|
|
217
226
|
transactions: allTransactions,
|
|
227
|
+
protocolVersion: genesis.protocolVersion,
|
|
218
228
|
};
|
|
219
229
|
const initialState = {
|
|
220
230
|
networkId,
|
|
@@ -222,11 +232,21 @@ export class Simulator {
|
|
|
222
232
|
blocks: [genesisBlock],
|
|
223
233
|
mempool: [],
|
|
224
234
|
currentTime: genesisTime, // Time stays at genesis; next block will advance it
|
|
235
|
+
protocolVersion: genesis.protocolVersion,
|
|
236
|
+
scheduledForks: [],
|
|
225
237
|
};
|
|
226
238
|
return yield* Simulator.fromState(initialState, blockProducer);
|
|
227
239
|
});
|
|
228
240
|
}
|
|
229
|
-
/**
|
|
241
|
+
/**
|
|
242
|
+
* Create a Simulator from an initial state with proper stream setup.
|
|
243
|
+
*
|
|
244
|
+
* The low-level constructor behind {@link Simulator.init}. Use it when the initial state cannot be expressed as a
|
|
245
|
+
* config — most importantly a ledger-v9 chain, whose ledger is handed over from the chain before it.
|
|
246
|
+
*
|
|
247
|
+
* @param initialState - State the simulator starts from
|
|
248
|
+
* @param blockProducer - Custom block producer (defaults to immediateBlockProducer())
|
|
249
|
+
*/
|
|
230
250
|
static fromState(initialState, blockProducer) {
|
|
231
251
|
return Effect.gen(function* () {
|
|
232
252
|
const stateRef = yield* SubscriptionRef.make(initialState);
|
|
@@ -353,6 +373,36 @@ export class Simulator {
|
|
|
353
373
|
const pendingTx = options?.strictness !== undefined ? { tx, strictness: createStrictness(options.strictness) } : { tx };
|
|
354
374
|
return SubscriptionRef.update(this.#stateRef, (s) => addToMempool(s, pendingTx));
|
|
355
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Produce a block with no transactions, advancing the chain by one height.
|
|
378
|
+
*
|
|
379
|
+
* Bypasses the configured block producer, which only produces when there is something in the mempool. Use it to reach
|
|
380
|
+
* a given height without traffic — a fork happens at a height whether or not transactions were submitted.
|
|
381
|
+
*
|
|
382
|
+
* @returns The produced block
|
|
383
|
+
*/
|
|
384
|
+
produceEmptyBlock() {
|
|
385
|
+
return this.#produceBlock({ transactions: [], fullness: 0 });
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Switch the chain to a protocol version, effective for blocks produced from now on. Already-produced blocks keep the
|
|
389
|
+
* version they were produced under.
|
|
390
|
+
*
|
|
391
|
+
* @param version - Version to switch the chain to
|
|
392
|
+
*/
|
|
393
|
+
setProtocolVersion(version) {
|
|
394
|
+
return SubscriptionRef.update(this.#stateRef, (state) => setProtocolVersion(state, version));
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Schedule a protocol version to activate at a block height — a fork on the simulated chain. The block produced at
|
|
398
|
+
* that height, and every block after it, is stamped with `version`.
|
|
399
|
+
*
|
|
400
|
+
* @param atBlock - Height of the first block produced under `version`
|
|
401
|
+
* @param version - Version that activates at `atBlock`
|
|
402
|
+
*/
|
|
403
|
+
scheduleFork(atBlock, version) {
|
|
404
|
+
return SubscriptionRef.update(this.#stateRef, (state) => scheduleFork(state, atBlock, version));
|
|
405
|
+
}
|
|
356
406
|
/**
|
|
357
407
|
* Fast-forward the simulator time by the given number of seconds. Does not produce a block - only advances the
|
|
358
408
|
* internal clock. Useful for testing time-sensitive functionality like TTL.
|