@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
474 lines • 20.8 kB
JavaScript
"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