@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
278 lines (250 loc) • 9.26 kB
text/typescript
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()
})
})