From aade4e3c3e348ab1979e3eebe1b19c769acaf818 Mon Sep 17 00:00:00 2001 From: Ivan Vershigora Date: Wed, 11 Dec 2024 15:03:25 +0000 Subject: [PATCH] fix: send tests --- package.json | 1 + tests/send.test.ts | 207 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 tests/send.test.ts diff --git a/package.json b/package.json index 52b8817..adf98b0 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "dist/index.js", "scripts": { "test": "yarn build && env mocha --exit -r ts-node/register 'tests/**/*.test.ts'", + "test:send": "yarn build && env mocha --exit -r ts-node/register 'tests/send.test.ts'", "test:boost": "yarn build && env mocha --exit -r ts-node/register 'tests/boost.test.ts'", "test:wallet": "yarn build && env mocha --exit -r ts-node/register 'tests/wallet.test.ts'", "test:receive": "yarn build && env mocha --exit -r ts-node/register 'tests/receive.test.ts'", diff --git a/tests/send.test.ts b/tests/send.test.ts new file mode 100644 index 0000000..e6d03a0 --- /dev/null +++ b/tests/send.test.ts @@ -0,0 +1,207 @@ +import BitcoinJsonRpc from 'bitcoin-json-rpc'; +import { expect } from 'chai'; +import net from 'net'; +import tls from 'tls'; + +import { + EAddressType, + EAvailableNetworks, + EProtocol, + generateMnemonic, + validateTransaction, + Wallet +} from '../'; +import { + bitcoinURL, + electrumHost, + electrumPort, + initWaitForElectrumToSync, + MessageListener, + TWaitForElectrum +} from './utils'; + +const testTimeout = 60000; +let wallet: Wallet; +let waitForElectrum: TWaitForElectrum; +const rpc = new BitcoinJsonRpc(bitcoinURL); +const ml = new MessageListener(); + +describe('Send', async function () { + this.timeout(testTimeout); + + beforeEach(async function () { + this.timeout(testTimeout); + ml.clear(); + + // Ensure sufficient balance in regtest + let balance = await rpc.getBalance(); + const address = await rpc.getNewAddress(); + await rpc.generateToAddress(1, address); + + while (balance < 10) { + await rpc.generateToAddress(10, address); + balance = await rpc.getBalance(); + } + + waitForElectrum = await initWaitForElectrumToSync( + { host: electrumHost, port: electrumPort }, + bitcoinURL + ); + await waitForElectrum(); + + const res = await Wallet.create({ + mnemonic: generateMnemonic(), + network: EAvailableNetworks.regtest, + addressType: EAddressType.p2wpkh, + electrumOptions: { + servers: [ + { + host: '127.0.0.1', + ssl: 60002, + tcp: 60001, + protocol: EProtocol.tcp + } + ], + net, + tls + }, + // reduce gap limit to speed up tests + gapLimitOptions: { + lookAhead: 2, + lookBehind: 2, + lookAheadChange: 2, + lookBehindChange: 2 + }, + addressTypesToMonitor: [EAddressType.p2wpkh], + onMessage: ml.onMessage + }); + if (res.isErr()) throw res.error; + wallet = res.value; + await wallet.refreshWallet({}); + }); + + afterEach(async function () { + await wallet?.stop(); + }); + + it('one input - one output transaction, no RBF', async () => { + const r = await wallet.getNextAvailableAddress(); + if (r.isErr()) throw r.error; + const wAddress = r.value.addressIndex.address; + await rpc.sendToAddress(wAddress, '0.1'); + await rpc.generateToAddress(3, await rpc.getNewAddress()); + await waitForElectrum(); + await wallet.refreshWallet(); + + const address = await rpc.getNewAddress(); + + const sendRes = await wallet.send({ + address, + amount: 10000, // amount in sats + satsPerByte: 1 + }); + if (sendRes.isErr()) throw sendRes.error; + const txid = sendRes.value; + + await wallet.refreshWallet({}); + expect(wallet.data.transactions).to.have.property(txid); + const tx = wallet.data.transactions[txid]; + expect(tx.fee).to.equal(0.00000256); + expect(tx.type).to.equal('sent'); + expect(tx.value).to.equal(-0.00010256); + expect(tx.txid).to.equal(txid); + expect(tx.rbf).to.equal(false); + }); + + it('one input - one output transaction, with RBF', async () => { + const r = await wallet.getNextAvailableAddress(); + if (r.isErr()) throw r.error; + const wAddress = r.value.addressIndex.address; + await rpc.sendToAddress(wAddress, '0.1'); + await rpc.generateToAddress(3, await rpc.getNewAddress()); + await waitForElectrum(); + await wallet.refreshWallet(); + + const address = await rpc.getNewAddress(); + + const sendRes = await wallet.send({ + address, + amount: 10000, // amount in sats + satsPerByte: 10, + rbf: true + }); + if (sendRes.isErr()) throw sendRes.error; + const txid = sendRes.value; + + await wallet.refreshWallet({}); + expect(wallet.data.transactions).to.have.property(txid); + const tx = wallet.data.transactions[txid]; + expect(tx.fee).to.equal(0.0000166); + expect(tx.type).to.equal('sent'); + expect(tx.value).to.equal(-0.0001166); + expect(tx.txid).to.equal(txid); + expect(tx.rbf).to.equal(true); + }); + + it('two inputs - two outputs', async () => { + const [a1, a2] = Object.values(wallet.data.addresses.p2wpkh).map( + (v) => v.address + ); + await rpc.sendToAddress(a1, '0.0001'); + await rpc.sendToAddress(a2, '0.0001'); + await rpc.generateToAddress(3, await rpc.getNewAddress()); + await waitForElectrum(); + await wallet.refreshWallet(); + + const resetRes = await wallet.resetSendTransaction(); + if (resetRes.isErr()) throw resetRes.error; + const setupRes = await wallet.setupTransaction({}); + if (setupRes.isErr()) throw setupRes.error; + const updateRes = wallet.transaction.updateSendTransaction({ + transaction: { + outputs: [ + { + index: 0, + address: await rpc.getNewAddress(), + value: 6000 + }, + { + index: 1, + address: await rpc.getNewAddress(), + value: 6000 + } + ] + } + }); + if (updateRes.isErr()) throw updateRes.error; + + const validateRes = validateTransaction(wallet.transaction.data); + if (validateRes.isErr()) throw validateRes.error; + const createRes = await wallet.transaction.createTransaction(); + if (createRes.isErr()) throw createRes.error; + const broadcastRes = await wallet.electrum.broadcastTransaction({ + rawTx: createRes.value.hex + }); + const txid = createRes.value.id; + if (broadcastRes.isErr()) throw broadcastRes.error; + + await rpc.generateToAddress(3, await rpc.getNewAddress()); + await wallet.refreshWallet({}); + expect(wallet.data.transactions).to.have.property(txid); + + // TODO: check tx inputs and outputs + }); + + it('should fail to send with insufficient balance', async () => { + const r = await wallet.getNextAvailableAddress(); + if (r.isErr()) throw r.error; + const address = r.value.addressIndex.address; + + const sendRes = await wallet.send({ + address, + amount: 1000000000, + satsPerByte: 1 + }); + expect(sendRes.isErr()).to.be.true; + }); +});