UNPKG

wallet-storage

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

512 lines 27.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const sdk_1 = require("@bsv/sdk"); const index_all_1 = require("../../../src/index.all"); const TestUtilsWalletStorage_1 = require("../../utils/TestUtilsWalletStorage"); describe('internalizeAction tests', () => { jest.setTimeout(99999999); const env = TestUtilsWalletStorage_1._tu.getEnv('test'); const gctxs = []; const useSharedCtxs = true; beforeAll(async () => { if (!env.noMySQL) gctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeActionTests')); gctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeActionTests')); }); afterAll(async () => { for (const ctx of gctxs) { await ctx.storage.destroy(); } }); test('0 invalid params', async () => { for (const { wallet } of gctxs) { const beef0 = new sdk_1.Beef(); const beef1 = new sdk_1.Beef(); beef1.mergeTxidOnly('1'.repeat(64)); const invalidArgs = [ { tx: [], outputs: [], description: '' }, { tx: [], outputs: [], description: '12345' }, { tx: beef0.toBinary(), outputs: [], description: '12345' }, { tx: beef1.toBinary(), outputs: [], description: '12345' } // Oh so many things to test... ]; for (const args of invalidArgs) { await (0, TestUtilsWalletStorage_1.expectToThrowWERR)(index_all_1.sdk.WERR_INVALID_PARAMETER, () => wallet.internalizeAction(args)); } } }); test('1_internalize custom output in receiving wallet with checks', async () => { const ctxs = []; if (useSharedCtxs) ctxs.push(...gctxs); else { if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeAction1Tests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeAction1Tests')); } for (const { wallet } of ctxs) { const root = '02135476'; const kp = TestUtilsWalletStorage_1._tu.getKeyPair(root.repeat(8)); const fredsAddress = kp.address; const outputSatoshis = 4; { const createArgs = { description: `${kp.address} of ${root}`, outputs: [{ satoshis: outputSatoshis, lockingScript: TestUtilsWalletStorage_1._tu.getLockP2PKH(fredsAddress).toHex(), outputDescription: 'pay fred' }], options: { returnTXIDOnly: false, randomizeOutputs: false, signAndProcess: true, noSend: true } }; // This createAction creates a new P2PKH output of 4 satoshis for Fred using his publish payment address... old school. const cr = await wallet.createAction(createArgs); expect(cr.tx).toBeTruthy(); // Fred's new wallet (context) const fred = await TestUtilsWalletStorage_1._tu.createSQLiteTestWallet({ chain: 'test', databaseName: 'internalizeAction1fred', rootKeyHex: '2'.repeat(64), dropAll: true }); // Internalize args to add fred's new output to his own wallet const internalizeArgs = { tx: cr.tx, outputs: [ { outputIndex: 0, protocol: 'basket insertion', insertionRemittance: { basket: 'payments', customInstructions: JSON.stringify({ root, repeat: 8 }), tags: ['test', 'again'] } } ], description: 'got paid!' }; // And do it... const ir = await fred.wallet.internalizeAction(internalizeArgs); expect(ir.accepted).toBe(true); const ro = await fred.activeStorage.findOutputs({ partial: { outputId: 1 } }); expect(ro[0].basketId).toBe(2); // Basket can't be default basket so basketId must be 2 expect(ro[0].satoshis).toBe(outputSatoshis); // Validate custom instructions and tags expect(ro[0].customInstructions).toBe(JSON.stringify({ root, repeat: 8 })); const rtm = await fred.activeStorage.findOutputTagMaps({ partial: { outputId: 1 } }); const rt1 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[0].outputTagId } }); expect(rt1[0].tag).toBe('test'); const rt2 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[1].outputTagId } }); expect(rt2[0].tag).toBe('again'); // Check that calling again does not throw an error const r = await fred.wallet.internalizeAction(internalizeArgs); await expect(Promise.resolve(r)).resolves.toBeTruthy(); // Cleanup Fred's storage await fred.activeStorage.destroy(); } } if (!useSharedCtxs) { for (const ctx of ctxs) { await ctx.storage.destroy(); } } }); test('2_internalize 2 custom outputs in receiving wallet with checks', async () => { const ctxs = []; if (useSharedCtxs) ctxs.push(...gctxs); else { if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeAction2Tests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeAction2Tests')); } for (const { wallet } of ctxs) { const root = '02135476'; const kp = TestUtilsWalletStorage_1._tu.getKeyPair(root.repeat(8)); const fredsAddress = kp.address; const outputSatoshis1 = 4; const outputSatoshis2 = 5; { const createArgs = { description: `${kp.address} of ${root}`, outputs: [ { satoshis: outputSatoshis1, lockingScript: TestUtilsWalletStorage_1._tu.getLockP2PKH(fredsAddress).toHex(), outputDescription: 'pay fred 1st payment' }, { satoshis: outputSatoshis2, lockingScript: TestUtilsWalletStorage_1._tu.getLockP2PKH(fredsAddress).toHex(), outputDescription: 'pay fred 2nd payment' } ], options: { returnTXIDOnly: false, randomizeOutputs: false, signAndProcess: true, noSend: true } }; // This createAction creates a new P2PKH output of 4 and 5 satoshis for Fred using his publish payment address... old school. const cr = await wallet.createAction(createArgs); expect(cr.tx).toBeTruthy(); // Fred's new wallet (context) const fred = await TestUtilsWalletStorage_1._tu.createSQLiteTestWallet({ chain: 'test', databaseName: 'internalizeAction2fred', rootKeyHex: '2'.repeat(64), dropAll: true }); // Internalize args to add fred's new output to his own wallet const internalizeArgs = { tx: cr.tx, outputs: [ { outputIndex: 0, protocol: 'basket insertion', insertionRemittance: { basket: 'payments', customInstructions: JSON.stringify({ root, repeat: 8 }), tags: ['2 tests', 'test 1'] } }, { outputIndex: 1, protocol: 'basket insertion', insertionRemittance: { basket: 'payments', customInstructions: JSON.stringify({ root, repeat: 8 }), tags: ['2 tests', 'test 2'] } } ], description: 'got paid twice!' }; // And do it... const ir = await fred.wallet.internalizeAction(internalizeArgs); expect(ir.accepted).toBe(true); { const ro = await fred.activeStorage.findOutputs({ partial: { outputId: 1 } }); expect(ro[0].basketId).toBe(2); expect(ro[0].satoshis).toBe(outputSatoshis1); // Validate custom instructions and tags expect(ro[0].customInstructions).toBe(JSON.stringify({ root, repeat: 8 })); const rtm = await fred.activeStorage.findOutputTagMaps({ partial: { outputId: 1 } }); const rt1 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[0].outputTagId } }); expect(rt1[0].tag).toBe('2 tests'); const rt2 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[1].outputTagId } }); expect(rt2[0].tag).toBe('test 1'); } { const ro = await fred.activeStorage.findOutputs({ partial: { outputId: 2 } }); expect(ro[0].basketId).toBe(2); expect(ro[0].satoshis).toBe(outputSatoshis2); expect(ro[0].customInstructions).toBe(JSON.stringify({ root, repeat: 8 })); const rtm = await fred.activeStorage.findOutputTagMaps({ partial: { outputId: 2 } }); const rt1 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[0].outputTagId } }); expect(rt1[0].tag).toBe('2 tests'); const rt2 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[1].outputTagId } }); expect(rt2[0].tag).toBe('test 2'); } // Check that calling again does not throw an error const r = await fred.wallet.internalizeAction(internalizeArgs); await expect(Promise.resolve(r)).resolves.toBeTruthy(); await fred.activeStorage.destroy(); } } if (!useSharedCtxs) { for (const ctx of ctxs) { await ctx.storage.destroy(); } } }); test('3_internalize wallet payment in receiving wallet with checks', async () => { const ctxs = []; if (useSharedCtxs) ctxs.push(...gctxs); else { if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeAction3Tests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeAction3Tests')); } for (const { wallet, identityKey: senderIdentityKey } of ctxs) { const fred = await TestUtilsWalletStorage_1._tu.createSQLiteTestWallet({ chain: 'test', databaseName: 'internalizeAction3fred', rootKeyHex: '2'.repeat(64), dropAll: true }); const outputSatoshis = 5; const derivationPrefix = Buffer.from('invoice-12345').toString('base64'); const derivationSuffix = Buffer.from('utxo-0').toString('base64'); const brc29ProtocolID = [2, '3241645161d8']; const derivedPublicKey = wallet.keyDeriver.derivePublicKey(brc29ProtocolID, `${derivationPrefix} ${derivationSuffix}`, fred.identityKey); const derivedAddress = derivedPublicKey.toAddress(); { const createArgs = { description: `description BRC-29`, outputs: [ { satoshis: outputSatoshis, lockingScript: new sdk_1.P2PKH().lock(derivedAddress).toHex(), outputDescription: 'pay fred BRC-29' } ], options: { returnTXIDOnly: false, randomizeOutputs: false, signAndProcess: true, noSend: true } }; const cr = await wallet.createAction(createArgs); expect(cr.tx).toBeTruthy(); const internalizeArgs = { tx: cr.tx, outputs: [ { outputIndex: 0, protocol: 'wallet payment', paymentRemittance: { derivationPrefix: derivationPrefix, derivationSuffix: derivationSuffix, senderIdentityKey: senderIdentityKey } } ], description: 'received BRC-29 payment!' }; const ir = await fred.wallet.internalizeAction(internalizeArgs); expect(ir.accepted).toBe(true); const rfbs = await fred.activeStorage.findOutputBaskets({ partial: { name: 'default' } }); expect(rfbs.length).toBe(1); const rfos = await fred.activeStorage.findOutputs({ partial: { basketId: rfbs[0].basketId } }); expect(rfos.length).toBe(1); expect(rfos[0].satoshis).toBe(outputSatoshis); expect(rfos[0].type).toBe('P2PKH'); expect(rfos[0].purpose).toBe('change'); const r = await fred.wallet.internalizeAction(internalizeArgs); await expect(Promise.resolve(r)).resolves.toBeTruthy(); await fred.activeStorage.destroy(); } } if (!useSharedCtxs) { for (const ctx of ctxs) { await ctx.storage.destroy(); } } }); test('4_internalize 2 wallet payments in receiving wallet with checks', async () => { const ctxs = []; if (useSharedCtxs) ctxs.push(...gctxs); else { if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeAction4Tests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeAction4Tests')); } for (const { wallet, identityKey: senderIdentityKey } of ctxs) { const fred = await TestUtilsWalletStorage_1._tu.createSQLiteTestWallet({ chain: 'test', databaseName: 'internalizeAction4fred', rootKeyHex: '2'.repeat(64), dropAll: true }); const brc29ProtocolID = [2, '3241645161d8']; const outputSatoshis1 = 6; const derivationPrefix = Buffer.from('invoice-12345').toString('base64'); const derivationSuffix1 = Buffer.from('utxo-1').toString('base64'); const derivedPublicKey1 = wallet.keyDeriver.derivePublicKey(brc29ProtocolID, `${derivationPrefix} ${derivationSuffix1}`, fred.identityKey); const derivedAddress1 = derivedPublicKey1.toAddress(); const outputSatoshis2 = 7; const derivationSuffix2 = Buffer.from('utxo-2').toString('base64'); const derivedPublicKey2 = wallet.keyDeriver.derivePublicKey(brc29ProtocolID, `${derivationPrefix} ${derivationSuffix2}`, fred.identityKey); const derivedAddress2 = derivedPublicKey2.toAddress(); { const createArgs = { description: `BRC-29 payments from other wallet`, outputs: [ { satoshis: outputSatoshis1, lockingScript: new sdk_1.P2PKH().lock(derivedAddress1).toHex(), outputDescription: 'pay fred 1st BRC-29 payment' }, { satoshis: outputSatoshis2, lockingScript: new sdk_1.P2PKH().lock(derivedAddress2).toHex(), outputDescription: 'pay fred 2nd BRC-29 payment' } ], options: { returnTXIDOnly: false, randomizeOutputs: false, signAndProcess: true, noSend: true } }; const cr = await wallet.createAction(createArgs); expect(cr.tx).toBeTruthy(); const internalizeArgs = { tx: cr.tx, outputs: [ { outputIndex: 0, protocol: 'wallet payment', paymentRemittance: { derivationPrefix: derivationPrefix, derivationSuffix: derivationSuffix1, senderIdentityKey: senderIdentityKey } }, { outputIndex: 1, protocol: 'wallet payment', paymentRemittance: { derivationPrefix: derivationPrefix, derivationSuffix: derivationSuffix2, senderIdentityKey: senderIdentityKey } } ], description: 'received pair of BRC-29 payments!' }; const ir = await fred.wallet.internalizeAction(internalizeArgs); expect(ir.accepted).toBe(true); const rfbs = await fred.activeStorage.findOutputBaskets({ partial: { name: 'default' } }); expect(rfbs.length).toBe(1); const rfos = await fred.activeStorage.findOutputs({ partial: { basketId: rfbs[0].basketId } }); expect(rfos.length).toBe(2); expect(rfos[0].satoshis).toBe(outputSatoshis1); expect(rfos[0].type).toBe('P2PKH'); expect(rfos[0].purpose).toBe('change'); expect(rfos[1].satoshis).toBe(outputSatoshis2); expect(rfos[1].type).toBe('P2PKH'); expect(rfos[1].purpose).toBe('change'); const r = await fred.wallet.internalizeAction(internalizeArgs); await expect(Promise.resolve(r)).resolves.toBeTruthy(); await fred.activeStorage.destroy(); } } if (!useSharedCtxs) { for (const ctx of ctxs) { await ctx.storage.destroy(); } } }); test('5_internalize 2 wallet payments and 2 basket insertions in receiving wallet with checks', async () => { const ctxs = []; if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('actionInternalizeAction5Tests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('actionInternalizeAction5Tests')); for (const { wallet, identityKey: senderIdentityKey } of ctxs) { const fred = await TestUtilsWalletStorage_1._tu.createSQLiteTestWallet({ chain: 'test', databaseName: 'internalizeAction5fred', rootKeyHex: '2'.repeat(64), dropAll: true }); const brc29ProtocolID = [2, '3241645161d8']; const outputSatoshis1 = 8; const derivationPrefix = Buffer.from('invoice-12345').toString('base64'); const derivationSuffix1 = Buffer.from('utxo-1').toString('base64'); const derivedPublicKey1 = wallet.keyDeriver.derivePublicKey(brc29ProtocolID, `${derivationPrefix} ${derivationSuffix1}`, fred.identityKey); const derivedAddress1 = derivedPublicKey1.toAddress(); const outputSatoshis2 = 9; const derivationSuffix2 = Buffer.from('utxo-2').toString('base64'); const derivedPublicKey2 = wallet.keyDeriver.derivePublicKey(brc29ProtocolID, `${derivationPrefix} ${derivationSuffix2}`, fred.identityKey); const derivedAddress2 = derivedPublicKey2.toAddress(); const root = '02135476'; const kp = TestUtilsWalletStorage_1._tu.getKeyPair(root.repeat(8)); const fredsAddress = kp.address; const outputSatoshis3 = 10; const outputSatoshis4 = 11; { const createArgs = { description: `BRC-29 payments from other wallet`, outputs: [ { satoshis: outputSatoshis1, lockingScript: new sdk_1.P2PKH().lock(derivedAddress1).toHex(), outputDescription: 'pay fred 1st BRC-29 payment' }, { satoshis: outputSatoshis2, lockingScript: new sdk_1.P2PKH().lock(derivedAddress2).toHex(), outputDescription: 'pay fred 2nd BRC-29 payment' }, { satoshis: outputSatoshis3, lockingScript: TestUtilsWalletStorage_1._tu.getLockP2PKH(fredsAddress).toHex(), outputDescription: 'pay fred 3rd payment' }, { satoshis: outputSatoshis4, lockingScript: TestUtilsWalletStorage_1._tu.getLockP2PKH(fredsAddress).toHex(), outputDescription: 'pay fred 4th payment' } ], options: { returnTXIDOnly: false, randomizeOutputs: false, signAndProcess: true, noSend: true } }; const cr = await wallet.createAction(createArgs); expect(cr.tx).toBeTruthy(); const internalizeArgs = { tx: cr.tx, outputs: [ { outputIndex: 0, protocol: 'wallet payment', paymentRemittance: { derivationPrefix: derivationPrefix, derivationSuffix: derivationSuffix1, senderIdentityKey: senderIdentityKey } }, { outputIndex: 1, protocol: 'wallet payment', paymentRemittance: { derivationPrefix: derivationPrefix, derivationSuffix: derivationSuffix2, senderIdentityKey: senderIdentityKey } }, { outputIndex: 2, protocol: 'basket insertion', insertionRemittance: { basket: 'payments', customInstructions: `3rd payment ${JSON.stringify({ root, repeat: 8 })}`, tags: ['basket payments', '1st basket payment'] } }, { outputIndex: 3, protocol: 'basket insertion', insertionRemittance: { basket: 'payments', customInstructions: `4th payment ${JSON.stringify({ root, repeat: 8 })}`, tags: ['basket payments', '2nd basket payment'] } } ], description: 'received 2 BRC-29 pay and 2 basket ins!' }; const ir = await fred.wallet.internalizeAction(internalizeArgs); expect(ir.accepted).toBe(true); const rfbs = await fred.activeStorage.findOutputBaskets({ partial: { name: 'default' } }); expect(rfbs.length).toBe(1); const rfos = await fred.activeStorage.findOutputs({ partial: { basketId: rfbs[0].basketId } }); expect(rfos.length).toBe(2); expect(rfos[0].satoshis).toBe(outputSatoshis1); expect(rfos[0].type).toBe('P2PKH'); expect(rfos[0].purpose).toBe('change'); expect(rfos[1].satoshis).toBe(outputSatoshis2); expect(rfos[1].type).toBe('P2PKH'); expect(rfos[1].purpose).toBe('change'); { const ro = await fred.activeStorage.findOutputs({ partial: { outputId: 3 } }); expect(ro[0].basketId).toBe(2); expect(ro[0].satoshis).toBe(outputSatoshis3); expect(ro[0].customInstructions).toBe(`3rd payment ${JSON.stringify({ root, repeat: 8 })}`); const rtm = await fred.activeStorage.findOutputTagMaps({ partial: { outputId: 3 } }); const rt1 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[0].outputTagId } }); expect(rt1[0].tag).toBe('basket payments'); const rt2 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[1].outputTagId } }); expect(rt2[0].tag).toBe('1st basket payment'); } { const ro = await fred.activeStorage.findOutputs({ partial: { outputId: 4 } }); expect(ro[0].basketId).toBe(2); expect(ro[0].satoshis).toBe(outputSatoshis4); expect(ro[0].customInstructions).toBe(`4th payment ${JSON.stringify({ root, repeat: 8 })}`); const rtm = await fred.activeStorage.findOutputTagMaps({ partial: { outputId: 4 } }); const rt1 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[0].outputTagId } }); expect(rt1[0].tag).toBe('basket payments'); const rt2 = await fred.activeStorage.findOutputTags({ partial: { outputTagId: rtm[1].outputTagId } }); expect(rt2[0].tag).toBe('2nd basket payment'); } const r = await fred.wallet.internalizeAction(internalizeArgs); await expect(Promise.resolve(r)).resolves.toBeTruthy(); await fred.activeStorage.destroy(); } } for (const ctx of ctxs) { await ctx.storage.destroy(); } }); }); //# sourceMappingURL=internalizeAction.test.js.map