UNPKG

@bsv/wallet-toolbox

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

474 lines 20.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const bsv = __importStar(require("@bsv/sdk")); const src_1 = require("../../../../../src"); const TestUtilsWalletStorage_1 = require("../../../../../test/utils/TestUtilsWalletStorage"); const EntityProvenTx_1 = require("../EntityProvenTx"); describe('ProvenTx class method tests', () => { jest.setTimeout(99999999); const env = TestUtilsWalletStorage_1.TestUtilsWalletStorage.getEnv('test'); const ctxs = []; const ctxs2 = []; beforeAll(async () => { if (env.runMySQL) { ctxs.push(await TestUtilsWalletStorage_1.TestUtilsWalletStorage.createLegacyWalletMySQLCopy('ProvenTxTests')); ctxs2.push(await TestUtilsWalletStorage_1.TestUtilsWalletStorage.createLegacyWalletMySQLCopy('ProvenTxTests2')); } ctxs.push(await TestUtilsWalletStorage_1.TestUtilsWalletStorage.createLegacyWalletSQLiteCopy('ProvenTxTests')); ctxs2.push(await TestUtilsWalletStorage_1.TestUtilsWalletStorage.createLegacyWalletSQLiteCopy('ProvenTxTests2')); }); afterAll(async () => { for (const ctx of ctxs) { await ctx.storage.destroy(); } for (const ctx of ctxs2) { await ctx.storage.destroy(); } }); test('0_fromTxid: valid txid with rawTx and Merkle proof (real database)', async () => { var _a, _b, _c, _d, _e; const ctx = ctxs[0]; const txid = '2795b293c698b2244147aaba745db887a632d21990c474df46d842ec3e52f122'; // Using a valid txid from the table // Fetch the rawTx and Merkle proof data directly from the database const provenTxRecord = await ctx.activeStorage.findProvenTxs({ partial: { txid } }); expect(provenTxRecord.length).toBeGreaterThan(0); // Ensure the record exists in the database const rawTx = (_a = provenTxRecord[0]) === null || _a === void 0 ? void 0 : _a.rawTx; const height = (_b = provenTxRecord[0]) === null || _b === void 0 ? void 0 : _b.height; const blockHash = (_c = provenTxRecord[0]) === null || _c === void 0 ? void 0 : _c.blockHash; const merkleRoot = (_d = provenTxRecord[0]) === null || _d === void 0 ? void 0 : _d.merkleRoot; const merklePathBinary = ((_e = provenTxRecord[0]) === null || _e === void 0 ? void 0 : _e.merklePath) || []; const services = { chain: 'test', hashOutputScript: (script) => { const hash = bsv.Utils.toHex((0, src_1.sha256Hash)(bsv.Utils.toArray(script, 'hex'))); return hash; }, getRawTx: async (requestedTxid) => { if (requestedTxid === txid) { return { txid: requestedTxid, rawTx }; } throw new Error('Unexpected txid'); }, getMerklePath: async (requestedTxid) => { if (requestedTxid === txid) { return { merklePath: { path: [[{ hash: txid, offset: 0 }]], blockHeight: height, toBinary: () => merklePathBinary, computeRoot: () => merkleRoot, verifyProof: () => true, toHex: () => Buffer.from(merklePathBinary).toString('hex'), indexOf: () => 0, findOrComputeLeaf: () => ({ hash: txid, offset: 0 }), verify: () => true, combine: () => ({}), trim: () => ({}) }, header: { version: 1, previousHash: 'prev-hash', merkleRoot: merkleRoot, time: 1610000000, bits: 123456, nonce: 78910, height: height, hash: blockHash }, name: 'mock-service' }; } throw new Error('Unexpected txid'); }, getChainTracker: () => Promise.resolve({ isValidRootForHeight: async (root, height) => true, currentHeight: async () => height }), getHeaderForHeight: async () => Promise.resolve([1, 2, 3, 4]), getHeight: async () => height, getBsvExchangeRate: async () => 0, getFiatExchangeRate: async () => 1, postBeef: async () => [], getStatusForTxids: async () => ({ name: 'mock-service', status: 'success', results: [] }), isUtxo: async () => true, getUtxoStatus: async () => ({ name: 'mock-service', status: 'success', isUtxo: true, details: [] }), getScriptHashHistory: async () => ({ name: 'mock-service', status: 'success', history: [] }), hashToHeader: async () => ({ version: 1, previousHash: 'prev-hash', merkleRoot: merkleRoot, time: 1610000000, bits: 123456, nonce: 78910, height: height, hash: blockHash }), nLockTimeIsFinal: async () => true, getBeefForTxid: async () => new bsv.Beef(), getServicesCallHistory: () => ({ version: 1, getMerklePath: { serviceName: '', historyByProvider: {} }, getRawTx: { serviceName: '', historyByProvider: {} }, postBeef: { serviceName: '', historyByProvider: {} }, getStatusForTxids: { serviceName: '', historyByProvider: {} }, getUtxoStatus: { serviceName: '', historyByProvider: {} }, getScriptHashHistory: { serviceName: '', historyByProvider: {} }, updateFiatExchangeRates: { serviceName: '', historyByProvider: {} } }) }; // Call the method under test const result = await EntityProvenTx_1.EntityProvenTx.fromTxid(txid, services); // Validate the ProvenTx result expect(result.proven).toBeDefined(); expect(result.proven.txid).toBe(txid); // Validate Merkle proof details expect(result.proven.height).toBe(height); expect(Buffer.from(result.proven.merklePath).toString('hex')).toEqual(Buffer.from(merklePathBinary).toString('hex')); expect(result.proven.blockHash).toBe(blockHash); expect(result.proven.merkleRoot).toBe(merkleRoot); expect(result.rawTx).toEqual(rawTx); }); test('1_fromTxid: txid with no rawTx available', async () => { if (TestUtilsWalletStorage_1.TestUtilsWalletStorage.noEnv('test')) return; const ctx = ctxs[0]; const txid = 'missing-txid'; const services = ctx.services; // Call the method under test const result = await EntityProvenTx_1.EntityProvenTx.fromTxid(txid, services); // Validate that ProvenTx could not be created expect(result.proven).toBeUndefined(); expect(result.rawTx).toBeUndefined(); }); test('2_fromTxid: txid with no Merkle proof available', async () => { if (TestUtilsWalletStorage_1.TestUtilsWalletStorage.noEnv('test')) return; const ctx = ctxs[0]; const txid = 'no-merkle-proof-txid'; const services = ctx.services; // Verify the rawTx and Merkle proof const rawTx = await services.getRawTx(txid); const merkleProof = await services.getMerklePath(txid); // Call the method under test const result = await EntityProvenTx_1.EntityProvenTx.fromTxid(txid, services); // Validate the ProvenTx result expect(result.proven).toBeUndefined(); expect(result.rawTx).toEqual(rawTx.rawTx); }); test('3_ProvenTx getters and setters', () => { // Mock data to initialize the ProvenTx entity const mockData = { provenTxId: 1, created_at: new Date('2025-01-01T00:00:00Z'), updated_at: new Date('2025-01-02T00:00:00Z'), txid: '2795b293c698b2244147aaba745db887a632d21990c474df46d842ec3e52f122', height: 123, index: 0, merklePath: [0x04, 0x05, 0x06], rawTx: [0x01, 0x02, 0x03], blockHash: 'mock-block-hash', merkleRoot: 'mock-merkle-root' }; // Initialize the ProvenTx entity with mock data const provenTx = new EntityProvenTx_1.EntityProvenTx(mockData); // Validate getters expect(provenTx.provenTxId).toBe(mockData.provenTxId); expect(provenTx.created_at).toEqual(mockData.created_at); expect(provenTx.updated_at).toEqual(mockData.updated_at); expect(provenTx.txid).toBe(mockData.txid); expect(provenTx.height).toBe(mockData.height); expect(provenTx.index).toBe(mockData.index); expect(provenTx.merklePath).toEqual(mockData.merklePath); expect(provenTx.rawTx).toEqual(mockData.rawTx); expect(provenTx.blockHash).toBe(mockData.blockHash); expect(provenTx.merkleRoot).toBe(mockData.merkleRoot); // Validate setters provenTx.provenTxId = 2; provenTx.created_at = new Date('2025-02-01T00:00:00Z'); provenTx.updated_at = new Date('2025-02-02T00:00:00Z'); provenTx.txid = 'a3b2f0935c7b5bb7a841a09e535c13be86f4df0e7a91cebdc33812bfcc0eb9d7'; provenTx.height = 456; provenTx.index = 1; provenTx.merklePath = [0x07, 0x08, 0x09]; provenTx.rawTx = [0x0a, 0x0b, 0x0c]; provenTx.blockHash = 'new-block-hash'; provenTx.merkleRoot = 'new-merkle-root'; // Validate updated values expect(provenTx.provenTxId).toBe(2); expect(provenTx.created_at).toEqual(new Date('2025-02-01T00:00:00Z')); expect(provenTx.updated_at).toEqual(new Date('2025-02-02T00:00:00Z')); expect(provenTx.txid).toBe('a3b2f0935c7b5bb7a841a09e535c13be86f4df0e7a91cebdc33812bfcc0eb9d7'); expect(provenTx.height).toBe(456); expect(provenTx.index).toBe(1); expect(provenTx.merklePath).toEqual([0x07, 0x08, 0x09]); expect(provenTx.rawTx).toEqual([0x0a, 0x0b, 0x0c]); expect(provenTx.blockHash).toBe('new-block-hash'); expect(provenTx.merkleRoot).toBe('new-merkle-root'); // Validate overridden methods expect(provenTx.id).toBe(2); expect(provenTx.entityName).toBe('provenTx'); expect(provenTx.entityTable).toBe('proven_txs'); // Update id via overridden setter provenTx.id = 3; expect(provenTx.provenTxId).toBe(3); }); // Test: equals identifies matching ProvenTx entities test('4_equals: identifies matching ProvenTx entities', async () => { const ctx1 = ctxs[0]; const ctx2 = ctxs2[0]; // Insert a ProvenTx into the first database const provenTx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 401, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); await ctx1.activeStorage.insertProvenTx(provenTx1.toApi()); // Insert a matching ProvenTx into the second database const provenTx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 401, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); await ctx2.activeStorage.insertProvenTx(provenTx2.toApi()); const syncMap = (0, src_1.createSyncMap)(); syncMap.provenTx.idMap = { [provenTx1.provenTxId]: provenTx2.provenTxId }; // Verify the ProvenTx entities match expect(provenTx1.equals(provenTx2.toApi(), syncMap)).toBe(true); }); // Test: equals identifies non-matching txid test('5_equals: identifies non-matching txid', async () => { const provenTx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 102, txid: 'txid1', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); const provenTx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 103, txid: 'txid2', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); expect(provenTx1.equals(provenTx2.toApi())).toBe(false); }); // Test: equals identifies non-matching height test('6_equals: identifies non-matching height', async () => { const provenTx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 104, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); const provenTx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 105, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588741, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); expect(provenTx1.equals(provenTx2.toApi())).toBe(false); }); // Test: equals identifies non-matching merklePath test('7_equals: identifies non-matching merklePath', async () => { const provenTx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 106, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); const provenTx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 107, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 4], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); expect(provenTx1.equals(provenTx2.toApi())).toBe(false); }); // Test: equals identifies non-matching syncMap test('8_equals: identifies non-matching syncMap', async () => { const provenTx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 108, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); const provenTx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 109, txid: 'valid-txid', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 1588740, index: 0, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash', merkleRoot: 'merkle-root' }); const syncMap = (0, src_1.createSyncMap)(); syncMap.provenTx.idMap = { 108: 999 }; expect(provenTx1.equals(provenTx2.toApi(), syncMap)).toBe(false); }); test('9_equals: provenTxId mismatch without syncMap', async () => { const ctx1 = ctxs[0]; const ctx2 = ctxs2[0]; // Insert a ProvenTx record into the first database const tx1 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 405, txid: 'txid1', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 100, index: 1, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash-1', merkleRoot: 'merkle-root-1' }); await ctx1.activeStorage.insertProvenTx(tx1.toApi()); // Insert a different ProvenTx record into the second database with a mismatched provenTxId const tx2 = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 406, txid: 'txid1', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 100, index: 1, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash-1', merkleRoot: 'merkle-root-1' }); await ctx2.activeStorage.insertProvenTx(tx2.toApi()); // Verify that `equals` returns false because provenTxId mismatch without a syncMap expect(tx1.equals(tx2.toApi())).toBe(false); }); test('10_mergeExisting: always returns false', async () => { const ctx = ctxs[0]; // Create a ProvenTx entity const provenTx = new EntityProvenTx_1.EntityProvenTx({ provenTxId: 101, txid: 'txid1', created_at: new Date('2023-01-01'), updated_at: new Date('2023-01-02'), height: 100, index: 1, merklePath: [1, 2, 3], rawTx: [4, 5, 6], blockHash: 'block-hash-1', merkleRoot: 'merkle-root-1' }); const mockSyncMap = (0, src_1.createSyncMap)(); mockSyncMap.provenTx.idMap = { [provenTx.provenTxId]: provenTx.provenTxId }; // Create mock storage, syncMap, and trx token const mockStorage = ctx.activeStorage; const mockTrx = {}; // Call the mergeExisting method const result = await provenTx.mergeExisting(mockStorage, new Date(), provenTx.toApi(), mockSyncMap, mockTrx); // Assert that it always returns false expect(result).toBe(false); }); }); //# sourceMappingURL=ProvenTxTests.test.js.map