wallet-storage
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
512 lines • 27.8 kB
JavaScript
"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