164 lines
5.7 KiB
TypeScript
164 lines
5.7 KiB
TypeScript
import { BlockchainMonitor, Engine } from "@xo-cash/engine";
|
|
|
|
import { createStorageAdapter, State, StorageType, type UnspentOutputData } from "@xo-cash/state";
|
|
import { InMemoryBlockchainProvider } from "@xo-cash/engine";
|
|
import { convertMnemonicToSeedBytes } from "@xo-cash/crypto";
|
|
|
|
import { binToHex, sha256 } from "@bitauth/libauth";
|
|
import { AppService } from "../../../src/services/app";
|
|
import { InMemoryStorage } from "../../../src/services/storage";
|
|
import { MockElectrumService } from "./electrum-service";
|
|
|
|
export const DEFAULT_SEED = "page pencil stock planet limb cluster assault speak off joke private pioneer";
|
|
|
|
/**
|
|
* Options for creating a fake resource (UTXO) in tests.
|
|
*/
|
|
export type FakeResourceOptions = {
|
|
/** Transaction hash of the outpoint. Auto-generated if not provided. */
|
|
outpointTransactionHash?: string;
|
|
/** Index of the outpoint in the transaction. Defaults to 0. */
|
|
outpointIndex?: number;
|
|
/** Value in satoshis. Defaults to 10000. */
|
|
valueSatoshis?: number;
|
|
/** Template identifier. Defaults to "test-template". */
|
|
templateIdentifier?: string;
|
|
/** Output identifier from the template. Defaults to "receiveOutput". */
|
|
outputIdentifier?: string;
|
|
/** Locking bytecode for this output. Defaults to a placeholder. */
|
|
lockingBytecode?: string;
|
|
/** Block height where the UTXO was mined. Defaults to 800000. */
|
|
minedAtHeight?: number;
|
|
/** Invitation identifier that reserves this output. Undefined means unreserved. */
|
|
reservedBy?: string;
|
|
};
|
|
|
|
/**
|
|
* Generates a random 64-character hex string representing a transaction hash.
|
|
*/
|
|
export const randomTxHash = (): string => {
|
|
const bytes = new Uint8Array(32);
|
|
crypto.getRandomValues(bytes);
|
|
return Array.from(bytes).map(b => b.toString(16).padStart(2, "0")).join("");
|
|
};
|
|
|
|
/**
|
|
* Adds a fake resource (UTXO) to the engine's state for testing purposes.
|
|
* @param engine - The engine instance to add the resource to.
|
|
* @param options - Options for the fake resource. All fields have sensible defaults.
|
|
* @returns The created UnspentOutputData object.
|
|
*/
|
|
export const addFakeResource = async (
|
|
engine: Engine,
|
|
options: FakeResourceOptions = {},
|
|
): Promise<UnspentOutputData> => {
|
|
const resource: UnspentOutputData = {
|
|
status: "confirmed",
|
|
selectable: true,
|
|
privacy: false,
|
|
templateIdentifier: options.templateIdentifier ?? "test-template",
|
|
outputIdentifier: options.outputIdentifier ?? "receiveOutput",
|
|
outpointIndex: options.outpointIndex ?? 0,
|
|
outpointTransactionHash: options.outpointTransactionHash ?? randomTxHash(),
|
|
minedAtHeight: options.minedAtHeight ?? 800000,
|
|
valueSatoshis: options.valueSatoshis ?? 10000,
|
|
lockingBytecode: options.lockingBytecode ?? "76a914000000000000000000000000000000000000000088ac",
|
|
reservedBy: options.reservedBy,
|
|
};
|
|
|
|
await engine.state.storeUnspentOutputData(resource);
|
|
return resource;
|
|
};
|
|
|
|
/**
|
|
* Reserves a resource for a specific invitation.
|
|
* @param engine - The engine instance.
|
|
* @param outpointTransactionHash - The transaction hash of the UTXO to reserve.
|
|
* @param outpointIndex - The output index of the UTXO to reserve.
|
|
* @param invitationIdentifier - The invitation identifier to reserve for.
|
|
*/
|
|
export const reserveResource = async (
|
|
engine: Engine,
|
|
outpointTransactionHash: string,
|
|
outpointIndex: number,
|
|
invitationIdentifier: string,
|
|
): Promise<void> => {
|
|
await engine.state.executeBulkUnspentOutputReservation(
|
|
[{ outpointTransactionHash, outpointIndex }],
|
|
true,
|
|
invitationIdentifier,
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Unreserves a resource from a specific invitation.
|
|
* @param engine - The engine instance.
|
|
* @param outpointTransactionHash - The transaction hash of the UTXO to unreserve.
|
|
* @param outpointIndex - The output index of the UTXO to unreserve.
|
|
* @param invitationIdentifier - The invitation identifier to unreserve from.
|
|
*/
|
|
export const unreserveResource = async (
|
|
engine: Engine,
|
|
outpointTransactionHash: string,
|
|
outpointIndex: number,
|
|
invitationIdentifier: string,
|
|
): Promise<void> => {
|
|
await engine.state.executeBulkUnspentOutputReservation(
|
|
[{ outpointTransactionHash, outpointIndex }],
|
|
false,
|
|
invitationIdentifier,
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Create a mock engine instance with a given seed. Uses the in-memory storage and blockchain provider.
|
|
* @param seed - The seed to use for the engine.
|
|
* @returns A mock engine instance.
|
|
*/
|
|
export const createMockEngine = async (seed: string) => {
|
|
// Create the in-memory storage adapter.
|
|
const storage = await createStorageAdapter({
|
|
storageType: StorageType.INMEMORY,
|
|
accountHash: binToHex(sha256.hash(convertMnemonicToSeedBytes(seed))),
|
|
});
|
|
|
|
// Initialize the storage adapter.
|
|
await storage.initialize();
|
|
|
|
// Create the state instance.
|
|
const state = new State(storage);
|
|
|
|
// Create the in-memory blockchain provider.
|
|
const blockchainProvider = new InMemoryBlockchainProvider();
|
|
await blockchainProvider.initialize({
|
|
applicationIdentifier: "xo-cli-tests",
|
|
electrumOptions: {},
|
|
});
|
|
|
|
// Create the blockchain monitor instance.
|
|
const blockchainMonitor = new BlockchainMonitor(state, blockchainProvider);
|
|
await blockchainMonitor.initializeEventListeners();
|
|
|
|
// Create the engine instance.
|
|
const engine = new Engine(seed, state, blockchainMonitor, blockchainProvider);
|
|
await engine.initializeStateSync();
|
|
|
|
return engine;
|
|
};
|
|
|
|
export const createMockAppService = async (engine: Engine) => {
|
|
const storage = await InMemoryStorage.create();
|
|
|
|
const electrum = new MockElectrumService();
|
|
|
|
const config = {
|
|
syncServerUrl: "http://localhost:3000",
|
|
engineConfig: {
|
|
databasePath: "test-data",
|
|
databaseFilename: "xo-wallet.db",
|
|
},
|
|
invitationStoragePath: "test-invitations.db",
|
|
};
|
|
|
|
return new AppService(engine, storage, config, electrum);
|
|
}; |