wallet-storage
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
400 lines (399 loc) • 20.9 kB
TypeScript
import { CreateActionResult, HexString, KeyDeriver, PrivateKey, PublicKey, SignActionResult, WalletAction, WalletActionInput, WalletActionOutput, WalletCertificate, WalletInterface } from '@bsv/sdk';
import { sdk, StorageProvider, StorageKnex, table, Wallet, Monitor, Services, WalletStorageManager } from '../../src/index.all';
import { Knex } from 'knex';
import { Beef } from '@bsv/sdk';
export interface TuEnv {
chain: sdk.Chain;
userId: number;
identityKey: string;
mainTaalApiKey: string;
testTaalApiKey: string;
devKeys: Record<string, string>;
noMySQL: boolean;
runSlowTests: boolean;
logTests: boolean;
}
export declare abstract class TestUtilsWalletStorage {
static getEnv(chain: sdk.Chain): {
chain: sdk.Chain;
userId: number;
identityKey: string;
identityKey2: string | undefined;
mainTaalApiKey: string;
testTaalApiKey: string;
devKeys: Record<string, string>;
noMySQL: boolean;
runSlowTests: boolean;
logTests: boolean;
};
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: import("@bsv/sdk").Transaction, inputIndex: number) => Promise<import("@bsv/sdk").UnlockingScript>;
estimateLength: () => Promise<108>;
};
static createWalletOnly(args: {
chain?: sdk.Chain;
rootKeyHex?: string;
active?: sdk.WalletStorageProvider;
backups?: sdk.WalletStorageProvider[];
privKeyHex?: string;
}): Promise<TestWalletOnly>;
static createTestWalletWithStorageClient(args: {
rootKeyHex?: string;
endpointUrl?: string;
chain?: sdk.Chain;
}): Promise<TestWalletOnly>;
static createKnexTestWalletWithSetup<T>(args: {
knex: Knex<any, any[]>;
databaseName: string;
chain?: sdk.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?: sdk.Chain;
rootKeyHex?: string;
dropAll?: boolean;
}): Promise<TestWallet<{}>>;
static createMySQLTestSetup1Wallet(args: {
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
}): Promise<TestWallet<TestSetup1>>;
static createSQLiteTestWallet(args: {
filePath?: string;
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
dropAll?: boolean;
privKeyHex?: string;
}): Promise<TestWalletNoSetup>;
static createSQLiteTestSetup1Wallet(args: {
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
}): Promise<TestWallet<TestSetup1>>;
static createSQLiteTestSetup2Wallet(args: {
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
}): Promise<TestWallet<TestSetup2>>;
static createKnexTestWallet(args: {
knex: Knex<any, any[]>;
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
dropAll?: boolean;
privKeyHex?: string;
}): Promise<TestWalletNoSetup>;
static createKnexTestSetup1Wallet(args: {
knex: Knex<any, any[]>;
databaseName: string;
chain?: sdk.Chain;
rootKeyHex?: string;
dropAll?: boolean;
}): Promise<TestWallet<TestSetup1>>;
static createKnexTestSetup2Wallet(args: {
knex: Knex<any, any[]>;
databaseName: string;
chain?: sdk.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 makeSampleCert(subject?: string): {
cert: WalletCertificate;
subject: string;
certifier: PrivateKey;
};
static insertTestProvenTx(storage: StorageProvider, txid?: string): Promise<table.ProvenTx>;
static insertTestProvenTxReq(storage: StorageProvider, txid?: string, provenTxId?: number, onlyRequired?: boolean): Promise<table.ProvenTxReq>;
static insertTestUser(storage: StorageProvider, identityKey?: string): Promise<table.User>;
static insertTestCertificate(storage: StorageProvider, u?: table.User): Promise<table.Certificate>;
static insertTestCertificateField(storage: StorageProvider, c: table.Certificate, name: string, value: string): Promise<table.CertificateField>;
static insertTestOutputBasket(storage: StorageProvider, u?: table.User | number, partial?: Partial<table.OutputBasket>): Promise<table.OutputBasket>;
static insertTestTransaction(storage: StorageProvider, u?: table.User, onlyRequired?: boolean, partial?: Partial<table.Transaction>): Promise<{
tx: table.Transaction;
user: table.User;
}>;
static insertTestOutput(storage: StorageProvider, t: table.Transaction, vout: number, satoshis: number, basket?: table.OutputBasket, requiredOnly?: boolean, partial?: Partial<table.Output>): Promise<table.Output>;
static insertTestOutputTag(storage: StorageProvider, u: table.User, partial?: Partial<table.OutputTag>): Promise<table.OutputTag>;
static insertTestOutputTagMap(storage: StorageProvider, o: table.Output, tag: table.OutputTag): Promise<table.OutputTagMap>;
static insertTestTxLabel(storage: StorageProvider, u: table.User, partial?: Partial<table.TxLabel>): Promise<table.TxLabel>;
static insertTestTxLabelMap(storage: StorageProvider, tx: table.Transaction, label: table.TxLabel, partial?: Partial<table.TxLabelMap>): Promise<table.TxLabelMap>;
static insertTestSyncState(storage: StorageProvider, u: table.User): Promise<table.SyncState>;
static insertTestMonitorEvent(storage: StorageProvider): Promise<table.MonitorEvent>;
static insertTestCommission(storage: StorageProvider, t: table.Transaction): Promise<table.Commission>;
static createTestSetup1(storage: StorageProvider, u1IdentityKey?: string): Promise<TestSetup1>;
static createTestSetup2(storage: StorageProvider, u1IdentityKey: 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<sdk.GetMerklePathResult>): void;
}
export declare abstract class _tu extends TestUtilsWalletStorage {
}
export interface TestSetup1 {
u1: table.User;
u1basket1: table.OutputBasket;
u1basket2: table.OutputBasket;
u1label1: table.TxLabel;
u1label2: table.TxLabel;
u1tag1: table.OutputTag;
u1tag2: table.OutputTag;
u1tx1: table.Transaction;
u1comm1: table.Commission;
u1tx1label1: table.TxLabelMap;
u1tx1label2: table.TxLabelMap;
u1tx1o0: table.Output;
u1o0tag1: table.OutputTagMap;
u1o0tag2: table.OutputTagMap;
u1tx1o1: table.Output;
u1o1tag1: table.OutputTagMap;
u1cert1: table.Certificate;
u1cert1field1: table.CertificateField;
u1cert1field2: table.CertificateField;
u1cert2: table.Certificate;
u1cert2field1: table.CertificateField;
u1cert3: table.Certificate;
u1sync1: table.SyncState;
u2: table.User;
u2basket1: table.OutputBasket;
u2label1: table.TxLabel;
u2tx1: table.Transaction;
u2comm1: table.Commission;
u2tx1label1: table.TxLabelMap;
u2tx1o0: table.Output;
u2tx2: table.Transaction;
u2comm2: table.Commission;
proven1: table.ProvenTx;
req1: table.ProvenTxReq;
req2: table.ProvenTxReq;
we1: table.MonitorEvent;
}
export interface MockData {
inputs?: WalletActionInput[];
outputs?: WalletActionOutput[];
actions: WalletAction[];
}
export interface TestSetup2 {
}
export interface TestWallet<T> extends TestWalletOnly {
activeStorage: StorageKnex;
setup?: T;
userId: number;
rootKey: PrivateKey;
identityKey: string;
keyDeriver: KeyDeriver;
chain: sdk.Chain;
storage: WalletStorageManager;
services: Services;
monitor: Monitor;
wallet: Wallet;
}
export interface TestWalletOnly {
rootKey: PrivateKey;
identityKey: string;
keyDeriver: KeyDeriver;
chain: sdk.Chain;
storage: WalletStorageManager;
services: Services;
monitor: Monitor;
wallet: Wallet;
}
export type TestSetup1Wallet = TestWallet<TestSetup1>;
export type TestWalletNoSetup = TestWallet<{}>;
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 the `logEnabled` flag.
*
* @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 log: (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, 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: table.Output): Promise<string>;
export declare function logBasket(storage: StorageKnex, basket: table.OutputBasket): string;
//# sourceMappingURL=TestUtilsWalletStorage.d.ts.map