@hardkas/localnet 0.2.2-alpha → 0.3.0-alpha
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/dist/{chunk-W7ZBGUNK.js → chunk-DPHHOGGK.js} +2 -1
- package/dist/{chunk-CXDVB3K4.js → chunk-GVBX3TPM.js} +2 -1
- package/dist/index.d.ts +77 -4
- package/dist/index.js +316 -83
- package/dist/{receipts-ICI3GEJW.js → receipts-JSTNZMDD.js} +1 -1
- package/dist/{traces-DSACQ53V.js → traces-HDVV77MQ.js} +1 -1
- package/package.json +8 -5
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
|
+
import { writeFileAtomic } from "@hardkas/core";
|
|
5
6
|
function getDefaultTracesDir(cwd = process.cwd()) {
|
|
6
7
|
return path.join(cwd, ".hardkas", "traces");
|
|
7
8
|
}
|
|
@@ -20,7 +21,7 @@ async function saveSimulatedTrace(trace, options) {
|
|
|
20
21
|
await fs.mkdir(dir, { recursive: true });
|
|
21
22
|
}
|
|
22
23
|
const filePath = getTracePath(trace.txId, options?.cwd);
|
|
23
|
-
await
|
|
24
|
+
await writeFileAtomic(filePath, JSON.stringify(trace, null, 2), { encoding: "utf-8" });
|
|
24
25
|
return filePath;
|
|
25
26
|
}
|
|
26
27
|
async function loadSimulatedTrace(txId, options) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
|
+
import { writeFileAtomic } from "@hardkas/core";
|
|
5
6
|
function getDefaultReceiptsDir(cwd = process.cwd()) {
|
|
6
7
|
return path.join(cwd, ".hardkas", "receipts");
|
|
7
8
|
}
|
|
@@ -20,7 +21,7 @@ async function saveSimulatedReceipt(receipt, options) {
|
|
|
20
21
|
await fs.mkdir(dir, { recursive: true });
|
|
21
22
|
}
|
|
22
23
|
const filePath = getReceiptPath(receipt.txId, options?.cwd);
|
|
23
|
-
await
|
|
24
|
+
await writeFileAtomic(filePath, JSON.stringify(receipt, null, 2), { encoding: "utf-8" });
|
|
24
25
|
return filePath;
|
|
25
26
|
}
|
|
26
27
|
async function loadSimulatedReceipt(txId, options) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import * as _hardkas_artifacts from '@hardkas/artifacts';
|
|
1
2
|
import { HardkasArtifactBase, Snapshot as Snapshot$1, ARTIFACT_SCHEMAS, TxReceipt, TxPlan } from '@hardkas/artifacts';
|
|
3
|
+
import * as _hardkas_simulator from '@hardkas/simulator';
|
|
2
4
|
import { ExecutionMode, NetworkId } from '@hardkas/core';
|
|
5
|
+
import { KaspaRpcClient } from '@hardkas/kaspa-rpc';
|
|
3
6
|
|
|
4
7
|
interface HardkasAccount {
|
|
5
8
|
readonly name: string;
|
|
@@ -32,6 +35,11 @@ declare class SimulatedKaspaChain {
|
|
|
32
35
|
getBalance(address: string): bigint;
|
|
33
36
|
getUtxos(address: string): readonly SimulatedUtxo[];
|
|
34
37
|
fund(address: string, amountSompi: bigint): SimulatedUtxo;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a snapshot of the current chain state.
|
|
40
|
+
* Snapshot IDs use Date.now() and are intended for session-based
|
|
41
|
+
* debugging and restore points, not for canonical identity.
|
|
42
|
+
*/
|
|
35
43
|
snapshot(): Snapshot;
|
|
36
44
|
restore(snapshot: Snapshot): void;
|
|
37
45
|
}
|
|
@@ -68,6 +76,13 @@ interface LocalnetState extends HardkasArtifactBase {
|
|
|
68
76
|
utxos: LocalnetUtxo[];
|
|
69
77
|
snapshots?: Snapshot$1[];
|
|
70
78
|
dag?: SimulatedDag;
|
|
79
|
+
forkSource?: {
|
|
80
|
+
network: string;
|
|
81
|
+
rpcUrl: string;
|
|
82
|
+
daaScore: string;
|
|
83
|
+
forkedAt: string;
|
|
84
|
+
addresses: string[];
|
|
85
|
+
};
|
|
71
86
|
}
|
|
72
87
|
interface SimulatedBlock {
|
|
73
88
|
id: string;
|
|
@@ -76,6 +91,12 @@ interface SimulatedBlock {
|
|
|
76
91
|
daaScore: string;
|
|
77
92
|
acceptedTxIds: string[];
|
|
78
93
|
isGenesis?: boolean;
|
|
94
|
+
/** GHOSTDAG blue_work for this block (computed by ApproxGhostdagEngine). */
|
|
95
|
+
blueWork?: string;
|
|
96
|
+
/** True if GHOSTDAG colored this block blue, false if red. */
|
|
97
|
+
isBlue?: boolean;
|
|
98
|
+
/** Full GHOSTDAG data if computed. */
|
|
99
|
+
ghostdagData?: _hardkas_simulator.GhostdagData;
|
|
79
100
|
}
|
|
80
101
|
interface SimulatedDag {
|
|
81
102
|
blocks: Record<string, SimulatedBlock>;
|
|
@@ -88,6 +109,10 @@ interface SimulatedDag {
|
|
|
88
109
|
winnerTxId: string;
|
|
89
110
|
loserTxIds: string[];
|
|
90
111
|
}>;
|
|
112
|
+
/** Internal GHOSTDAG store for this DAG session. */
|
|
113
|
+
ghostdagStore?: _hardkas_simulator.GhostdagStore;
|
|
114
|
+
/** Internal GHOSTDAG engine for this DAG session. */
|
|
115
|
+
ghostdagEngine?: _hardkas_simulator.ApproxGhostdagEngine;
|
|
91
116
|
}
|
|
92
117
|
interface StateTransition {
|
|
93
118
|
preStateHash: string;
|
|
@@ -97,8 +122,8 @@ interface StateTransition {
|
|
|
97
122
|
interface SimulationResult {
|
|
98
123
|
ok: boolean;
|
|
99
124
|
state: LocalnetState;
|
|
100
|
-
receipt:
|
|
101
|
-
planArtifact?:
|
|
125
|
+
receipt: _hardkas_artifacts.TxReceipt;
|
|
126
|
+
planArtifact?: _hardkas_artifacts.TxPlan;
|
|
102
127
|
errors: string[];
|
|
103
128
|
}
|
|
104
129
|
interface ReplayInvariantResult {
|
|
@@ -106,9 +131,25 @@ interface ReplayInvariantResult {
|
|
|
106
131
|
mismatches: string[];
|
|
107
132
|
}
|
|
108
133
|
interface ReplayVerificationReport {
|
|
134
|
+
schema: "hardkas.replayReport.v1";
|
|
135
|
+
txId: string;
|
|
109
136
|
planOk: boolean;
|
|
110
137
|
receiptOk: boolean;
|
|
111
138
|
invariantsOk: boolean;
|
|
139
|
+
/** Honest check status to avoid overclaiming. */
|
|
140
|
+
checks: {
|
|
141
|
+
/** Whether the internal HardKAS workflow (Plan -> Receipt) was reproduced. */
|
|
142
|
+
workflowDeterministic: "reproduced" | "diverged" | "skipped";
|
|
143
|
+
/** HardKAS DOES NOT currently validate full Kaspa consensus (GHOSTDAG, etc). */
|
|
144
|
+
consensusValidation: "unimplemented" | "partial" | "skipped";
|
|
145
|
+
/** HardKAS DOES NOT currently validate L2 bridge logic. */
|
|
146
|
+
l2BridgeCorrectness: "unimplemented" | "partial" | "skipped";
|
|
147
|
+
};
|
|
148
|
+
divergences: Array<{
|
|
149
|
+
path: string;
|
|
150
|
+
expected: any;
|
|
151
|
+
actual: any;
|
|
152
|
+
}>;
|
|
112
153
|
errors: string[];
|
|
113
154
|
}
|
|
114
155
|
interface SnapshotVerificationResult {
|
|
@@ -210,6 +251,7 @@ options?: {
|
|
|
210
251
|
interface StoredSimulatedTxReceipt extends HardkasArtifactBase {
|
|
211
252
|
schema: typeof ARTIFACT_SCHEMAS.TX_RECEIPT;
|
|
212
253
|
version: "1.0.0-alpha";
|
|
254
|
+
hashVersion?: number | string;
|
|
213
255
|
txId: string;
|
|
214
256
|
status: "confirmed" | "failed";
|
|
215
257
|
mode: ExecutionMode;
|
|
@@ -259,6 +301,8 @@ type StoredTraceEvent = {
|
|
|
259
301
|
};
|
|
260
302
|
interface StoredSimulatedTxTrace extends HardkasArtifactBase {
|
|
261
303
|
readonly schema: typeof ARTIFACT_SCHEMAS.TX_TRACE;
|
|
304
|
+
readonly version: string;
|
|
305
|
+
readonly hashVersion?: number | string;
|
|
262
306
|
readonly txId: string;
|
|
263
307
|
readonly mode: ExecutionMode;
|
|
264
308
|
readonly networkId: NetworkId;
|
|
@@ -291,6 +335,8 @@ interface SimulatedReplaySummary {
|
|
|
291
335
|
}
|
|
292
336
|
/**
|
|
293
337
|
* Verifies that a transaction replay matches the original artifacts.
|
|
338
|
+
* Implements an honest replay model that differentiates between
|
|
339
|
+
* reproduced results and unimplemented consensus/bridge features.
|
|
294
340
|
*/
|
|
295
341
|
declare function verifyReplay(state: LocalnetState, originalPlan: TxPlan, originalReceipt: TxReceipt): ReplayVerificationReport;
|
|
296
342
|
/**
|
|
@@ -303,7 +349,9 @@ declare function getSimulatedReplaySummary(txId: string, options?: {
|
|
|
303
349
|
/**
|
|
304
350
|
* Creates a fresh simulated DAG with a genesis block.
|
|
305
351
|
*/
|
|
306
|
-
declare function createSimulatedDag(
|
|
352
|
+
declare function createSimulatedDag(options?: {
|
|
353
|
+
k?: number;
|
|
354
|
+
}): SimulatedDag;
|
|
307
355
|
/**
|
|
308
356
|
* Adds a block to the DAG.
|
|
309
357
|
*/
|
|
@@ -330,5 +378,30 @@ declare function resolveConflictsDeterministically(txs: Array<{
|
|
|
330
378
|
displaced: string[];
|
|
331
379
|
conflicts: any[];
|
|
332
380
|
};
|
|
381
|
+
/**
|
|
382
|
+
* Get the blue/red coloring of all blocks in the DAG.
|
|
383
|
+
* Returns a map of blockId → { isBlue, blueWork, blueScore }.
|
|
384
|
+
*/
|
|
385
|
+
declare function getDagColoring(dag: SimulatedDag): Map<string, {
|
|
386
|
+
isBlue: boolean;
|
|
387
|
+
blueWork: string;
|
|
388
|
+
blueScore: number;
|
|
389
|
+
}>;
|
|
390
|
+
/**
|
|
391
|
+
* Get the selected parent chain from sink to genesis.
|
|
392
|
+
*/
|
|
393
|
+
declare function getSelectedChain(dag: SimulatedDag): string[];
|
|
394
|
+
/**
|
|
395
|
+
* Helper to find the best tip block according to GHOSTDAG.
|
|
396
|
+
*/
|
|
397
|
+
declare function findBestTip(dag: SimulatedDag): string;
|
|
398
|
+
|
|
399
|
+
interface ForkOptions {
|
|
400
|
+
network: string;
|
|
401
|
+
rpcUrl: string;
|
|
402
|
+
addresses: string[];
|
|
403
|
+
atDaaScore?: string;
|
|
404
|
+
}
|
|
405
|
+
declare function forkFromNetwork(rpc: KaspaRpcClient, opts: ForkOptions): Promise<LocalnetState>;
|
|
333
406
|
|
|
334
|
-
export { type CreateInitialStateOptions, DUST_LIMIT_SOMPI, type FundAddressInput, type HardkasAccount, type HardkasDevnet, type LocalnetAccount, type LocalnetState, type LocalnetUtxo, type ReplayInvariantResult, type ReplayVerificationReport, type SimulatedBlock, type SimulatedDag, SimulatedKaspaChain, type SimulatedPaymentInput, type SimulatedReplaySummary, type SimulatedUtxo, type SimulationResult, type Snapshot, type SnapshotRestoreResult, type SnapshotVerificationResult, type StateTransition, type StoredSimulatedTxReceipt, type StoredSimulatedTxTrace, type StoredTraceEvent, addSimulatedBlock, applySimulatedPayment, applySimulatedPlan, calculateAccountsHash, calculateStateHash, calculateUtxoSetHash, createDeterministicAccounts, createInitialLocalnetState, createLocalnetSnapshot, createSimulatedDag, fundAddress, getAccountBalanceSompi, getAddressBalanceSompi, getDefaultLocalnetDir, getDefaultLocalnetStatePath, getDefaultReceiptsDir, getDefaultTracesDir, getReceiptPath, getSimulatedReplaySummary, getSpendableUtxos, getTracePath, listSimulatedReceipts, listSimulatedTraces, loadLocalnetState, loadOrCreateLocalnetState, loadSimulatedReceipt, loadSimulatedTrace, moveSink, resetLocalnetState, resolveAccountAddress, resolveAccountAddressFromState, resolveConflictsDeterministically, restoreLocalnetSnapshot, saveLocalnetState, saveSimulatedReceipt, saveSimulatedTrace, startSimulatedDevnet, verifyReplay, verifySnapshot };
|
|
407
|
+
export { type CreateInitialStateOptions, DUST_LIMIT_SOMPI, type ForkOptions, type FundAddressInput, type HardkasAccount, type HardkasDevnet, type LocalnetAccount, type LocalnetState, type LocalnetUtxo, type ReplayInvariantResult, type ReplayVerificationReport, type SimulatedBlock, type SimulatedDag, SimulatedKaspaChain, type SimulatedPaymentInput, type SimulatedReplaySummary, type SimulatedUtxo, type SimulationResult, type Snapshot, type SnapshotRestoreResult, type SnapshotVerificationResult, type StateTransition, type StoredSimulatedTxReceipt, type StoredSimulatedTxTrace, type StoredTraceEvent, addSimulatedBlock, applySimulatedPayment, applySimulatedPlan, calculateAccountsHash, calculateStateHash, calculateUtxoSetHash, createDeterministicAccounts, createInitialLocalnetState, createLocalnetSnapshot, createSimulatedDag, findBestTip, forkFromNetwork, fundAddress, getAccountBalanceSompi, getAddressBalanceSompi, getDagColoring, getDefaultLocalnetDir, getDefaultLocalnetStatePath, getDefaultReceiptsDir, getDefaultTracesDir, getReceiptPath, getSelectedChain, getSimulatedReplaySummary, getSpendableUtxos, getTracePath, listSimulatedReceipts, listSimulatedTraces, loadLocalnetState, loadOrCreateLocalnetState, loadSimulatedReceipt, loadSimulatedTrace, moveSink, resetLocalnetState, resolveAccountAddress, resolveAccountAddressFromState, resolveConflictsDeterministically, restoreLocalnetSnapshot, saveLocalnetState, saveSimulatedReceipt, saveSimulatedTrace, startSimulatedDevnet, verifyReplay, verifySnapshot };
|
package/dist/index.js
CHANGED
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
listSimulatedReceipts,
|
|
5
5
|
loadSimulatedReceipt,
|
|
6
6
|
saveSimulatedReceipt
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GVBX3TPM.js";
|
|
8
8
|
import {
|
|
9
9
|
getDefaultTracesDir,
|
|
10
10
|
getTracePath,
|
|
11
11
|
listSimulatedTraces,
|
|
12
12
|
loadSimulatedTrace,
|
|
13
13
|
saveSimulatedTrace
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-DPHHOGGK.js";
|
|
15
15
|
|
|
16
16
|
// src/accounts.ts
|
|
17
17
|
function createDeterministicAccounts(input) {
|
|
@@ -22,21 +22,21 @@ function createDeterministicAccounts(input) {
|
|
|
22
22
|
const name = names[index] ?? `account${index}`;
|
|
23
23
|
return {
|
|
24
24
|
name,
|
|
25
|
-
address: `
|
|
25
|
+
address: `kaspasim:${name}`,
|
|
26
26
|
balanceSompi: initialBalanceSompi
|
|
27
27
|
};
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
function resolveAccountAddress(input) {
|
|
31
|
-
if (input.startsWith("kaspa:")) {
|
|
31
|
+
if (input.startsWith("kaspa:") || input.startsWith("kaspasim:")) {
|
|
32
32
|
return input;
|
|
33
33
|
}
|
|
34
34
|
const aliases = {
|
|
35
|
-
alice: "
|
|
36
|
-
bob: "
|
|
37
|
-
carol: "
|
|
38
|
-
dave: "
|
|
39
|
-
erin: "
|
|
35
|
+
alice: "kaspasim:alice",
|
|
36
|
+
bob: "kaspasim:bob",
|
|
37
|
+
carol: "kaspasim:carol",
|
|
38
|
+
dave: "kaspasim:dave",
|
|
39
|
+
erin: "kaspasim:erin"
|
|
40
40
|
};
|
|
41
41
|
const resolved = aliases[input.toLowerCase()];
|
|
42
42
|
if (!resolved) {
|
|
@@ -77,7 +77,7 @@ var SimulatedKaspaChain = class {
|
|
|
77
77
|
throw new Error("Faucet amount must be positive.");
|
|
78
78
|
}
|
|
79
79
|
const utxo = {
|
|
80
|
-
id: `faucet:${
|
|
80
|
+
id: `faucet:${address.slice(-8)}:${this.daaScore}:${this.utxos.length}`,
|
|
81
81
|
address,
|
|
82
82
|
amountSompi,
|
|
83
83
|
spent: false
|
|
@@ -86,6 +86,11 @@ var SimulatedKaspaChain = class {
|
|
|
86
86
|
this.mineBlock();
|
|
87
87
|
return utxo;
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Creates a snapshot of the current chain state.
|
|
91
|
+
* Snapshot IDs use Date.now() and are intended for session-based
|
|
92
|
+
* debugging and restore points, not for canonical identity.
|
|
93
|
+
*/
|
|
89
94
|
snapshot() {
|
|
90
95
|
return {
|
|
91
96
|
id: `snapshot:${Date.now().toString(36)}`,
|
|
@@ -161,6 +166,7 @@ function resolveAccountAddressFromState(state, nameOrAddress) {
|
|
|
161
166
|
// src/store.ts
|
|
162
167
|
import fs from "fs/promises";
|
|
163
168
|
import path from "path";
|
|
169
|
+
import { writeFileAtomic } from "@hardkas/core";
|
|
164
170
|
function getDefaultLocalnetDir(cwd = process.cwd()) {
|
|
165
171
|
return path.join(cwd, ".hardkas");
|
|
166
172
|
}
|
|
@@ -171,7 +177,7 @@ async function saveLocalnetState(state, filePath) {
|
|
|
171
177
|
const targetPath = filePath ?? getDefaultLocalnetStatePath();
|
|
172
178
|
const dir = path.dirname(targetPath);
|
|
173
179
|
await fs.mkdir(dir, { recursive: true });
|
|
174
|
-
await
|
|
180
|
+
await writeFileAtomic(targetPath, JSON.stringify(state, null, 2), { encoding: "utf-8" });
|
|
175
181
|
}
|
|
176
182
|
async function loadLocalnetState(filePath) {
|
|
177
183
|
const targetPath = filePath ?? getDefaultLocalnetStatePath();
|
|
@@ -211,7 +217,7 @@ function fundAddress(state, input) {
|
|
|
211
217
|
}
|
|
212
218
|
const nextDaaScore = (BigInt(state.daaScore) + 1n).toString();
|
|
213
219
|
const newUtxo = {
|
|
214
|
-
id: `faucet:${
|
|
220
|
+
id: `faucet:${input.address.slice(-8)}:${nextDaaScore}:0`,
|
|
215
221
|
address: input.address,
|
|
216
222
|
amountSompi: input.amountSompi.toString(),
|
|
217
223
|
spent: false,
|
|
@@ -339,6 +345,24 @@ import {
|
|
|
339
345
|
createSimulatedTxReceipt,
|
|
340
346
|
calculateContentHash as calculateContentHash2
|
|
341
347
|
} from "@hardkas/artifacts";
|
|
348
|
+
function buildDagContextFromState(state) {
|
|
349
|
+
if (state.dag) {
|
|
350
|
+
return {
|
|
351
|
+
mode: "dag-light",
|
|
352
|
+
sink: state.dag.sink,
|
|
353
|
+
acceptedTxIds: state.dag.acceptedTxIds,
|
|
354
|
+
displacedTxIds: state.dag.displacedTxIds,
|
|
355
|
+
conflictSet: state.dag.conflictSet
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return { mode: "linear", sink: "linear-pseudo-sink" };
|
|
359
|
+
}
|
|
360
|
+
function generateDeterministicFailedTxId(preStateHash, errorMessage, daaScore) {
|
|
361
|
+
const normalized = errorMessage.replace(/[^a-zA-Z0-9_:. -]/g, "");
|
|
362
|
+
const input = `failed:${preStateHash}:${normalized}:${daaScore}`;
|
|
363
|
+
const hash = createHash("sha256").update(input).digest("hex").slice(0, 32);
|
|
364
|
+
return `simtx_failed_${hash}`;
|
|
365
|
+
}
|
|
342
366
|
function generateDeterministicTxId(planArtifact, preStateHash, daaScore) {
|
|
343
367
|
const planHash = planArtifact.contentHash || calculateContentHash2(planArtifact);
|
|
344
368
|
const input = `${planHash}:${preStateHash}:${daaScore}`;
|
|
@@ -441,21 +465,11 @@ function applySimulatedPayment(state, input) {
|
|
|
441
465
|
const receipt = createSimulatedTxReceipt(planArtifact, txId, {
|
|
442
466
|
spentUtxoIds,
|
|
443
467
|
createdUtxoIds,
|
|
444
|
-
daaScore: nextDaaScore
|
|
468
|
+
daaScore: nextDaaScore,
|
|
469
|
+
preStateHash,
|
|
470
|
+
postStateHash,
|
|
471
|
+
dagContext: buildDagContextFromState(state)
|
|
445
472
|
});
|
|
446
|
-
receipt.preStateHash = preStateHash;
|
|
447
|
-
receipt.postStateHash = postStateHash;
|
|
448
|
-
if (state.dag) {
|
|
449
|
-
receipt.dagContext = {
|
|
450
|
-
mode: "dag-light",
|
|
451
|
-
sink: state.dag.sink,
|
|
452
|
-
acceptedTxIds: state.dag.acceptedTxIds,
|
|
453
|
-
displacedTxIds: state.dag.displacedTxIds,
|
|
454
|
-
conflictSet: state.dag.conflictSet
|
|
455
|
-
};
|
|
456
|
-
} else {
|
|
457
|
-
receipt.dagContext = { mode: "linear", sink: "linear-pseudo-sink" };
|
|
458
|
-
}
|
|
459
473
|
return {
|
|
460
474
|
ok: true,
|
|
461
475
|
state: nextState,
|
|
@@ -464,16 +478,18 @@ function applySimulatedPayment(state, input) {
|
|
|
464
478
|
errors
|
|
465
479
|
};
|
|
466
480
|
} catch (error) {
|
|
467
|
-
const
|
|
481
|
+
const daaScore = state.daaScore || "0";
|
|
482
|
+
const txId = generateDeterministicFailedTxId(preStateHash, error.message, daaScore);
|
|
468
483
|
const receipt = {
|
|
469
484
|
schema: "hardkas.txReceipt",
|
|
470
485
|
status: "failed",
|
|
471
486
|
mode: "simulated",
|
|
472
487
|
txId,
|
|
473
|
-
createdAt:
|
|
488
|
+
createdAt: "1970-01-01T00:00:00.000Z",
|
|
474
489
|
errors: [error.message],
|
|
475
490
|
preStateHash,
|
|
476
|
-
postStateHash: preStateHash
|
|
491
|
+
postStateHash: preStateHash,
|
|
492
|
+
dagContext: buildDagContextFromState(state)
|
|
477
493
|
};
|
|
478
494
|
return {
|
|
479
495
|
ok: false,
|
|
@@ -530,32 +546,25 @@ function applySimulatedPlan(state, planArtifact, options) {
|
|
|
530
546
|
const receipt = createSimulatedTxReceipt(planArtifact, txId, {
|
|
531
547
|
spentUtxoIds,
|
|
532
548
|
createdUtxoIds,
|
|
533
|
-
daaScore: nextDaaScore
|
|
549
|
+
daaScore: nextDaaScore,
|
|
550
|
+
preStateHash,
|
|
551
|
+
postStateHash,
|
|
552
|
+
dagContext: buildDagContextFromState(state)
|
|
534
553
|
});
|
|
535
|
-
receipt.preStateHash = preStateHash;
|
|
536
|
-
receipt.postStateHash = postStateHash;
|
|
537
|
-
if (state.dag) {
|
|
538
|
-
receipt.dagContext = {
|
|
539
|
-
mode: "dag-light",
|
|
540
|
-
sink: state.dag.sink,
|
|
541
|
-
acceptedTxIds: state.dag.acceptedTxIds,
|
|
542
|
-
displacedTxIds: state.dag.displacedTxIds,
|
|
543
|
-
conflictSet: state.dag.conflictSet
|
|
544
|
-
};
|
|
545
|
-
} else {
|
|
546
|
-
receipt.dagContext = { mode: "linear", sink: "linear-pseudo-sink" };
|
|
547
|
-
}
|
|
548
554
|
return { ok: true, state: nextState, receipt, planArtifact, errors };
|
|
549
555
|
} catch (error) {
|
|
556
|
+
const daaScore = state.daaScore || "0";
|
|
557
|
+
const txId = generateDeterministicFailedTxId(preStateHash, error.message, daaScore);
|
|
550
558
|
const receipt = {
|
|
551
559
|
schema: "hardkas.txReceipt",
|
|
552
560
|
status: "failed",
|
|
553
561
|
mode: "simulated",
|
|
554
|
-
txId
|
|
555
|
-
createdAt:
|
|
562
|
+
txId,
|
|
563
|
+
createdAt: "1970-01-01T00:00:00.000Z",
|
|
556
564
|
errors: [error.message],
|
|
557
565
|
preStateHash,
|
|
558
|
-
postStateHash: preStateHash
|
|
566
|
+
postStateHash: preStateHash,
|
|
567
|
+
dagContext: buildDagContextFromState(state)
|
|
559
568
|
};
|
|
560
569
|
return { ok: false, state, receipt, errors: [error.message] };
|
|
561
570
|
}
|
|
@@ -563,61 +572,90 @@ function applySimulatedPlan(state, planArtifact, options) {
|
|
|
563
572
|
|
|
564
573
|
// src/replay.ts
|
|
565
574
|
import {
|
|
566
|
-
calculateContentHash as calculateContentHash3
|
|
575
|
+
calculateContentHash as calculateContentHash3,
|
|
576
|
+
diffArtifacts
|
|
567
577
|
} from "@hardkas/artifacts";
|
|
568
578
|
import { coreEvents } from "@hardkas/core";
|
|
569
579
|
function verifyReplay(state, originalPlan, originalReceipt) {
|
|
570
580
|
const errors = [];
|
|
581
|
+
const reportDivergences = [];
|
|
571
582
|
const currentPlanHash = calculateContentHash3(originalPlan);
|
|
583
|
+
let planOk = true;
|
|
572
584
|
if (originalPlan.contentHash && currentPlanHash !== originalPlan.contentHash) {
|
|
585
|
+
planOk = false;
|
|
573
586
|
const errorMsg = `TxPlan contentHash mismatch: expected ${originalPlan.contentHash}, got ${currentPlanHash}`;
|
|
574
587
|
errors.push(errorMsg);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
588
|
+
reportDivergences.push({ path: "plan.contentHash", expected: originalPlan.contentHash, actual: currentPlanHash });
|
|
589
|
+
}
|
|
590
|
+
const originalPreState = originalReceipt.preStateHash;
|
|
591
|
+
if (originalPreState) {
|
|
592
|
+
const currentStateHash = calculateStateHash(state);
|
|
593
|
+
if (currentStateHash !== originalPreState) {
|
|
594
|
+
const errorMsg = `preStateHash mismatch: expected ${originalPreState}, got ${currentStateHash}`;
|
|
595
|
+
errors.push(errorMsg);
|
|
596
|
+
reportDivergences.push({
|
|
597
|
+
path: "preStateHash",
|
|
598
|
+
expected: originalPreState,
|
|
599
|
+
actual: currentStateHash
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
reportDivergences.push({
|
|
604
|
+
path: "preStateHash",
|
|
605
|
+
expected: "present",
|
|
606
|
+
actual: "missing",
|
|
607
|
+
severity: "warning"
|
|
581
608
|
});
|
|
582
609
|
}
|
|
583
610
|
const result = applySimulatedPlan(state, originalPlan, { txId: originalReceipt.txId });
|
|
584
611
|
const replayReceipt = result.receipt;
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
];
|
|
593
|
-
for (const check of checks) {
|
|
594
|
-
if (check.expected !== check.actual) {
|
|
595
|
-
errors.push(`${check.field} mismatch: expected ${check.expected}, got ${check.actual}`);
|
|
596
|
-
coreEvents.normalizeAndEmit({
|
|
597
|
-
kind: "replay.divergence",
|
|
598
|
-
txId: originalReceipt.txId,
|
|
599
|
-
field: check.field,
|
|
600
|
-
expected: check.expected,
|
|
601
|
-
actual: check.actual
|
|
612
|
+
const diff = diffArtifacts(originalReceipt, replayReceipt);
|
|
613
|
+
if (!diff.identical) {
|
|
614
|
+
for (const entry of diff.entries) {
|
|
615
|
+
reportDivergences.push({
|
|
616
|
+
path: `receipt.${entry.path}`,
|
|
617
|
+
expected: entry.left,
|
|
618
|
+
actual: entry.right
|
|
602
619
|
});
|
|
620
|
+
errors.push(`Receipt divergence at ${entry.path}: expected ${JSON.stringify(entry.left)}, got ${JSON.stringify(entry.right)}`);
|
|
603
621
|
}
|
|
604
622
|
}
|
|
605
|
-
|
|
623
|
+
for (const div of reportDivergences) {
|
|
624
|
+
coreEvents.normalizeAndEmit({
|
|
625
|
+
kind: "replay.divergence",
|
|
626
|
+
txId: originalReceipt.txId,
|
|
627
|
+
field: div.path,
|
|
628
|
+
expected: String(div.expected),
|
|
629
|
+
actual: String(div.actual)
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
const invariantsOk = errors.length === 0;
|
|
633
|
+
if (invariantsOk) {
|
|
606
634
|
coreEvents.normalizeAndEmit({
|
|
607
635
|
kind: "replay.verified",
|
|
608
636
|
txId: originalReceipt.txId
|
|
609
637
|
});
|
|
610
638
|
}
|
|
611
639
|
return {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
640
|
+
schema: "hardkas.replayReport.v1",
|
|
641
|
+
txId: originalReceipt.txId,
|
|
642
|
+
planOk,
|
|
643
|
+
receiptOk: !diff.entries.some((e) => !e.path.startsWith("plan")),
|
|
644
|
+
invariantsOk,
|
|
645
|
+
checks: {
|
|
646
|
+
workflowDeterministic: invariantsOk ? "reproduced" : "diverged",
|
|
647
|
+
consensusValidation: "unimplemented",
|
|
648
|
+
// Explicit trust boundary
|
|
649
|
+
l2BridgeCorrectness: "unimplemented"
|
|
650
|
+
// Explicit trust boundary
|
|
651
|
+
},
|
|
652
|
+
divergences: reportDivergences,
|
|
615
653
|
errors
|
|
616
654
|
};
|
|
617
655
|
}
|
|
618
656
|
async function getSimulatedReplaySummary(txId, options = {}) {
|
|
619
|
-
const { loadSimulatedReceipt: loadSimulatedReceipt2 } = await import("./receipts-
|
|
620
|
-
const { loadSimulatedTrace: loadSimulatedTrace2 } = await import("./traces-
|
|
657
|
+
const { loadSimulatedReceipt: loadSimulatedReceipt2 } = await import("./receipts-JSTNZMDD.js");
|
|
658
|
+
const { loadSimulatedTrace: loadSimulatedTrace2 } = await import("./traces-HDVV77MQ.js");
|
|
621
659
|
const receipt = await loadSimulatedReceipt2(txId, options);
|
|
622
660
|
const trace = await loadSimulatedTrace2(txId, options);
|
|
623
661
|
if (!receipt || !trace) {
|
|
@@ -638,7 +676,51 @@ async function getSimulatedReplaySummary(txId, options = {}) {
|
|
|
638
676
|
}
|
|
639
677
|
|
|
640
678
|
// src/dag.ts
|
|
641
|
-
|
|
679
|
+
import {
|
|
680
|
+
ApproxGhostdagEngine,
|
|
681
|
+
GhostdagStore,
|
|
682
|
+
genesisGhostdagData,
|
|
683
|
+
findSelectedParent,
|
|
684
|
+
GENESIS_HASH as SIM_GENESIS_HASH
|
|
685
|
+
} from "@hardkas/simulator";
|
|
686
|
+
var dagIdMaps = /* @__PURE__ */ new WeakMap();
|
|
687
|
+
var dagReverseIdMaps = /* @__PURE__ */ new WeakMap();
|
|
688
|
+
function getIdMap(dag) {
|
|
689
|
+
let m = dagIdMaps.get(dag);
|
|
690
|
+
if (!m) {
|
|
691
|
+
m = /* @__PURE__ */ new Map();
|
|
692
|
+
m.set("genesis", SIM_GENESIS_HASH);
|
|
693
|
+
dagIdMaps.set(dag, m);
|
|
694
|
+
}
|
|
695
|
+
return m;
|
|
696
|
+
}
|
|
697
|
+
function getReverseIdMap(dag) {
|
|
698
|
+
let m = dagReverseIdMaps.get(dag);
|
|
699
|
+
if (!m) {
|
|
700
|
+
m = /* @__PURE__ */ new Map();
|
|
701
|
+
m.set(SIM_GENESIS_HASH, "genesis");
|
|
702
|
+
dagReverseIdMaps.set(dag, m);
|
|
703
|
+
}
|
|
704
|
+
return m;
|
|
705
|
+
}
|
|
706
|
+
function toGhostdagSimBlock(block, idMap) {
|
|
707
|
+
const hash = idMap.get(block.id) || block.id;
|
|
708
|
+
const parents = block.parents.map((p) => idMap.get(p) || p);
|
|
709
|
+
return {
|
|
710
|
+
header: {
|
|
711
|
+
hash,
|
|
712
|
+
parents,
|
|
713
|
+
timestampUs: 0,
|
|
714
|
+
minerId: 0,
|
|
715
|
+
bits: 1e3,
|
|
716
|
+
// Default difficulty for simulation
|
|
717
|
+
nonce: 0
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function createSimulatedDag(options) {
|
|
722
|
+
const k = options?.k ?? 18;
|
|
723
|
+
const engine = new ApproxGhostdagEngine(k);
|
|
642
724
|
const genesis = {
|
|
643
725
|
id: "genesis",
|
|
644
726
|
parents: [],
|
|
@@ -647,21 +729,70 @@ function createSimulatedDag() {
|
|
|
647
729
|
acceptedTxIds: [],
|
|
648
730
|
isGenesis: true
|
|
649
731
|
};
|
|
650
|
-
|
|
732
|
+
const gdStore = new GhostdagStore();
|
|
733
|
+
gdStore.insert(SIM_GENESIS_HASH, genesisGhostdagData(SIM_GENESIS_HASH));
|
|
734
|
+
const dag = {
|
|
651
735
|
blocks: { [genesis.id]: genesis },
|
|
652
736
|
sink: genesis.id,
|
|
653
737
|
selectedPathToSink: [genesis.id],
|
|
654
738
|
acceptedTxIds: [],
|
|
655
739
|
displacedTxIds: [],
|
|
656
|
-
conflictSet: []
|
|
740
|
+
conflictSet: [],
|
|
741
|
+
ghostdagStore: gdStore,
|
|
742
|
+
ghostdagEngine: engine
|
|
657
743
|
};
|
|
744
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
745
|
+
idMap.set("genesis", SIM_GENESIS_HASH);
|
|
746
|
+
dagIdMaps.set(dag, idMap);
|
|
747
|
+
const reverseIdMap = /* @__PURE__ */ new Map();
|
|
748
|
+
reverseIdMap.set(SIM_GENESIS_HASH, "genesis");
|
|
749
|
+
dagReverseIdMaps.set(dag, reverseIdMap);
|
|
750
|
+
return dag;
|
|
658
751
|
}
|
|
659
752
|
function addSimulatedBlock(dag, block) {
|
|
660
|
-
const
|
|
661
|
-
|
|
753
|
+
const idMap = getIdMap(dag);
|
|
754
|
+
const reverseIdMap = getReverseIdMap(dag);
|
|
755
|
+
const gdBlock = toGhostdagSimBlock(block, idMap);
|
|
756
|
+
const allGdBlocks = /* @__PURE__ */ new Map();
|
|
757
|
+
for (const b of Object.values(dag.blocks)) {
|
|
758
|
+
allGdBlocks.set(idMap.get(b.id) || b.id, toGhostdagSimBlock(b, idMap));
|
|
759
|
+
}
|
|
760
|
+
allGdBlocks.set(gdBlock.header.hash, gdBlock);
|
|
761
|
+
const gdStore = dag.ghostdagStore || new GhostdagStore();
|
|
762
|
+
const dagEngine = dag.ghostdagEngine || new ApproxGhostdagEngine();
|
|
763
|
+
const gdData = dagEngine.computeGhostdag(gdBlock, allGdBlocks, gdStore);
|
|
764
|
+
gdStore.insert(gdBlock.header.hash, gdData);
|
|
765
|
+
const updatedBlock = {
|
|
766
|
+
...block,
|
|
767
|
+
ghostdagData: gdData,
|
|
768
|
+
blueWork: gdData.blueWork.toString(),
|
|
769
|
+
isBlue: true
|
|
770
|
+
// The block itself is the new tip on its own path
|
|
771
|
+
};
|
|
772
|
+
if (!idMap.has(block.id)) {
|
|
773
|
+
idMap.set(block.id, block.id);
|
|
774
|
+
reverseIdMap.set(block.id, block.id);
|
|
775
|
+
}
|
|
776
|
+
const newBlocks = { ...dag.blocks, [updatedBlock.id]: updatedBlock };
|
|
777
|
+
for (const blueHash of gdData.mergesetBlues) {
|
|
778
|
+
const id = reverseIdMap.get(blueHash) || blueHash;
|
|
779
|
+
if (newBlocks[id]) {
|
|
780
|
+
newBlocks[id] = { ...newBlocks[id], isBlue: true };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
for (const redHash of gdData.mergesetReds) {
|
|
784
|
+
const id = reverseIdMap.get(redHash) || redHash;
|
|
785
|
+
if (newBlocks[id]) {
|
|
786
|
+
newBlocks[id] = { ...newBlocks[id], isBlue: false };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const newDag = {
|
|
662
790
|
...dag,
|
|
663
791
|
blocks: newBlocks
|
|
664
792
|
};
|
|
793
|
+
dagIdMaps.set(newDag, idMap);
|
|
794
|
+
dagReverseIdMaps.set(newDag, reverseIdMap);
|
|
795
|
+
return newDag;
|
|
665
796
|
}
|
|
666
797
|
function moveSink(dag, newSinkId, txProvider) {
|
|
667
798
|
if (!dag.blocks[newSinkId]) {
|
|
@@ -670,6 +801,12 @@ function moveSink(dag, newSinkId, txProvider) {
|
|
|
670
801
|
const selectedPath = calculateSelectedPath(dag, newSinkId);
|
|
671
802
|
const reachableBlocks = identifyReachableBlocks(dag, newSinkId);
|
|
672
803
|
const sortedBlocks = reachableBlocks.sort((a, b) => {
|
|
804
|
+
if (a.ghostdagData && b.ghostdagData) {
|
|
805
|
+
const workA = BigInt(a.blueWork || "0");
|
|
806
|
+
const workB = BigInt(b.blueWork || "0");
|
|
807
|
+
if (workA !== workB) return workA < workB ? -1 : 1;
|
|
808
|
+
if (a.isBlue !== b.isBlue) return a.isBlue ? -1 : 1;
|
|
809
|
+
}
|
|
673
810
|
const daaA = BigInt(a.daaScore);
|
|
674
811
|
const daaB = BigInt(b.daaScore);
|
|
675
812
|
if (daaA !== daaB) return daaA < daaB ? -1 : 1;
|
|
@@ -742,14 +879,21 @@ function moveSink(dag, newSinkId, txProvider) {
|
|
|
742
879
|
};
|
|
743
880
|
}
|
|
744
881
|
function calculateSelectedPath(dag, sinkId) {
|
|
882
|
+
const reverseIdMap = getReverseIdMap(dag);
|
|
745
883
|
const path2 = [];
|
|
746
884
|
let currentId = sinkId;
|
|
747
885
|
while (currentId) {
|
|
748
886
|
const current = dag.blocks[currentId];
|
|
749
887
|
if (!current) break;
|
|
750
888
|
path2.unshift(current);
|
|
751
|
-
if (current.
|
|
752
|
-
|
|
889
|
+
if (current.isGenesis) break;
|
|
890
|
+
if (current.ghostdagData) {
|
|
891
|
+
const spHash = current.ghostdagData.selectedParent;
|
|
892
|
+
currentId = reverseIdMap.get(spHash) || spHash;
|
|
893
|
+
} else {
|
|
894
|
+
if (current.parents.length === 0) break;
|
|
895
|
+
currentId = current.parents[0];
|
|
896
|
+
}
|
|
753
897
|
}
|
|
754
898
|
return path2;
|
|
755
899
|
}
|
|
@@ -780,6 +924,12 @@ function resolveConflictsDeterministically(txs, dag) {
|
|
|
780
924
|
const inPathA = dag.selectedPathToSink.includes(a.blockId);
|
|
781
925
|
const inPathB = dag.selectedPathToSink.includes(b.blockId);
|
|
782
926
|
if (inPathA !== inPathB) return inPathA ? -1 : 1;
|
|
927
|
+
if (blockA.ghostdagData && blockB.ghostdagData) {
|
|
928
|
+
if (blockA.isBlue !== blockB.isBlue) return blockA.isBlue ? -1 : 1;
|
|
929
|
+
const workA = BigInt(blockA.blueWork || "0");
|
|
930
|
+
const workB = BigInt(blockB.blueWork || "0");
|
|
931
|
+
if (workA !== workB) return workA < workB ? -1 : 1;
|
|
932
|
+
}
|
|
783
933
|
const daaA = BigInt(blockA.daaScore);
|
|
784
934
|
const daaB = BigInt(blockB.daaScore);
|
|
785
935
|
if (daaA !== daaB) return daaA < daaB ? -1 : 1;
|
|
@@ -816,6 +966,85 @@ function resolveConflictsDeterministically(txs, dag) {
|
|
|
816
966
|
}
|
|
817
967
|
return { accepted, displaced, conflicts };
|
|
818
968
|
}
|
|
969
|
+
function getDagColoring(dag) {
|
|
970
|
+
const coloring = /* @__PURE__ */ new Map();
|
|
971
|
+
for (const [id, block] of Object.entries(dag.blocks)) {
|
|
972
|
+
coloring.set(id, {
|
|
973
|
+
isBlue: block.isBlue || false,
|
|
974
|
+
blueWork: block.blueWork || "0",
|
|
975
|
+
blueScore: block.ghostdagData?.blueScore || Number(block.blueScore) || 0
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
return coloring;
|
|
979
|
+
}
|
|
980
|
+
function getSelectedChain(dag) {
|
|
981
|
+
return calculateSelectedPath(dag, dag.sink).map((b) => b.id);
|
|
982
|
+
}
|
|
983
|
+
function findBestTip(dag) {
|
|
984
|
+
const idMap = getIdMap(dag);
|
|
985
|
+
const allBlockIds = Object.keys(dag.blocks);
|
|
986
|
+
if (allBlockIds.length === 0) return "genesis";
|
|
987
|
+
const parentIds = /* @__PURE__ */ new Set();
|
|
988
|
+
for (const block of Object.values(dag.blocks)) {
|
|
989
|
+
for (const p of block.parents) {
|
|
990
|
+
parentIds.add(p);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
const tips = allBlockIds.filter((id) => !parentIds.has(id));
|
|
994
|
+
if (tips.length === 0) return dag.sink;
|
|
995
|
+
const tipData = tips.map((id) => ({
|
|
996
|
+
hash: idMap.get(id) || id,
|
|
997
|
+
blueWork: BigInt(dag.blocks[id].blueWork || "0")
|
|
998
|
+
}));
|
|
999
|
+
const bestTipHash = findSelectedParent(tipData);
|
|
1000
|
+
const bestTip = tips.find((id) => (idMap.get(id) || id) === bestTipHash);
|
|
1001
|
+
return bestTip || tips[0];
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/fork.ts
|
|
1005
|
+
import { ARTIFACT_SCHEMAS as ARTIFACT_SCHEMAS2, HARDKAS_VERSION as HARDKAS_VERSION4, ARTIFACT_VERSION as ARTIFACT_VERSION3 } from "@hardkas/artifacts";
|
|
1006
|
+
async function forkFromNetwork(rpc, opts) {
|
|
1007
|
+
const info = await rpc.getInfo();
|
|
1008
|
+
const networkId = info.networkId || opts.network;
|
|
1009
|
+
const currentDaaScore = info.virtualDaaScore?.toString() || "0";
|
|
1010
|
+
const targetDaaScore = opts.atDaaScore || currentDaaScore;
|
|
1011
|
+
const utxos = [];
|
|
1012
|
+
for (const address of opts.addresses) {
|
|
1013
|
+
const rpcUtxos = await rpc.getUtxosByAddress(address);
|
|
1014
|
+
for (const u of rpcUtxos) {
|
|
1015
|
+
utxos.push({
|
|
1016
|
+
id: `${u.outpoint.transactionId}:${u.outpoint.index}`,
|
|
1017
|
+
address: u.address,
|
|
1018
|
+
amountSompi: u.amountSompi.toString(),
|
|
1019
|
+
spent: false,
|
|
1020
|
+
createdAtDaaScore: u.blockDaaScore?.toString() || "0"
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
const state = {
|
|
1025
|
+
schema: ARTIFACT_SCHEMAS2.LOCALNET_STATE,
|
|
1026
|
+
hardkasVersion: HARDKAS_VERSION4,
|
|
1027
|
+
version: ARTIFACT_VERSION3,
|
|
1028
|
+
hashVersion: "sha256-canonical",
|
|
1029
|
+
mode: "simulated",
|
|
1030
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1031
|
+
networkId,
|
|
1032
|
+
daaScore: targetDaaScore,
|
|
1033
|
+
accounts: opts.addresses.map((addr, i) => ({
|
|
1034
|
+
name: `forked_${i}`,
|
|
1035
|
+
address: addr
|
|
1036
|
+
})),
|
|
1037
|
+
utxos,
|
|
1038
|
+
forkSource: {
|
|
1039
|
+
network: opts.network,
|
|
1040
|
+
rpcUrl: opts.rpcUrl,
|
|
1041
|
+
daaScore: targetDaaScore,
|
|
1042
|
+
forkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1043
|
+
addresses: opts.addresses
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
return state;
|
|
1047
|
+
}
|
|
819
1048
|
export {
|
|
820
1049
|
DUST_LIMIT_SOMPI,
|
|
821
1050
|
SimulatedKaspaChain,
|
|
@@ -829,14 +1058,18 @@ export {
|
|
|
829
1058
|
createInitialLocalnetState,
|
|
830
1059
|
createLocalnetSnapshot,
|
|
831
1060
|
createSimulatedDag,
|
|
1061
|
+
findBestTip,
|
|
1062
|
+
forkFromNetwork,
|
|
832
1063
|
fundAddress,
|
|
833
1064
|
getAccountBalanceSompi,
|
|
834
1065
|
getAddressBalanceSompi,
|
|
1066
|
+
getDagColoring,
|
|
835
1067
|
getDefaultLocalnetDir,
|
|
836
1068
|
getDefaultLocalnetStatePath,
|
|
837
1069
|
getDefaultReceiptsDir,
|
|
838
1070
|
getDefaultTracesDir,
|
|
839
1071
|
getReceiptPath,
|
|
1072
|
+
getSelectedChain,
|
|
840
1073
|
getSimulatedReplaySummary,
|
|
841
1074
|
getSpendableUtxos,
|
|
842
1075
|
getTracePath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/localnet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
".": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@hardkas/artifacts": "0.
|
|
12
|
-
"@hardkas/
|
|
13
|
-
"@hardkas/
|
|
14
|
-
"@hardkas/
|
|
11
|
+
"@hardkas/artifacts": "0.3.0-alpha",
|
|
12
|
+
"@hardkas/query": "0.3.0-alpha",
|
|
13
|
+
"@hardkas/tx-builder": "0.3.0-alpha",
|
|
14
|
+
"@hardkas/core": "0.3.0-alpha",
|
|
15
|
+
"@hardkas/kaspa-rpc": "0.3.0-alpha",
|
|
16
|
+
"@hardkas/simulator": "0.3.0-alpha"
|
|
15
17
|
},
|
|
16
18
|
"devDependencies": {
|
|
19
|
+
"fast-check": "^4.8.0",
|
|
17
20
|
"tsup": "^8.3.5",
|
|
18
21
|
"typescript": "^5.7.2",
|
|
19
22
|
"vitest": "^2.1.8"
|