UNPKG

@bsv/wallet-toolbox-client

Version:
522 lines (521 loc) 26.2 kB
import { Knex } from 'knex'; import { Beef, CreateActionResult, HexString, KeyDeriverApi, PrivateKey, PublicKey, SignActionResult, Transaction, WalletAction, WalletActionInput, WalletActionOutput, WalletCertificate, WalletInterface } from '@bsv/sdk'; import { Chain } from '../../src/sdk/types'; import { StorageKnex } from '../../src/storage/StorageKnex'; import { Services } from '../../src/services/Services'; import { TrxToken, WalletStorageProvider } from '../../src/sdk/WalletStorage.interfaces'; import { WalletStorageManager } from '../../src/storage/WalletStorageManager'; import { GetMerklePathResult } from '../../src/sdk/WalletServices.interfaces'; import { Monitor } from '../../src/monitor/Monitor'; import { Wallet } from '../../src/Wallet'; import { StorageProvider } from '../../src/storage/StorageProvider'; import { TableProvenTx } from '../../src/storage/schema/tables/TableProvenTx'; import { TableProvenTxReq } from '../../src/storage/schema/tables/TableProvenTxReq'; import { TableUser } from '../../src/storage/schema/tables/TableUser'; import { TableCertificate } from '../../src/storage/schema/tables/TableCertificate'; import { TableCertificateField } from '../../src/storage/schema/tables/TableCertificateField'; import { TableOutputBasket } from '../../src/storage/schema/tables/TableOutputBasket'; import { TableTransaction } from '../../src/storage/schema/tables/TableTransaction'; import { TableOutput } from '../../src/storage/schema/tables/TableOutput'; import { TableOutputTag } from '../../src/storage/schema/tables/TableOutputTag'; import { TableOutputTagMap } from '../../src/storage/schema/tables/TableOutputTagMap'; import { TableTxLabel } from '../../src/storage/schema/tables/TableTxLabel'; import { TableTxLabelMap } from '../../src/storage/schema/tables/TableTxLabelMap'; import { TableSyncState } from '../../src/storage/schema/tables/TableSyncState'; import { TableMonitorEvent } from '../../src/storage/schema/tables/TableMonitorEvent'; import { TableCommission } from '../../src/storage/schema/tables/TableCommission'; export interface TuEnvFlags { chain: Chain; runMySQL: boolean; runSlowTests: boolean; logTests: boolean; } export interface TuEnv extends TuEnvFlags { chain: Chain; identityKey: string; identityKey2: string; taalApiKey: string; bitailsApiKey: string; whatsonchainApiKey: string; devKeys: Record<string, string>; /** * file path to local sqlite file for identityKey */ filePath?: string; /** * identityKey for automated test wallet on this chain */ testIdentityKey?: string; /** * file path to local sqlite file for testIdentityKey */ testFilePath?: string; cloudMySQLConnection?: string; } export declare abstract class TestUtilsWalletStorage { /** * @param chain * @returns true if .env has truthy idenityKey, idenityKey2 values for chain */ static noEnv(chain: Chain): boolean; /** * @param chain * @returns true if .env is not valid for chain or testIdentityKey or testFilePath are undefined or empty. */ static noTestEnv(chain: Chain): boolean; static getEnvFlags(chain: Chain): TuEnvFlags; static getEnv(chain: Chain): TuEnv; static createMainReviewSetup(): Promise<{ env: TuEnv; storage: StorageKnex; services: Services; }>; static createNoSendP2PKHTestOutpoint(address: string, satoshis: number, noSendChange: string[] | undefined, wallet: WalletInterface): Promise<{ noSendChange: string[]; txid: string; cr: CreateActionResult; sr: SignActionResult; }>; static createNoSendP2PKHTestOutpoints(count: number, address: string, satoshis: number, noSendChange: string[] | undefined, wallet: WalletInterface): Promise<{ noSendChange: string[]; txid: string; cr: CreateActionResult; sr: SignActionResult; }>; static getKeyPair(priv?: string | PrivateKey): TestKeyPair; static getLockP2PKH(address: string): import("@bsv/sdk").LockingScript; static getUnlockP2PKH(priv: PrivateKey, satoshis: number): { sign: (tx: Transaction, inputIndex: number) => Promise<import("@bsv/sdk").UnlockingScript>; estimateLength: () => Promise<108>; }; static createWalletOnly(args: { chain?: Chain; rootKeyHex?: string; active?: WalletStorageProvider; backups?: WalletStorageProvider[]; privKeyHex?: string; }): Promise<TestWalletOnly>; /** * Creates a wallet with both local sqlite and cloud stores, the local store is left active. * * Requires a valid .env file with chain matching testIdentityKey and testFilePath properties valid. * Or `args` with those properties. * * Verifies wallet has at least 1000 satoshis in at least 10 change utxos. * * @param chain * * @returns {TestWalletNoSetup} */ static createTestWallet(args: Chain | CreateTestWalletArgs): Promise<TestWalletNoSetup>; static createTestWalletWithStorageClient(args: { rootKeyHex?: string; endpointUrl?: string; chain?: Chain; }): Promise<TestWalletOnly>; static createKnexTestWalletWithSetup<T>(args: { knex: Knex<any, any[]>; databaseName: string; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; privKeyHex?: string; insertSetup: (storage: StorageKnex, identityKey: string) => Promise<T>; }): Promise<TestWallet<T>>; /** * Returns path to temporary file in project's './test/data/tmp/' folder. * * Creates any missing folders. * * Optionally tries to delete any existing file. This may fail if the file file is locked * by another process. * * Optionally copies filename (or if filename has no dir component, a file of the same filename from the project's './test/data' folder) to initialize file's contents. * * CAUTION: returned file path will include four random hex digits unless tryToDelete is true. Files must be purged periodically. * * @param filename target filename without path, optionally just extension in which case random name is used * @param tryToDelete true to attempt to delete an existing file at the returned file path. * @param copyToTmp true to copy file of same filename from './test/data' (or elsewhere if filename has path) to tmp folder * @param reuseExisting true to use existing file if found, otherwise a random string is added to filename. * @returns path in './test/data/tmp' folder. */ static newTmpFile(filename?: string, tryToDelete?: boolean, copyToTmp?: boolean, reuseExisting?: boolean): Promise<string>; static copyFile(srcPath: string, dstPath: string): Promise<void>; static existingDataFile(filename: string): Promise<string>; static createLocalSQLite(filename: string): Knex; static createMySQLFromConnection(connection: object): Knex; static createLocalMySQL(database: string): Knex; static createMySQLTestWallet(args: { databaseName: string; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; }): Promise<TestWallet<{}>>; static createMySQLTestSetup1Wallet(args: { databaseName: string; chain?: Chain; rootKeyHex?: string; }): Promise<TestWallet<TestSetup1>>; static createMySQLTestSetup2Wallet(args: { databaseName: string; mockData: MockData; chain?: Chain; rootKeyHex?: string; }): Promise<TestWallet<TestSetup2>>; static createSQLiteTestWallet(args: { filePath?: string; databaseName: string; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; privKeyHex?: string; }): Promise<TestWalletNoSetup>; static createSQLiteTestSetup1Wallet(args: { databaseName: string; chain?: Chain; rootKeyHex?: string; }): Promise<TestWallet<TestSetup1>>; static createSQLiteTestSetup2Wallet(args: { databaseName: string; mockData: MockData; chain?: Chain; rootKeyHex?: string; }): Promise<TestWallet<TestSetup2>>; static createKnexTestWallet(args: { knex: Knex<any, any[]>; databaseName: string; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; privKeyHex?: string; }): Promise<TestWalletNoSetup>; static createKnexTestSetup1Wallet(args: { knex: Knex<any, any[]>; databaseName: string; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; }): Promise<TestWallet<TestSetup1>>; static createKnexTestSetup2Wallet(args: { knex: Knex<any, any[]>; databaseName: string; mockData: MockData; chain?: Chain; rootKeyHex?: string; dropAll?: boolean; }): Promise<TestWallet<TestSetup2>>; static fileExists(file: string): Promise<boolean>; static createLegacyWalletSQLiteCopy(databaseName: string): Promise<TestWalletNoSetup>; static createLegacyWalletMySQLCopy(databaseName: string): Promise<TestWalletNoSetup>; static createLiveWalletSQLiteWARNING(databaseFullPath?: string): Promise<TestWalletNoSetup>; static createWalletSQLite(databaseFullPath?: string, databaseName?: string): Promise<TestWalletNoSetup>; static legacyRootKeyHex: string; static createLegacyWalletCopy(databaseName: string, walletKnex: Knex<any, any[]>, tryCopyToPath?: string): Promise<TestWalletNoSetup>; static wrapProfiling(o: Object, name: string): Record<string, { count: number; totalMsecs: number; }>; static createIdbLegacyWalletCopy(databaseName: string): Promise<TestWalletProviderNoSetup>; static makeSampleCert(subject?: string): { cert: WalletCertificate; subject: string; certifier: PrivateKey; }; static insertTestProvenTx(storage: StorageProvider, txid?: string, trx?: TrxToken): Promise<TableProvenTx>; static insertTestProvenTxReq(storage: StorageProvider, txid?: string, provenTxId?: number, onlyRequired?: boolean): Promise<TableProvenTxReq>; static insertTestUser(storage: StorageProvider, identityKey?: string): Promise<TableUser>; static insertTestCertificate(storage: StorageProvider, u?: TableUser): Promise<TableCertificate>; static insertTestCertificateField(storage: StorageProvider, c: TableCertificate, name: string, value: string): Promise<TableCertificateField>; static insertTestOutputBasket(storage: StorageProvider, u?: TableUser | number, partial?: Partial<TableOutputBasket>): Promise<TableOutputBasket>; static insertTestTransaction(storage: StorageProvider, u?: TableUser, onlyRequired?: boolean, partial?: Partial<TableTransaction>): Promise<{ tx: TableTransaction; user: TableUser; }>; static insertTestOutput(storage: StorageProvider, t: TableTransaction, vout: number, satoshis: number, basket?: TableOutputBasket, requiredOnly?: boolean, partial?: Partial<TableOutput>): Promise<TableOutput>; static insertTestOutputTag(storage: StorageProvider, u: TableUser, partial?: Partial<TableOutputTag>): Promise<TableOutputTag>; static insertTestOutputTagMap(storage: StorageProvider, o: TableOutput, tag: TableOutputTag): Promise<TableOutputTagMap>; static insertTestTxLabel(storage: StorageProvider, u: TableUser, partial?: Partial<TableTxLabel>): Promise<TableTxLabel>; static insertTestTxLabelMap(storage: StorageProvider, tx: TableTransaction, label: TableTxLabel, partial?: Partial<TableTxLabelMap>): Promise<TableTxLabelMap>; static insertTestSyncState(storage: StorageProvider, u: TableUser): Promise<TableSyncState>; static insertTestMonitorEvent(storage: StorageProvider): Promise<TableMonitorEvent>; static insertTestCommission(storage: StorageProvider, t: TableTransaction): Promise<TableCommission>; static createTestSetup1(storage: StorageProvider, u1IdentityKey?: string): Promise<TestSetup1>; static createTestSetup2(storage: StorageProvider, identityKey: string, mockData?: MockData): Promise<TestSetup2>; static mockPostServicesAsSuccess(ctxs: TestWalletOnly[]): void; static mockPostServicesAsError(ctxs: TestWalletOnly[]): void; static mockPostServicesAsCallback(ctxs: TestWalletOnly[], callback: (beef: Beef, txids: string[]) => 'success' | 'error'): void; static mockMerklePathServicesAsCallback(ctxs: TestWalletOnly[], callback: (txid: string) => Promise<GetMerklePathResult>): void; static createWalletSetupEnv(chain: Chain): Promise<TestWalletOnly>; /** * Create a pair of transacitons that cancel out, other than the transaciton fees. * Both created transactions are left with status 'noSend'. * This allows the transactions to either be broadcast by an external party, * or they may be aborted. * * `doubleSpendTx` should only be used for double spend testing. * It attempts to forward the txidDo input, which should already have been reclaimed by txidUndo, to a random private key output. * * @param wallet the wallet that will create both transactions, or Chain and createWalletEnv is used to create a wallet. * @param satoshis amount of new output created and consumed. Defaults to 41. * @returns { txidDo: string, txidUndo: string, beef: Beef, doubleSpendTx: transaction } */ static createNoSendTxPair(wallet: Wallet | Chain, satoshis?: number): Promise<{ txidDo: string; txidUndo: string; beef: Beef; doubleSpendTx: Transaction; }>; } export declare abstract class _tu extends TestUtilsWalletStorage { } export interface TestSetup1 { u1: TableUser; u1basket1: TableOutputBasket; u1basket2: TableOutputBasket; u1label1: TableTxLabel; u1label2: TableTxLabel; u1tag1: TableOutputTag; u1tag2: TableOutputTag; u1tx1: TableTransaction; u1comm1: TableCommission; u1tx1label1: TableTxLabelMap; u1tx1label2: TableTxLabelMap; u1tx1o0: TableOutput; u1o0tag1: TableOutputTagMap; u1o0tag2: TableOutputTagMap; u1tx1o1: TableOutput; u1o1tag1: TableOutputTagMap; u1cert1: TableCertificate; u1cert1field1: TableCertificateField; u1cert1field2: TableCertificateField; u1cert2: TableCertificate; u1cert2field1: TableCertificateField; u1cert3: TableCertificate; u1sync1: TableSyncState; u2: TableUser; u2basket1: TableOutputBasket; u2label1: TableTxLabel; u2tx1: TableTransaction; u2comm1: TableCommission; u2tx1label1: TableTxLabelMap; u2tx1o0: TableOutput; u2tx2: TableTransaction; u2comm2: TableCommission; proven1: TableProvenTx; req1: TableProvenTxReq; req2: TableProvenTxReq; we1: TableMonitorEvent; } export interface MockData { inputs?: WalletActionInput[]; outputs?: WalletActionOutput[]; actions: WalletAction[]; } export interface TestSetup2 extends MockData { } export interface TestWalletProvider<T> extends TestWalletOnly { activeStorage: StorageProvider; setup?: T; userId: number; rootKey: PrivateKey; identityKey: string; keyDeriver: KeyDeriverApi; chain: Chain; storage: WalletStorageManager; services: Services; monitor: Monitor; wallet: Wallet; localStorageIdentityKey?: string; clientStorageIdentityKey?: string; localBackupStorageIdentityKey?: string; } export interface TestWallet<T> extends TestWalletOnly { activeStorage: StorageKnex; setup?: T; userId: number; rootKey: PrivateKey; identityKey: string; keyDeriver: KeyDeriverApi; chain: Chain; storage: WalletStorageManager; services: Services; monitor: Monitor; wallet: Wallet; localStorageIdentityKey?: string; clientStorageIdentityKey?: string; localBackupStorageIdentityKey?: string; } export interface TestWalletOnly { rootKey: PrivateKey; identityKey: string; keyDeriver: KeyDeriverApi; chain: Chain; storage: WalletStorageManager; services: Services; monitor: Monitor; wallet: Wallet; } export type TestSetup2Wallet = TestWallet<TestSetup2>; export type TestSetup1Wallet = TestWallet<TestSetup1>; export type TestWalletNoSetup = TestWallet<{}>; export type TestWalletProviderNoSetup = TestWalletProvider<{}>; export declare function expectToThrowWERR<R>(expectedClass: new (...args: any[]) => any, fn: () => Promise<R>): Promise<void>; export type TestKeyPair = { privateKey: PrivateKey; publicKey: PublicKey; address: string; }; /** * Centralized logging function to handle logging based on running in jest "single test" mode, * or when `logEnabled` is true. * * @param {string} message - The main message to log. * @param {...any} optionalParams - Additional parameters to log (optional). * @returns {void} This function does not return any value. * * @example * log('Test message', someVariable); * log('Another message with multiple params', param1, param2); */ export declare const logger: (message: string, ...optionalParams: any[]) => void; /** * Updates a table dynamically based on key-value pairs in testValues. * @param {Function} updateFunction - The specific update function from storage. * @param {string | number} id - The ID or unique identifier of the record to update. * @param {Object} testValues - An object containing key-value pairs to update. */ export declare const updateTable: (updateFunction: any, id: any, testValues: any) => Promise<void>; /** * Verifies that all key-value pairs in `testValues` match the corresponding keys in `targetObject`. * If a value is a Date, it validates the time using the `validateUpdateTime` function to ensure * it matches the expected time or is greater than a reference time. * * @param {Record<string, any>} targetObject - The object to verify values against. * @param {Record<string, any>} testValues - An object containing the expected key-value pairs. * @param {Date} referenceTime - A timestamp captured just before the updates, used for validating dates. * * @example * const targetObject = { key1: 'value1', created_at: new Date('2024-12-30T23:00:00Z') } * const testValues = { key1: 'value1', created_at: new Date('2024-12-30T23:00:00Z') } * const referenceTime = new Date() * verifyValues(targetObject, testValues, referenceTime) */ export declare const verifyValues: (targetObject: Record<string, any>, testValues: Record<string, any>, referenceTime: Date) => void; /** * Comparison function to validate update time. * Allows the time to match the expected update time or be greater than a reference time. * Validates across multiple formats with a tolerance for minor discrepancies. * @param {Date} actualTime - The `updated_at` time returned from the storage. * @param {Date} expectedTime - The time you tried to set. * @param {Date} referenceTime - A timestamp captured just before the update attempt. * @param {number} toleranceMs - Optional tolerance in milliseconds for discrepancies (default: 10ms). * @param {boolean} [ logEnabled=false ] - A flag to enable or disable logging for this error. * @returns {boolean} - Returns `true` if the validation passes; `false` otherwise. * Logs human-readable details if the validation fails. */ export declare const validateUpdateTime: (actualTime: Date, expectedTime: Date, referenceTime: Date, toleranceMs?: number, logEnabled?: boolean) => boolean; /** * Set whether logging should be enabled or disabled globally. * * @param {boolean} enabled - A flag to enable or disable logging. * `true` enables logging, `false` disables logging. * * @returns {void} This function does not return any value. * * @example * setLogging(true); // Enable logging * setLogging(false); // Disable logging */ export declare const setLogging: (enabled: boolean) => void; /** * Logs the unique constraint error for multiple fields. * * @param {any} error - The error object that contains the error message. * @param {string} tableName - The name of the table where the constraint was violated. * @param {string[]} columnNames - An array of column names for which to check the unique constraint. * @param {boolean} logEnabled - A flag to enable or disable logging. */ export declare const logUniqueConstraintError: (error: any, tableName: string, columnNames: string[], logEnabled?: boolean) => void; /** * Triggers a unique constraint error by attempting to update a row with a value that violates a unique constraint. * * @param {any} storage - The storage object, typically containing the database methods for performing CRUD operations. * @param {string} findMethod - The method name for finding rows in the table (e.g., `findProvenTxReqs`). * @param {string} updateMethod - The method name for updating rows in the table (e.g., `updateProvenTxReq`). * @param {string} tableName - The name of the table being updated. * @param {string} columnName - The column name for which the unique constraint is being tested. * @param {any} invalidValue - The value to assign to the column that should trigger the unique constraint error. This should be an object with the column name(s) as the key(s). * @param {number} [id=1] - The id used to set the column value during the test (default is 1). * @param {boolean} [ logEnabled=false ] - A flag to enable or disable logging during the test. Default is `true` (logging enabled). * * @returns {Promise<boolean>} This function returns true if error thrown otherwise false, it performs an async operation to test the unique constraint error. * * @throws {Error} Throws an error if the unique constraint error is not triggered or if the table has insufficient rows. * * @example await triggerUniqueConstraintError(storage, 'ProvenTxReq', 'proven_tx_reqs', 'provenTxReqId', { provenTxReqId: 42 }, 1, true) */ export declare const triggerUniqueConstraintError: (storage: any, findMethod: string, updateMethod: string, tableName: string, columnName: string, invalidValue: any, // This remains an object passed in by the caller id?: number, logEnabled?: boolean) => Promise<boolean>; /** * Tests that the foreign key constraint error is triggered for any table and column. * * @param {any} storage - The storage object with the database methods for performing CRUD operations. * @param {string} findMethod - The method name for finding rows in the table (e.g., `findProvenTxReqs`). * @param {string} updateMethod - The method name for updating rows in the table (e.g., `updateProvenTxReq`). * @param {string} tableName - The name of the table being updated. * @param {string} columnName - The column name being tested for the foreign key constraint. * @param {any} invalidValue - The value to assign to the column that should trigger the foreign key constraint error. This should be an object with the column name as the key. * @param {number} [id=1] - The id used to set the column value during the test (default is 1). * @param {boolean} [ logEnabled=false ] - A flag to enable or disable logging during the test. Default is `true` (logging enabled). * * @returns {Promise<boolean>} This function returns true if error thrown otherwise false, it performs an async operation to test the foreign key constraint error. * * @throws {Error} Throws an error if the foreign key constraint error is not triggered. * * @example await triggerForeignKeyConstraintError(storage, 'findProvenTxReqs', 'updateProvenTxReq', 'proven_tx_reqs', 'provenTxId', { provenTxId: 42 }) */ export declare const triggerForeignKeyConstraintError: (storage: any, findMethod: string, updateMethod: string, tableName: string, columnName: string, invalidValue: any, id?: number, logEnabled?: boolean) => Promise<boolean>; /** * Aborts all transactions with the status `'nosend'` in the storage and verifies success. * * @param {Wallet} wallet - The wallet instance used to abort actions. * @param {StorageKnex} storage - The storage instance to query transactions from. * @returns {Promise<boolean>} - Resolves to `true` if all `'nosend'` transactions were successfully aborted. */ export declare function cleanUnsentTransactionsUsingAbort(wallet: Wallet, storage: StorageKnex): Promise<boolean>; /** * Aborts all transactions with the status `'unsigned'` in the storage and verifies success. * * @param {Wallet} wallet - The wallet instance used to abort actions. * @param {StorageKnex} storage - The storage instance to query transactions from. * @returns {Promise<boolean>} - Resolves to `true` if all `'unsigned'` transactions were successfully aborted. */ export declare function cleanUnsignedTransactionsUsingAbort(wallet: Wallet, storage: StorageKnex): Promise<boolean>; /** * Aborts all transactions with the status `'unprocessed'` in the storage and verifies success. * * @param {Wallet} wallet - The wallet instance used to abort actions. * @param {StorageKnex} storage - The storage instance to query transactions from. * @returns {Promise<boolean>} - Resolves to `true` if all `'unprocessed'` transactions were successfully aborted. */ export declare function cleanUnprocessedTransactionsUsingAbort(wallet: Wallet, storage: StorageKnex): Promise<boolean>; /** * Normalize a date or ISO string to a consistent ISO string format. * @param value - The value to normalize (Date object or ISO string). * @returns ISO string or null if not a date-like value. */ export declare const normalizeDate: (value: any) => string | null; export declare function logTransaction(storage: StorageKnex, txid: HexString): Promise<string>; export declare function logOutput(storage: StorageKnex, output: TableOutput): Promise<string>; export declare function logInput(storage: StorageKnex, prevOutputTxid: HexString, prevOutputVout: number, indentLevel?: number): Promise<string>; export declare function logBasket(basket: TableOutputBasket): string; export interface CreateTestWalletArgs { chain: Chain; rootKeyHex: string; filePath: string; addLocalBackup?: boolean; setActiveClient?: boolean; useMySQLConnectionForClient?: boolean; } //# sourceMappingURL=TestUtilsWalletStorage.d.ts.map