UNPKG

@bsv/wallet-toolbox

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

278 lines (250 loc) 9.26 kB
import { Beef, PrivateKey, SignActionArgs, WalletOutput } from '@bsv/sdk' import { sdk, Services, Setup, StorageKnex, TableUser } from '../../../src' import { _tu, TuEnv } from '../../utils/TestUtilsWalletStorage' import { specOpInvalidChange, ValidListOutputsArgs, WERR_REVIEW_ACTIONS } from '../../../src/sdk' import { burnOneSatTestOutput, createMainReviewSetup, createOneSatTestOutput, createSetup, doubleSpendOldChange, LocalWalletTestOptions, recoverOneSatTestOutputs } from '../../utils/localWalletMethods' import * as dotenv from 'dotenv' dotenv.config() const chain: sdk.Chain = 'main' const options: LocalWalletTestOptions = { setActiveClient: true, useMySQLConnectionForClient: true, useTestIdentityKey: false, useIdentityKey2: false } describe('localWallet2 tests', () => { jest.setTimeout(99999999) test('0 monitor runOnce', async () => { const setup = await createSetup(chain, options) const log = await setup.monitor.runTask('UnFail') if (log) console.log(log) await setup.monitor.runOnce() await setup.wallet.destroy() }) test('0a abort nosend', async () => { const setup = await createSetup(chain, options) await setup.wallet.listNoSendActions({ labels: [] }, true) await setup.wallet.destroy() }) test('1 recover 1 sat outputs', async () => { const setup = await createSetup(chain, options) await recoverOneSatTestOutputs(setup, 1) await setup.wallet.destroy() }) test('2 create 1 sat delayed', async () => { const setup = await createSetup(chain, options) const car = await createOneSatTestOutput(setup, {}, 1) //await trackReqByTxid(setup, car.txid!) await setup.wallet.destroy() }) test('2a create 1 sat immediate', async () => { const setup = await createSetup(chain, options) const car = await createOneSatTestOutput(setup, { acceptDelayedBroadcast: false }, 1) await setup.wallet.destroy() }) test('2c burn 1 sat output', async () => { const setup = await createSetup(chain, options) await burnOneSatTestOutput(setup, {}, 1) await setup.wallet.destroy() }) test('2d doubleSpend old change', async () => { const setup = await createSetup(chain, options) try { await doubleSpendOldChange(setup, { acceptDelayedBroadcast: false }) } catch (eu: unknown) { const e = sdk.WalletError.fromUnknown(eu) as WERR_REVIEW_ACTIONS expect(e.code).toBe('WERR_REVIEW_ACTIONS') expect(e.reviewActionResults?.length === 1).toBe(true) const rar = e.reviewActionResults![0]! expect(rar.status).toBe('doubleSpend') expect(rar.competingTxs?.length).toBe(1) } await setup.wallet.destroy() }) test('4 review change utxos', async () => { const setup = await createSetup(chain, options) const lor = await setup.wallet.listOutputs({ basket: specOpInvalidChange, tags: ['all'] }) if (lor.totalOutputs > 0) { debugger const lor = await setup.wallet.listOutputs({ basket: specOpInvalidChange, tags: ['all', 'release'] }) } await setup.wallet.destroy() }) test('5 review and release all production invalid change utxos', async () => { const { env, storage } = await createMainReviewSetup() const users = await storage.findUsers({ partial: {} }) const withInvalid: Record<number, { user: TableUser; outputs: WalletOutput[]; total: number }> = {} // [76, 48, 166, 94, 110, 111, 81] const vargs: ValidListOutputsArgs = { basket: specOpInvalidChange, tags: [], tagQueryMode: 'all', includeLockingScripts: false, includeTransactions: false, includeCustomInstructions: false, includeTags: false, includeLabels: false, limit: 0, offset: 0, seekPermission: false, knownTxids: [] } for (const user of users) { const { userId } = user const auth = { userId, identityKey: '' } let r = await storage.listOutputs(auth, vargs) if (r.totalOutputs > 0) { const total: number = r.outputs.reduce((s, o) => (s += o.satoshis), 0) console.log(`userId ${userId}: ${r.totalOutputs} unspendable utxos, total ${total}, ${user.identityKey}`) withInvalid[userId] = { user, outputs: r.outputs, total } } } if (Object.keys(withInvalid).length > 0) { debugger // Release invalids for (const { user, outputs } of Object.values(withInvalid)) { const { userId } = user const auth = { userId, identityKey: '' } await storage.listOutputs(auth, { ...vargs, tags: ['release'] }) } // Verify for (const { user, outputs } of Object.values(withInvalid)) { const { userId } = user const auth = { userId, identityKey: '' } const r = await storage.listOutputs(auth, vargs) expect(r.totalOutputs).toBe(0) } } await storage.destroy() }) test('6 review and unfail false doubleSpends', async () => { const { env, storage, services } = await createMainReviewSetup() let offset = 1100 const limit = 100 let allUnfails: number[] = [] for (;;) { let log = '' const unfails: number[] = [] const reqs = await storage.findProvenTxReqs({ partial: { status: 'doubleSpend' }, paged: { limit, offset } }) for (const req of reqs) { const gsr = await services.getStatusForTxids([req.txid]) if (gsr.results[0].status !== 'unknown') { log += `unfail ${req.provenTxReqId} ${req.txid}\n` unfails.push(req.provenTxReqId) } } console.log(`OFFSET: ${offset} ${unfails.length} unfails\n${log}`) allUnfails = allUnfails.concat(unfails) if (reqs.length < limit) break offset += reqs.length } debugger for (const id of allUnfails) { await storage.updateProvenTxReq(id, { status: 'unfail' }) } await storage.destroy() }) test('7 review and unfail false invalids', async () => { const { env, storage, services } = await createMainReviewSetup() let offset = 400 const limit = 100 let allUnfails: number[] = [] for (;;) { let log = '' const unfails: number[] = [] const reqs = await storage.findProvenTxReqs({ partial: { status: 'invalid' }, paged: { limit, offset } }) for (const req of reqs) { if (!req.txid || !req.rawTx) continue const gsr = await services.getStatusForTxids([req.txid]) if (gsr.results[0].status !== 'unknown') { log += `unfail ${req.provenTxReqId} ${req.txid}\n` unfails.push(req.provenTxReqId) } } console.log(`OFFSET: ${offset} ${unfails.length} unfails\n${log}`) allUnfails = allUnfails.concat(unfails) if (reqs.length < limit) break offset += reqs.length } debugger for (const id of allUnfails) { await storage.updateProvenTxReq(id, { status: 'unfail' }) } await storage.destroy() }) test('8 Beef verifier', async () => { const setup = await createSetup(chain, options) // replace bb with beef to test const bb = new Beef().toBinary() const beef = Beef.fromBinary(bb) console.log(beef.toLogString()) const ok = await beef.verify(await setup.services.getChainTracker()) await setup.wallet.destroy() }) test.skip('9 received payment from wif and outpoint', async () => { const setup = await createSetup(chain, options) console.log(`active store ${setup.wallet.storage.getActiveStore()}`) if (!setup.wallet.storage.isActiveEnabled) throw new Error('Active storage is not enabled.') const pk = PrivateKey.fromWif('L4ZRWA...Nw4Brt8rvJLRZegPF2oiBKJaxUgr4e') const outpoint = { txid: '5e2965a50618425af21bebddb9aa60c3e12f64c8e1eb44b6589273455a9760e9', vout: 0 } const address = pk.toAddress() console.log(`address: ${address.toString()}`) const inputBEEF = await setup.activeStorage.getBeefForTransaction(outpoint.txid, { ignoreStorage: true }) const btx = inputBEEF.findTxid(outpoint.txid) const satoshis = btx!.tx!.outputs[0]!.satoshis! const unlock = Setup.getUnlockP2PKH(pk, satoshis) const label = 'inputBrayden257' const car = await setup.wallet.createAction({ inputBEEF: inputBEEF.toBinary(), inputs: [ { outpoint: `${outpoint.txid}.${outpoint.vout}`, unlockingScriptLength: 108, inputDescription: label } ], labels: [label], description: label }) const st = car.signableTransaction! const beef = Beef.fromBinary(st.tx) const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid)! tx.inputs[0].unlockingScriptTemplate = unlock await tx.sign() const unlockingScript = tx.inputs[0].unlockingScript!.toHex() const signArgs: SignActionArgs = { reference: st.reference, spends: { 0: { unlockingScript } }, options: { acceptDelayedBroadcast: false } } const sar = await setup.wallet.signAction(signArgs) { const beef = Beef.fromBinary(sar.tx!) const txid = sar.txid! console.log(` BEEF ${beef.toHex()} ${beef.toLogString()} `) } await setup.wallet.destroy() }) })