@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
305 lines • 11.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSetup = createSetup;
exports.burnOneSatTestOutput = burnOneSatTestOutput;
exports.createOneSatTestOutput = createOneSatTestOutput;
exports.recoverOneSatTestOutputs = recoverOneSatTestOutputs;
exports.trackReqByTxid = trackReqByTxid;
exports.doubleSpendOldChange = doubleSpendOldChange;
exports.createMainReviewSetup = createMainReviewSetup;
const sdk_1 = require("@bsv/sdk");
const src_1 = require("../../src");
const TestUtilsWalletStorage_1 = require("./TestUtilsWalletStorage");
const sdk_2 = require("../../src/sdk");
const createAction_1 = require("../../src/storage/methods/createAction");
async function createSetup(chain, options) {
const env = TestUtilsWalletStorage_1._tu.getEnv(chain);
let identityKey;
let filePath;
if (options.useTestIdentityKey) {
identityKey = env.testIdentityKey;
filePath = env.testFilePath;
}
else {
if (options.useIdentityKey2) {
identityKey = env.identityKey2;
}
else {
identityKey = env.identityKey;
filePath = env.filePath;
}
}
if (!identityKey)
throw new src_1.sdk.WERR_INVALID_PARAMETER('identityKey', 'valid');
if (!filePath)
filePath = `./backup-${chain}-${identityKey}.sqlite`;
const setup = {
...options,
...(await TestUtilsWalletStorage_1._tu.createTestWallet({
chain,
rootKeyHex: env.devKeys[identityKey],
filePath,
setActiveClient: options.setActiveClient,
addLocalBackup: false,
useMySQLConnectionForClient: options.useMySQLConnectionForClient
}))
};
console.log(`ACTIVE STORAGE: ${setup.storage.getActiveStoreName()}`);
return setup;
}
async function burnOneSatTestOutput(setup, options = {}, howMany = 1) {
const outputs = await setup.wallet.listOutputs({
basket: 'test-output',
include: 'entire transactions',
limit: 1000
});
while (howMany-- > 0) {
const o = outputs.outputs.find(o => o.satoshis === 1);
if (!o)
break;
console.log(`burning ${o.outpoint}`);
const inputBEEF = outputs.BEEF;
const p2pkh = new sdk_1.P2PKH();
const args = {
inputBEEF,
inputs: [
{
unlockingScriptLength: 108,
outpoint: o.outpoint,
inputDescription: 'burn 1 sat output'
}
],
description: 'burn output'
};
const bcar = await setup.wallet.createAction(args);
expect(bcar.signableTransaction);
const st = bcar.signableTransaction;
const beef = sdk_1.Beef.fromBinary(st.tx);
const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid);
const unlock = p2pkh.unlock(setup.keyDeriver.rootKey, 'all', false);
const unlockingScript = (await unlock.sign(tx, 0)).toHex();
const signArgs = {
reference: st.reference,
spends: { 0: { unlockingScript } }
};
const sar = await setup.wallet.signAction(signArgs);
console.log(sar.txid);
expect(sar.txid);
}
}
async function createOneSatTestOutput(setup, options = {}, howMany = 1) {
if (howMany < 1)
throw new src_1.sdk.WERR_INVALID_PARAMETER('howMany', 'at least 1');
let car = {};
let noSendChange = undefined;
let txids = [];
let vargs;
for (let i = 0; i < howMany; i++) {
const args = {
outputs: [
{
lockingScript: new sdk_1.P2PKH().lock(sdk_1.PublicKey.fromString(setup.identityKey).toAddress()).toHex(),
satoshis: 1,
outputDescription: 'test output',
customInstructions: JSON.stringify({
type: 'P2PKH',
key: 'identity'
}),
basket: 'test-output'
}
],
description: 'create test output',
options: {
...options,
noSendChange
}
};
vargs = (0, sdk_2.validateCreateActionArgs)(args);
car = await setup.wallet.createAction(args);
expect(car.txid);
txids.push(car.txid);
noSendChange = car.noSendChange;
const req = await src_1.EntityProvenTxReq.fromStorageTxid(setup.activeStorage, car.txid);
expect(req !== undefined && req.history.notes !== undefined);
if (req && req.history.notes) {
if (vargs.isNoSend) {
expect(req.status === 'nosend').toBe(true);
expect(req.history.notes.length).toBe(1);
const n = req.history.notes[0];
expect(n.what === 'status' && n.status_now === 'nosend').toBe(true);
}
else {
expect(req.status === 'unsent').toBe(true);
expect(req.history.notes.length).toBe(1);
const n = req.history.notes[0];
expect(n.what === 'status' && n.status_now === 'unsent').toBe(true);
}
}
}
if (vargs.isNoSend) {
// Create final sending transaction
const args = {
description: 'send batch',
options: {
...options,
sendWith: txids
}
};
vargs = (0, sdk_2.validateCreateActionArgs)(args);
car = await setup.wallet.createAction(args);
}
return car;
}
async function recoverOneSatTestOutputs(setup, testOptionsMode) {
const outputs = await setup.wallet.listOutputs({
basket: 'test-output',
include: 'entire transactions',
limit: 1000
});
if (outputs.outputs.length > 0) {
const args = {
inputBEEF: outputs.BEEF,
inputs: [],
description: 'recover test output'
};
if (testOptionsMode === 1) {
args.options = {
acceptDelayedBroadcast: false
};
}
const p2pkh = new sdk_1.P2PKH();
for (const o of outputs.outputs) {
args.inputs.push({
unlockingScriptLength: 108,
outpoint: o.outpoint,
inputDescription: 'recovered test output'
});
}
const car = await setup.wallet.createAction(args);
expect(car.signableTransaction);
const st = car.signableTransaction;
const beef = sdk_1.Beef.fromBinary(st.tx);
const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid);
const signArgs = {
reference: st.reference,
spends: {} // 0: { unlockingScript } },
};
for (let i = 0; i < outputs.outputs.length; i++) {
const o = outputs.outputs[i];
const unlock = p2pkh.unlock(setup.keyDeriver.rootKey, 'all', false);
const unlockingScript = (await unlock.sign(tx, i)).toHex();
signArgs.spends[i] = { unlockingScript };
}
const sar = await setup.wallet.signAction(signArgs);
expect(sar.txid);
}
}
async function trackReqByTxid(setup, txid) {
var _a;
const req = await src_1.EntityProvenTxReq.fromStorageTxid(setup.activeStorage, txid);
expect(req !== undefined && req.history.notes !== undefined);
if (!req || !req.history.notes)
throw new src_1.sdk.WERR_INTERNAL();
let newBlocks = 0;
let lastHeight;
for (; req.status !== 'completed';) {
let height = (_a = setup.monitor.lastNewHeader) === null || _a === void 0 ? void 0 : _a.height;
if (req.status === 'unsent') {
// send it...
}
if (req.status === 'sending') {
// send it...
}
if (req.status === 'unmined') {
if (height && lastHeight) {
if (height === lastHeight) {
await (0, src_1.wait)(1000 * 60);
}
else {
newBlocks++;
expect(newBlocks < 5);
}
}
}
await setup.monitor.runOnce();
await req.refreshFromStorage(setup.activeStorage);
lastHeight = height;
}
}
/**
* This method will normally throw an error on the initial createAction call due to the output being doublespent
* @param setup
* @param options
* @returns
*/
async function doubleSpendOldChange(setup, options) {
const auth = await setup.wallet.storage.getAuth(true);
if (!auth.userId || !setup.wallet.storage.getActive().isStorageProvider)
throw new Error('active must be StorageProvider');
const s = setup.wallet.storage.getActive();
const o = (0, src_1.verifyOne)(await s.findOutputs({ partial: { userId: auth.userId, spendable: false, change: true }, paged: { limit: 1 } }));
await s.validateOutputScript(o);
const lockingScript = sdk_1.Script.fromBinary(o.lockingScript);
const otx = (0, src_1.verifyTruthy)(await s.findTransactionById(o.transactionId));
if (otx.status !== 'completed')
throw new Error('output must be from completed transaction');
const inputBEEF = (await s.getBeefForTransaction(o.txid, {})).toBinary();
(0, TestUtilsWalletStorage_1.logger)(`spending ${o.txid} vout ${o.vout}`);
const sabppp = new src_1.ScriptTemplateBRC29({
derivationPrefix: o.derivationPrefix,
derivationSuffix: o.derivationSuffix,
keyDeriver: setup.wallet.keyDeriver
});
const args = {
inputBEEF,
inputs: [
{
unlockingScriptLength: 108,
outpoint: `${o.txid}.${o.vout}`,
inputDescription: 'spent change output'
}
],
description: 'intentional doublespend',
options
};
let car;
try {
(0, createAction_1.setDisableDoubleSpendCheckForTest)(true);
car = await setup.wallet.createAction(args);
}
finally {
(0, createAction_1.setDisableDoubleSpendCheckForTest)(false);
}
expect(car.signableTransaction);
const st = car.signableTransaction;
const beef = sdk_1.Beef.fromBinary(st.tx);
const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid);
const unlock = sabppp.unlock(setup.rootKey.toHex(), setup.identityKey, o.satoshis, lockingScript);
const unlockingScript = (await unlock.sign(tx, 0)).toHex();
const signArgs = {
reference: st.reference,
spends: { '0': { unlockingScript } },
options
};
const sar = await setup.wallet.signAction(signArgs);
return sar;
}
async function createMainReviewSetup() {
const env = TestUtilsWalletStorage_1._tu.getEnv('main');
const knex = src_1.Setup.createMySQLKnex(process.env.MAIN_CLOUD_MYSQL_CONNECTION);
const storage = new src_1.StorageKnex({
chain: env.chain,
knex: knex,
commissionSatoshis: 0,
commissionPubKeyHex: undefined,
feeModel: { model: 'sat/kb', value: 1 }
});
const servicesOptions = src_1.Services.createDefaultOptions(env.chain);
if (env.whatsonchainApiKey)
servicesOptions.whatsOnChainApiKey = env.whatsonchainApiKey;
const services = new src_1.Services(servicesOptions);
storage.setServices(services);
await storage.makeAvailable();
return { env, storage, services };
}
//# sourceMappingURL=localWalletMethods.js.map