@hardkas/simulator 0.8.20-alpha → 0.9.1-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/index.d.ts CHANGED
@@ -374,4 +374,164 @@ declare function profileAndCompare(opts: {
374
374
  comparison?: MassComparison;
375
375
  };
376
376
 
377
- export { ApproxGhostdagEngine, type BlockHash, type BlueWorkType, type CandidateColor, type CompactGhostdagData, DEFAULT_K, type DagMetrics, GENESIS_HASH, type GhostdagData, GhostdagStore, type MassBreakdown, type MassComparison, type MassSnapshot, type MassSnapshotStore, type ScenarioConfig, type ScenarioResult, type SimBlock, type SimBlockHeader, type SimulationResult, type SortableBlock, type TxLifecyclePhase, TxSimulator, type TxTraceEvent, blockBlueScore, blockBlueWork, blockHash, blockParents, compactFromFull, compareMassProfiles, compareSortableBlocks, computeDagMetrics, createTraceId, findSelectedParent, formatMassComparison, formatMassProfile, formatScenarioReport, genesisGhostdagData, headerWork, isDagAncestorOf, loadMassSnapshot, orderedMergesetWithoutSelectedParent, pastSet, profileAndCompare, profileMass, runAllScenarios, runDiamondDag, runForkResolution, runLinearChain, runWideDag, saveMassSnapshot, sortBlocks, unorderedMergesetWithoutSelectedParent };
377
+ declare const SILVER_SIMULATOR_FEE_SOMPI = 2000n;
378
+ declare const SILVER_SIMULATOR_CREATED_AT = "1970-01-01T00:00:00.000Z";
379
+ declare const SILVER_SIMULATOR_VERSION = "1.0.0-alpha";
380
+ type SilverSimulationStatus = "SIMULATED_ACCEPTED";
381
+ type SilverSimulationErrorCode = "SILVERSCRIPT_REDEEM_HASH_MISMATCH" | "SILVERSCRIPT_LOCKING_SCRIPT_MISMATCH" | "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY" | "SILVERSCRIPT_ARGS_HASH_MISMATCH" | "SILVERSCRIPT_UTXO_ALREADY_SPENT" | "SILVERSCRIPT_NETWORK_UNSUPPORTED" | "SILVERSCRIPT_AMOUNT_TOO_SMALL" | "SILVERSCRIPT_DEPLOY_RECEIPT_NOT_FOUND" | "SILVERSCRIPT_SCHEMA_INVALID" | "SILVERSCRIPT_INVALID_HEX" | "SILVERSCRIPT_EXPECTED_OUTPUTS_REQUIRED" | "SILVERSCRIPT_SIGNATURE_SCRIPT_MISMATCH";
382
+ declare class SilverSimulationError extends Error {
383
+ readonly code: SilverSimulationErrorCode;
384
+ constructor(code: SilverSimulationErrorCode, message: string);
385
+ }
386
+ interface SilverDeployPlanArtifactLike {
387
+ schema: "hardkas.silver.deployPlan";
388
+ hardkasVersion?: string;
389
+ version?: string;
390
+ hashVersion?: number | string;
391
+ networkId: string;
392
+ mode?: string;
393
+ createdAt?: string;
394
+ contentHash?: string;
395
+ compileArtifactHash: string;
396
+ compiledScriptHash: string;
397
+ redeemScriptHex: string;
398
+ redeemScriptHash: string;
399
+ lockingScriptHex: string;
400
+ scriptPublicKeyVersion: number;
401
+ amountSompi: string;
402
+ deployerAddress: string;
403
+ }
404
+ interface SilverArgArtifactLike {
405
+ type: "hex";
406
+ value: string;
407
+ }
408
+ interface SilverSpendPlanArtifactLike {
409
+ schema: "hardkas.silver.spendPlan";
410
+ hardkasVersion?: string;
411
+ version?: string;
412
+ hashVersion?: number | string;
413
+ networkId: string;
414
+ mode?: string;
415
+ createdAt?: string;
416
+ contentHash?: string;
417
+ deployArtifactHash: string;
418
+ compileArtifactHash: string;
419
+ redeemScriptHash: string;
420
+ lockingScriptHex: string;
421
+ contractUtxoRef: {
422
+ transactionId: string;
423
+ index: number;
424
+ };
425
+ args: SilverArgArtifactLike[];
426
+ argsHash: string;
427
+ signatureScriptHex: string;
428
+ expectedOutputs: SilverExpectedOutput[];
429
+ }
430
+ interface SilverExpectedOutput {
431
+ address: string;
432
+ amountSompi: string;
433
+ scriptHash?: string;
434
+ }
435
+ interface SilverSyntheticOutpoint {
436
+ transactionId: string;
437
+ index: number;
438
+ }
439
+ interface SilverDeploySimulationReceipt {
440
+ schema: "hardkas.silver.deploySimulation";
441
+ hardkasVersion: string;
442
+ version: "1.0.0-alpha";
443
+ hashVersion: number;
444
+ networkId: "simnet";
445
+ mode: "simulated";
446
+ createdAt: string;
447
+ deployPlanHash: string;
448
+ compileArtifactHash: string;
449
+ compiledScriptHash: string;
450
+ redeemScriptHex: string;
451
+ redeemScriptHash: string;
452
+ lockingScriptHex: string;
453
+ scriptPublicKeyVersion: 0;
454
+ simulatedDeployTxId: string;
455
+ syntheticOutpoint: SilverSyntheticOutpoint;
456
+ amountSompi: string;
457
+ feeSompi: string;
458
+ status: SilverSimulationStatus;
459
+ lineage: {
460
+ artifactId: string;
461
+ lineageId: string;
462
+ parentArtifactId: string;
463
+ rootArtifactId: string;
464
+ sequence: number;
465
+ };
466
+ contentHash: string;
467
+ artifactId: string;
468
+ }
469
+ interface SilverSpendSimulationReceipt {
470
+ schema: "hardkas.silver.spendSimulation";
471
+ hardkasVersion: string;
472
+ version: "1.0.0-alpha";
473
+ hashVersion: number;
474
+ networkId: "simnet";
475
+ mode: "simulated";
476
+ createdAt: string;
477
+ deploySimulationHash: string;
478
+ spendPlanHash: string;
479
+ redeemScriptHash: string;
480
+ lockingScriptHex: string;
481
+ signatureScriptHex: string;
482
+ simulatedSpendTxId: string;
483
+ spentOutpoint: SilverSyntheticOutpoint;
484
+ expectedOutputs: SilverExpectedOutput[];
485
+ feeSompi: string;
486
+ status: SilverSimulationStatus;
487
+ lineage: {
488
+ artifactId: string;
489
+ lineageId: string;
490
+ parentArtifactId: string;
491
+ rootArtifactId: string;
492
+ sequence: number;
493
+ };
494
+ contentHash: string;
495
+ artifactId: string;
496
+ }
497
+ interface SilverSimulatedUtxo {
498
+ outpoint: SilverSyntheticOutpoint;
499
+ deploySimulationHash: string;
500
+ deployPlanHash: string;
501
+ redeemScriptHex: string;
502
+ redeemScriptHash: string;
503
+ lockingScriptHex: string;
504
+ amountSompi: string;
505
+ networkId: "simnet";
506
+ spent: boolean;
507
+ spentByTxId?: string;
508
+ }
509
+ interface SilverSimulationState {
510
+ schema: "hardkas.silver.simulationState.v1";
511
+ version: "1.0.0-alpha";
512
+ networkId: "simnet";
513
+ mode: "simulated";
514
+ deployReceipts: Record<string, SilverDeploySimulationReceipt>;
515
+ utxos: Record<string, SilverSimulatedUtxo>;
516
+ spentOutpoints: string[];
517
+ }
518
+ interface SilverDeploySimulationResult {
519
+ receipt: SilverDeploySimulationReceipt;
520
+ state: SilverSimulationState;
521
+ }
522
+ interface SilverSpendSimulationResult {
523
+ receipt: SilverSpendSimulationReceipt;
524
+ state: SilverSimulationState;
525
+ }
526
+ interface SilverSimulationOptions {
527
+ hardkasVersion?: string;
528
+ createdAt?: string;
529
+ }
530
+ declare function createSilverSimulationState(): SilverSimulationState;
531
+ declare function simulateSilverDeploy(deployPlanArtifact: SilverDeployPlanArtifactLike, options?: SilverSimulationOptions): SilverDeploySimulationResult;
532
+ declare function simulateSilverSpend(spendPlanArtifact: SilverSpendPlanArtifactLike, simulatedState: SilverSimulationState, options?: SilverSimulationOptions): SilverSpendSimulationResult;
533
+ declare function calculateSilverArgsHash(args: readonly SilverArgArtifactLike[]): string;
534
+ declare function outpointKey(outpoint: SilverSyntheticOutpoint): string;
535
+ declare function parsePushOnlyScript(signatureScriptHex: string): string[];
536
+
537
+ export { ApproxGhostdagEngine, type BlockHash, type BlueWorkType, type CandidateColor, type CompactGhostdagData, DEFAULT_K, type DagMetrics, GENESIS_HASH, type GhostdagData, GhostdagStore, type MassBreakdown, type MassComparison, type MassSnapshot, type MassSnapshotStore, SILVER_SIMULATOR_CREATED_AT, SILVER_SIMULATOR_FEE_SOMPI, SILVER_SIMULATOR_VERSION, type ScenarioConfig, type ScenarioResult, type SilverArgArtifactLike, type SilverDeployPlanArtifactLike, type SilverDeploySimulationReceipt, type SilverDeploySimulationResult, type SilverExpectedOutput, type SilverSimulatedUtxo, SilverSimulationError, type SilverSimulationErrorCode, type SilverSimulationOptions, type SilverSimulationState, type SilverSimulationStatus, type SilverSpendPlanArtifactLike, type SilverSpendSimulationReceipt, type SilverSpendSimulationResult, type SimBlock, type SimBlockHeader, type SimulationResult, type SortableBlock, type TxLifecyclePhase, TxSimulator, type TxTraceEvent, blockBlueScore, blockBlueWork, blockHash, blockParents, calculateSilverArgsHash, compactFromFull, compareMassProfiles, compareSortableBlocks, computeDagMetrics, createSilverSimulationState, createTraceId, findSelectedParent, formatMassComparison, formatMassProfile, formatScenarioReport, genesisGhostdagData, headerWork, isDagAncestorOf, loadMassSnapshot, orderedMergesetWithoutSelectedParent, outpointKey, parsePushOnlyScript, pastSet, profileAndCompare, profileMass, runAllScenarios, runDiamondDag, runForkResolution, runLinearChain, runWideDag, saveMassSnapshot, simulateSilverDeploy, simulateSilverSpend, sortBlocks, unorderedMergesetWithoutSelectedParent };
package/dist/index.js CHANGED
@@ -624,6 +624,7 @@ function formatScenarioReport(results) {
624
624
 
625
625
  // src/mass-profile.ts
626
626
  import { estimateTransactionMass, estimateFeeFromMass } from "@hardkas/tx-builder";
627
+ import { formatSompiToKas } from "@hardkas/core";
627
628
  function formatBigInt(n) {
628
629
  return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
629
630
  }
@@ -678,7 +679,7 @@ function compareMassProfiles(current, previous) {
678
679
  };
679
680
  }
680
681
  function formatMassProfile(breakdown) {
681
- const kasAmount = Number(breakdown.estimatedFeeSompi) / 1e8;
682
+ const kasAmount = formatSompiToKas(breakdown.estimatedFeeSompi);
682
683
  return [
683
684
  "\u2550\u2550\u2550 Mass Profile \u2550\u2550\u2550",
684
685
  ` Inputs : ${breakdown.inputCount} inputs \u2192 ${formatBigInt(breakdown.inputMass)} mass`,
@@ -686,7 +687,7 @@ function formatMassProfile(breakdown) {
686
687
  ` Payload : ${breakdown.payloadBytes} bytes \u2192 ${formatBigInt(breakdown.payloadMass)} mass`,
687
688
  ` Total mass : ${formatBigInt(breakdown.totalMass)}`,
688
689
  ` Fee rate : ${formatBigInt(breakdown.feeRate)} sompi/mass`,
689
- ` Est. fee : ${formatBigInt(breakdown.estimatedFeeSompi)} sompi (${kasAmount.toFixed(8)} KAS)`
690
+ ` Est. fee : ${formatBigInt(breakdown.estimatedFeeSompi)} sompi (${kasAmount} KAS)`
690
691
  ].join("\n");
691
692
  }
692
693
  function formatMassComparison(comparison) {
@@ -757,20 +758,445 @@ function reviveSnapshot(snapshot) {
757
758
  };
758
759
  return snapshot;
759
760
  }
761
+
762
+ // src/silver-simulator.ts
763
+ import { createHash as createHash2 } from "crypto";
764
+ import { createKaspaP2shBlake2bLock } from "@hardkas/core";
765
+ var SILVER_SIMULATOR_FEE_SOMPI = 2000n;
766
+ var SILVER_SIMULATOR_CREATED_AT = "1970-01-01T00:00:00.000Z";
767
+ var SILVER_SIMULATOR_VERSION = "1.0.0-alpha";
768
+ var CURRENT_HASH_VERSION = 4;
769
+ var SilverSimulationError = class extends Error {
770
+ code;
771
+ constructor(code, message) {
772
+ super(message);
773
+ this.name = "SilverSimulationError";
774
+ this.code = code;
775
+ }
776
+ };
777
+ function createSilverSimulationState() {
778
+ return {
779
+ schema: "hardkas.silver.simulationState.v1",
780
+ version: SILVER_SIMULATOR_VERSION,
781
+ networkId: "simnet",
782
+ mode: "simulated",
783
+ deployReceipts: {},
784
+ utxos: {},
785
+ spentOutpoints: []
786
+ };
787
+ }
788
+ function simulateSilverDeploy(deployPlanArtifact, options = {}) {
789
+ if (deployPlanArtifact.schema !== "hardkas.silver.deployPlan") {
790
+ throw new SilverSimulationError(
791
+ "SILVERSCRIPT_SCHEMA_INVALID",
792
+ "Expected hardkas.silver.deployPlan."
793
+ );
794
+ }
795
+ assertSimnet(deployPlanArtifact.networkId);
796
+ assertHex(deployPlanArtifact.redeemScriptHex, "redeemScriptHex");
797
+ const amountSompi = parseSompi(deployPlanArtifact.amountSompi);
798
+ if (amountSompi <= SILVER_SIMULATOR_FEE_SOMPI) {
799
+ throw new SilverSimulationError(
800
+ "SILVERSCRIPT_AMOUNT_TOO_SMALL",
801
+ "Deploy amount must be greater than the simulator fee."
802
+ );
803
+ }
804
+ const lock = createKaspaP2shBlake2bLock(deployPlanArtifact.redeemScriptHex);
805
+ if (lock.redeemScriptHash !== deployPlanArtifact.redeemScriptHash) {
806
+ throw new SilverSimulationError(
807
+ "SILVERSCRIPT_REDEEM_HASH_MISMATCH",
808
+ "redeemScriptHash must equal blake2b32(raw redeem script bytes)."
809
+ );
810
+ }
811
+ if (lock.lockingScriptHex !== deployPlanArtifact.lockingScriptHex) {
812
+ throw new SilverSimulationError(
813
+ "SILVERSCRIPT_LOCKING_SCRIPT_MISMATCH",
814
+ "lockingScriptHex must equal aa20 + redeemScriptHash + 87."
815
+ );
816
+ }
817
+ if (deployPlanArtifact.scriptPublicKeyVersion !== 0) {
818
+ throw new SilverSimulationError(
819
+ "SILVERSCRIPT_LOCKING_SCRIPT_MISMATCH",
820
+ "scriptPublicKeyVersion must be 0 for this local simulator."
821
+ );
822
+ }
823
+ const deployPlanHash = artifactHash(deployPlanArtifact);
824
+ const simulatedDeployTxId = deterministicHex({
825
+ kind: "hardkas.silver.simulatedDeployTx",
826
+ deployPlanHash,
827
+ redeemScriptHash: deployPlanArtifact.redeemScriptHash,
828
+ amountSompi: deployPlanArtifact.amountSompi
829
+ });
830
+ const syntheticOutpoint = {
831
+ transactionId: simulatedDeployTxId,
832
+ index: 0
833
+ };
834
+ const draft = {
835
+ schema: "hardkas.silver.deploySimulation",
836
+ hardkasVersion: options.hardkasVersion ?? deployPlanArtifact.hardkasVersion ?? "0.9.1-alpha",
837
+ version: SILVER_SIMULATOR_VERSION,
838
+ hashVersion: CURRENT_HASH_VERSION,
839
+ networkId: "simnet",
840
+ mode: "simulated",
841
+ createdAt: options.createdAt ?? SILVER_SIMULATOR_CREATED_AT,
842
+ deployPlanHash,
843
+ compileArtifactHash: deployPlanArtifact.compileArtifactHash,
844
+ compiledScriptHash: deployPlanArtifact.compiledScriptHash,
845
+ redeemScriptHex: deployPlanArtifact.redeemScriptHex,
846
+ redeemScriptHash: deployPlanArtifact.redeemScriptHash,
847
+ lockingScriptHex: deployPlanArtifact.lockingScriptHex,
848
+ scriptPublicKeyVersion: 0,
849
+ simulatedDeployTxId,
850
+ syntheticOutpoint,
851
+ amountSompi: amountSompi.toString(),
852
+ feeSompi: SILVER_SIMULATOR_FEE_SOMPI.toString(),
853
+ status: "SIMULATED_ACCEPTED",
854
+ lineage: {
855
+ artifactId: `deploy-sim-${deployPlanHash.slice(0, 16)}`,
856
+ lineageId: `silver-lineage-${deployPlanHash.slice(0, 16)}`,
857
+ parentArtifactId: deployPlanHash,
858
+ rootArtifactId: deployPlanHash,
859
+ sequence: 1
860
+ }
861
+ };
862
+ const receipt = finalizeArtifact(draft, "silverdeploysim");
863
+ const state = createSilverSimulationState();
864
+ state.deployReceipts[receipt.contentHash] = receipt;
865
+ state.utxos[outpointKey(syntheticOutpoint)] = {
866
+ outpoint: syntheticOutpoint,
867
+ deploySimulationHash: receipt.contentHash,
868
+ deployPlanHash,
869
+ redeemScriptHex: deployPlanArtifact.redeemScriptHex,
870
+ redeemScriptHash: deployPlanArtifact.redeemScriptHash,
871
+ lockingScriptHex: deployPlanArtifact.lockingScriptHex,
872
+ amountSompi: amountSompi.toString(),
873
+ networkId: "simnet",
874
+ spent: false
875
+ };
876
+ return {
877
+ receipt,
878
+ state: normalizeState(state)
879
+ };
880
+ }
881
+ function simulateSilverSpend(spendPlanArtifact, simulatedState, options = {}) {
882
+ if (spendPlanArtifact.schema !== "hardkas.silver.spendPlan") {
883
+ throw new SilverSimulationError(
884
+ "SILVERSCRIPT_SCHEMA_INVALID",
885
+ "Expected hardkas.silver.spendPlan."
886
+ );
887
+ }
888
+ assertSimnet(spendPlanArtifact.networkId);
889
+ if (simulatedState.networkId !== "simnet") {
890
+ throw new SilverSimulationError(
891
+ "SILVERSCRIPT_NETWORK_UNSUPPORTED",
892
+ "Silver/Toccata simulation state must be simnet."
893
+ );
894
+ }
895
+ if (!Array.isArray(spendPlanArtifact.expectedOutputs) || spendPlanArtifact.expectedOutputs.length === 0) {
896
+ throw new SilverSimulationError(
897
+ "SILVERSCRIPT_EXPECTED_OUTPUTS_REQUIRED",
898
+ "Spend simulation requires explicit expectedOutputs."
899
+ );
900
+ }
901
+ const key = outpointKey(spendPlanArtifact.contractUtxoRef);
902
+ const utxo = simulatedState.utxos[key];
903
+ const deployReceipt = simulatedState.deployReceipts[spendPlanArtifact.deployArtifactHash] ?? Object.values(simulatedState.deployReceipts).find((receipt2) => {
904
+ return outpointKey(receipt2.syntheticOutpoint) === key;
905
+ });
906
+ if (!utxo || !deployReceipt) {
907
+ throw new SilverSimulationError(
908
+ "SILVERSCRIPT_DEPLOY_RECEIPT_NOT_FOUND",
909
+ `No deploy simulation receipt exists for ${key}.`
910
+ );
911
+ }
912
+ if (deployReceipt.networkId !== spendPlanArtifact.networkId || utxo.networkId !== spendPlanArtifact.networkId) {
913
+ throw new SilverSimulationError(
914
+ "SILVERSCRIPT_NETWORK_UNSUPPORTED",
915
+ "Spend plan network must match the deployed synthetic UTXO network."
916
+ );
917
+ }
918
+ if (utxo.spent || simulatedState.spentOutpoints.includes(key)) {
919
+ throw new SilverSimulationError(
920
+ "SILVERSCRIPT_UTXO_ALREADY_SPENT",
921
+ `Synthetic UTXO ${key} has already been spent.`
922
+ );
923
+ }
924
+ const pushes = parsePushOnlyScript(spendPlanArtifact.signatureScriptHex);
925
+ const redeemScriptHex = pushes.at(-1);
926
+ if (!redeemScriptHex) {
927
+ throw new SilverSimulationError(
928
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY",
929
+ "signatureScriptHex must end with a pushed redeem script."
930
+ );
931
+ }
932
+ if (redeemScriptHex !== utxo.redeemScriptHex.toLowerCase()) {
933
+ throw new SilverSimulationError(
934
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_MISMATCH",
935
+ "Redeem script must be the last push and match the deployed script."
936
+ );
937
+ }
938
+ const lock = createKaspaP2shBlake2bLock(redeemScriptHex);
939
+ if (lock.redeemScriptHash !== spendPlanArtifact.redeemScriptHash) {
940
+ throw new SilverSimulationError(
941
+ "SILVERSCRIPT_REDEEM_HASH_MISMATCH",
942
+ "Spend redeemScriptHash does not match pushed redeem script."
943
+ );
944
+ }
945
+ if (lock.lockingScriptHex !== spendPlanArtifact.lockingScriptHex) {
946
+ throw new SilverSimulationError(
947
+ "SILVERSCRIPT_LOCKING_SCRIPT_MISMATCH",
948
+ "Spend lockingScriptHex does not match pushed redeem script."
949
+ );
950
+ }
951
+ if (calculateSilverArgsHash(spendPlanArtifact.args) !== spendPlanArtifact.argsHash) {
952
+ throw new SilverSimulationError(
953
+ "SILVERSCRIPT_ARGS_HASH_MISMATCH",
954
+ "Spend argsHash does not match args."
955
+ );
956
+ }
957
+ const argValues = spendPlanArtifact.args.map((arg) => {
958
+ if (arg.type !== "hex" || !isHex(arg.value)) {
959
+ throw new SilverSimulationError(
960
+ "SILVERSCRIPT_INVALID_HEX",
961
+ "Spend args must be hex push values."
962
+ );
963
+ }
964
+ return arg.value.toLowerCase();
965
+ });
966
+ const pushedArgs = pushes.slice(0, -1);
967
+ if (JSON.stringify(pushedArgs) !== JSON.stringify(argValues)) {
968
+ throw new SilverSimulationError(
969
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_MISMATCH",
970
+ "signatureScriptHex args do not match spend plan args."
971
+ );
972
+ }
973
+ const inputAmount = BigInt(utxo.amountSompi);
974
+ const outputTotal = spendPlanArtifact.expectedOutputs.reduce((sum, output) => {
975
+ return sum + parseSompi(output.amountSompi);
976
+ }, 0n);
977
+ if (outputTotal <= 0n || outputTotal + SILVER_SIMULATOR_FEE_SOMPI > inputAmount) {
978
+ throw new SilverSimulationError(
979
+ "SILVERSCRIPT_AMOUNT_TOO_SMALL",
980
+ "Synthetic UTXO amount must cover expectedOutputs plus simulator fee."
981
+ );
982
+ }
983
+ const spendPlanHash = artifactHash(spendPlanArtifact);
984
+ const simulatedSpendTxId = deterministicHex({
985
+ kind: "hardkas.silver.simulatedSpendTx",
986
+ spendPlanHash,
987
+ deploySimulationHash: deployReceipt.contentHash,
988
+ spentOutpoint: key,
989
+ expectedOutputs: spendPlanArtifact.expectedOutputs
990
+ });
991
+ const draft = {
992
+ schema: "hardkas.silver.spendSimulation",
993
+ hardkasVersion: options.hardkasVersion ?? spendPlanArtifact.hardkasVersion ?? "0.9.1-alpha",
994
+ version: SILVER_SIMULATOR_VERSION,
995
+ hashVersion: CURRENT_HASH_VERSION,
996
+ networkId: "simnet",
997
+ mode: "simulated",
998
+ createdAt: options.createdAt ?? SILVER_SIMULATOR_CREATED_AT,
999
+ deploySimulationHash: deployReceipt.contentHash,
1000
+ spendPlanHash,
1001
+ redeemScriptHash: spendPlanArtifact.redeemScriptHash,
1002
+ lockingScriptHex: spendPlanArtifact.lockingScriptHex,
1003
+ signatureScriptHex: spendPlanArtifact.signatureScriptHex,
1004
+ simulatedSpendTxId,
1005
+ spentOutpoint: spendPlanArtifact.contractUtxoRef,
1006
+ expectedOutputs: spendPlanArtifact.expectedOutputs,
1007
+ feeSompi: SILVER_SIMULATOR_FEE_SOMPI.toString(),
1008
+ status: "SIMULATED_ACCEPTED",
1009
+ lineage: {
1010
+ artifactId: `spend-sim-${spendPlanHash.slice(0, 16)}`,
1011
+ lineageId: deployReceipt.lineage.lineageId,
1012
+ parentArtifactId: spendPlanHash,
1013
+ rootArtifactId: deployReceipt.lineage.rootArtifactId,
1014
+ sequence: 2
1015
+ }
1016
+ };
1017
+ const receipt = finalizeArtifact(draft, "silverspendsim");
1018
+ const nextState = cloneState(simulatedState);
1019
+ nextState.utxos[key] = {
1020
+ ...utxo,
1021
+ spent: true,
1022
+ spentByTxId: simulatedSpendTxId
1023
+ };
1024
+ nextState.spentOutpoints = Array.from(
1025
+ /* @__PURE__ */ new Set([...nextState.spentOutpoints, key])
1026
+ ).sort();
1027
+ return {
1028
+ receipt,
1029
+ state: normalizeState(nextState)
1030
+ };
1031
+ }
1032
+ function calculateSilverArgsHash(args) {
1033
+ return createHash2("sha256").update(JSON.stringify(args)).digest("hex");
1034
+ }
1035
+ function outpointKey(outpoint) {
1036
+ return `${outpoint.transactionId}:${outpoint.index}`;
1037
+ }
1038
+ function parsePushOnlyScript(signatureScriptHex) {
1039
+ assertHex(signatureScriptHex, "signatureScriptHex");
1040
+ const pushes = [];
1041
+ let offset = 0;
1042
+ while (offset < signatureScriptHex.length) {
1043
+ const opcode = readByte(signatureScriptHex, offset);
1044
+ offset += 2;
1045
+ let byteCount;
1046
+ if (opcode === 0) {
1047
+ byteCount = 0;
1048
+ } else if (opcode >= 1 && opcode <= 75) {
1049
+ byteCount = opcode;
1050
+ } else if (opcode === 76) {
1051
+ byteCount = readByte(signatureScriptHex, offset);
1052
+ offset += 2;
1053
+ } else if (opcode === 77) {
1054
+ byteCount = readLittleEndian(signatureScriptHex, offset, 2);
1055
+ offset += 4;
1056
+ } else if (opcode === 78) {
1057
+ byteCount = readLittleEndian(signatureScriptHex, offset, 4);
1058
+ offset += 8;
1059
+ } else {
1060
+ throw new SilverSimulationError(
1061
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY",
1062
+ `Non-push opcode 0x${opcode.toString(16)} encountered.`
1063
+ );
1064
+ }
1065
+ const hexCount = byteCount * 2;
1066
+ const end = offset + hexCount;
1067
+ if (end > signatureScriptHex.length) {
1068
+ throw new SilverSimulationError(
1069
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY",
1070
+ "Truncated pushdata in signatureScriptHex."
1071
+ );
1072
+ }
1073
+ pushes.push(signatureScriptHex.slice(offset, end).toLowerCase());
1074
+ offset = end;
1075
+ }
1076
+ return pushes;
1077
+ }
1078
+ function assertSimnet(networkId) {
1079
+ if (networkId !== "simnet") {
1080
+ throw new SilverSimulationError(
1081
+ "SILVERSCRIPT_NETWORK_UNSUPPORTED",
1082
+ "Silver/Toccata simulator only supports local simnet."
1083
+ );
1084
+ }
1085
+ }
1086
+ function assertHex(value, field) {
1087
+ if (!isHex(value)) {
1088
+ throw new SilverSimulationError(
1089
+ "SILVERSCRIPT_INVALID_HEX",
1090
+ `${field} must be even-length hex.`
1091
+ );
1092
+ }
1093
+ }
1094
+ function isHex(value) {
1095
+ return typeof value === "string" && value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
1096
+ }
1097
+ function calculateContentHash(value, _version = CURRENT_HASH_VERSION) {
1098
+ return createHash2("sha256").update(canonicalStringify(value)).digest("hex");
1099
+ }
1100
+ function canonicalStringify(value) {
1101
+ if (typeof value === "bigint") {
1102
+ return JSON.stringify(`n:${value.toString()}`);
1103
+ }
1104
+ if (value === null || typeof value !== "object") {
1105
+ return JSON.stringify(value);
1106
+ }
1107
+ if (Array.isArray(value)) {
1108
+ return `[${value.map((item) => canonicalStringify(item)).join(",")}]`;
1109
+ }
1110
+ const exclusions = /* @__PURE__ */ new Set([
1111
+ "contentHash",
1112
+ "artifactId",
1113
+ "hashVersion",
1114
+ "createdAt",
1115
+ "hardkasVersion",
1116
+ "status"
1117
+ ]);
1118
+ const record = value;
1119
+ return `{${Object.keys(record).filter((key) => !exclusions.has(key) && record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalStringify(record[key])}`).join(",")}}`;
1120
+ }
1121
+ function parseSompi(value) {
1122
+ if (!/^[0-9]+$/.test(value)) {
1123
+ throw new SilverSimulationError(
1124
+ "SILVERSCRIPT_AMOUNT_TOO_SMALL",
1125
+ "Sompi values must be unsigned integer strings."
1126
+ );
1127
+ }
1128
+ return BigInt(value);
1129
+ }
1130
+ function artifactHash(value) {
1131
+ return value.contentHash ?? calculateContentHash(value, CURRENT_HASH_VERSION);
1132
+ }
1133
+ function deterministicHex(value) {
1134
+ return calculateContentHash(value, CURRENT_HASH_VERSION);
1135
+ }
1136
+ function finalizeArtifact(artifact, prefix) {
1137
+ const contentHash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1138
+ return {
1139
+ ...artifact,
1140
+ contentHash,
1141
+ artifactId: `${prefix}-${contentHash.slice(0, 16)}`
1142
+ };
1143
+ }
1144
+ function cloneState(state) {
1145
+ return JSON.parse(JSON.stringify(state));
1146
+ }
1147
+ function normalizeState(state) {
1148
+ return {
1149
+ ...state,
1150
+ deployReceipts: Object.fromEntries(
1151
+ Object.entries(state.deployReceipts).sort(([a], [b]) => a.localeCompare(b))
1152
+ ),
1153
+ utxos: Object.fromEntries(
1154
+ Object.entries(state.utxos).sort(([a], [b]) => a.localeCompare(b))
1155
+ ),
1156
+ spentOutpoints: [...state.spentOutpoints].sort()
1157
+ };
1158
+ }
1159
+ function readByte(hex, offset) {
1160
+ const raw = hex.slice(offset, offset + 2);
1161
+ if (raw.length !== 2) {
1162
+ throw new SilverSimulationError(
1163
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY",
1164
+ "Truncated opcode in signatureScriptHex."
1165
+ );
1166
+ }
1167
+ return parseInt(raw, 16);
1168
+ }
1169
+ function readLittleEndian(hex, offset, byteCount) {
1170
+ const raw = hex.slice(offset, offset + byteCount * 2);
1171
+ if (raw.length !== byteCount * 2) {
1172
+ throw new SilverSimulationError(
1173
+ "SILVERSCRIPT_SIGNATURE_SCRIPT_NOT_PUSH_ONLY",
1174
+ "Truncated pushdata length in signatureScriptHex."
1175
+ );
1176
+ }
1177
+ const bytes = raw.match(/../g) ?? [];
1178
+ return parseInt(bytes.reverse().join(""), 16);
1179
+ }
760
1180
  export {
761
1181
  ApproxGhostdagEngine,
762
1182
  DEFAULT_K,
763
1183
  GENESIS_HASH,
764
1184
  GhostdagStore,
1185
+ SILVER_SIMULATOR_CREATED_AT,
1186
+ SILVER_SIMULATOR_FEE_SOMPI,
1187
+ SILVER_SIMULATOR_VERSION,
1188
+ SilverSimulationError,
765
1189
  TxSimulator,
766
1190
  blockBlueScore,
767
1191
  blockBlueWork,
768
1192
  blockHash,
769
1193
  blockParents,
1194
+ calculateSilverArgsHash,
770
1195
  compactFromFull,
771
1196
  compareMassProfiles,
772
1197
  compareSortableBlocks,
773
1198
  computeDagMetrics,
1199
+ createSilverSimulationState,
774
1200
  createTraceId,
775
1201
  findSelectedParent,
776
1202
  formatMassComparison,
@@ -781,6 +1207,8 @@ export {
781
1207
  isDagAncestorOf,
782
1208
  loadMassSnapshot,
783
1209
  orderedMergesetWithoutSelectedParent,
1210
+ outpointKey,
1211
+ parsePushOnlyScript,
784
1212
  pastSet,
785
1213
  profileAndCompare,
786
1214
  profileMass,
@@ -790,6 +1218,8 @@ export {
790
1218
  runLinearChain,
791
1219
  runWideDag,
792
1220
  saveMassSnapshot,
1221
+ simulateSilverDeploy,
1222
+ simulateSilverSpend,
793
1223
  sortBlocks,
794
1224
  unorderedMergesetWithoutSelectedParent
795
1225
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/simulator",
3
- "version": "0.8.20-alpha",
3
+ "version": "0.9.1-alpha",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -12,8 +12,8 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@hardkas/core": "0.8.20-alpha",
16
- "@hardkas/tx-builder": "0.8.20-alpha"
15
+ "@hardkas/core": "0.9.1-alpha",
16
+ "@hardkas/tx-builder": "0.9.1-alpha"
17
17
  },
18
18
  "devDependencies": {
19
19
  "tsup": "^8.3.5",