From 7a8a9d00e5a089b6bfcd0cc7bdf74325bdf5a2c6 Mon Sep 17 00:00:00 2001 From: ordinariusprof Date: Fri, 22 Mar 2024 22:15:53 -0700 Subject: [PATCH 1/2] remove unused --- model/wallet.js | 72 --------------- test/wallet.js | 227 ------------------------------------------------ 2 files changed, 299 deletions(-) delete mode 100644 test/wallet.js diff --git a/model/wallet.js b/model/wallet.js index b29ac50..52b7a01 100644 --- a/model/wallet.js +++ b/model/wallet.js @@ -80,53 +80,6 @@ class Wallet { confTarget, ); - /** - * create a raw bitcoin transaction manually, - * instead of using bitcoind createrawtransaction. - * This is needed because bitcoind does not support - * having duplicate output addresses. - * @param {Input[]} inputs - Array of inputs - * @param {Output[]} outputs - Input object - * @param {string[]} inputsRawTx - Array of raw transactions - * @returns {string} Raw transaction hex - * - * // Inputs and outputs arrays - * const inputs = [ - * { hash: 'transactionId1', index: 0 }, - * { hash: 'transactionId2', index: 1 } - * ]; - * const outputs = [ - * { address: address1, value: amount1 }, - * { address: address2, value: amount2 } - * ]; - */ - createRawTransaction = (inputs, outputs, inputsRawTx = null) => { - // Without this sending to taproot addresses is failing - bitcoinjs.initEccLib(ecc); - - const { network } = this; - const txb = new bitcoinjs.Psbt({ network }); - - inputs.forEach((input, i) => { - const x = { - ...input, - sequence: bitcoinjs.Transaction.DEFAULT_SEQUENCE - 2, // Enable RBF - }; - - // Add the raw transaction if it exists - if (inputsRawTx) { - x.nonWitnessUtxo = Buffer.from(inputsRawTx[i], 'hex'); - } - - txb.addInput(x); - }); - outputs.forEach((output) => txb.addOutput({ ...output })); - - // Build the transaction - const tx = txb.data.globalMap.unsignedTx.toBuffer().toString('hex'); - return tx; - }; - /** * @param {Input[]} inputs - the inputs * @param {Output[]} outputs - the outputs @@ -161,31 +114,6 @@ class Wallet { sendRawTransaction = async (signedTx) => this.bitcoinClient.sendRawTransaction(signedTx); - /** - * - * @typedef {object} Input - * @property {string} hash - Transaction hash - * @property {number} index - Transaction index - * @typedef {object} Output - * @property {string} address - Address - * @property {number} value - Value in satoshis - * @param {Input[]} inputs - Array of inputs - * @param {Output[]} outputs - Input object - * @returns {Promise} txId - Transaction id - */ - sendCustomTransaction = async (inputs, outputs) => { - // Create raw transaction - const rawTx = await this.createUnsignedHexTransaction(inputs, outputs); - - // Sign raw transaction - const signedTx = await this.signRawTransaction(rawTx); - - // Send raw transaction - const txId = await this.sendRawTransaction(signedTx); - - return txId; - }; - decodeRawTransaction = async (rawTx) => this.bitcoinClient.decodeRawTransaction(rawTx); } diff --git a/test/wallet.js b/test/wallet.js deleted file mode 100644 index 472c105..0000000 --- a/test/wallet.js +++ /dev/null @@ -1,227 +0,0 @@ -const assert = require('assert'); -const bitcoinjs = require('bitcoinjs-lib'); -const BitcoinClient = require('bitcoin-core'); -const sinon = require('sinon'); -const Wallet = require('../model/wallet'); - -describe('Wallet', () => { - describe('#createRawTransaction()', () => { - it('should build a raw transaction', () => { - const inputs = [ - { - hash: 'b69a64b2e8feebae77e4a67b908392c7cfc5c648e55e1949d3187c1c1b5e95e7', - index: 1, - }, - { - hash: 'c87146f7ffbd127c8daa7259f66f578a92eee5f4ed8afe690bb81e0c8de28092', - index: 1, - }, - { - hash: '4d0fc9a7afbcd0ff2c5a2e3091114e2dbbe23e6e737729dee3c975aadbef2b91', - index: 1, - }, - { - hash: '9217167d96de3cb45dc1b79f358901de3ad1778c7d6858cd03942fc59f92590c', - index: 0, - }, - ]; - const outputs = [ - { - address: 'bc1qkz6m0ynel5y8ra9x8vqf04v75ayweavwm9l5k5', - value: 40546, - }, - { - address: '3LA964JzvWFDH9kG9uTZBHA7CZgPUB6doi', - value: 1007453, - }, - ]; - const inputsRaw = [ - '0200000000010131ee2bd58aef2d83580c1d3ff198e9dadf38d5559c1c286e9776aad25826d6de0000000000ffffffff04415a041900000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d2afab580400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202c63804b458f63827bb73d1f9e5eb74a20ca3c79f2bbf4907137a765a96547d302202d9f2fae0dceb1cf32411555a3bc8b6ee4299b8a5f17e75c169cfa2263d20aab01210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101a87b658b2f739a861e3cdc29224923e0240a2cd6fc7f830cc2c784d58b8b5e590300000000ffffffff042090f71000000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d26326f50400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202199a46d54e7f4fc3bde2868cfe93cc4d91c43a077e5826b44573bc59ff23ebc02202c036c942c6b9ddfb88bb96f37b2c19176334b4b791239c1d75a4d644c82346501210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '020000000001018dfd49e5ab4e6998290e9e0d93ef241fb0bede0402c4eaf174487726ea8ba0f10200000000ffffffff0428cec91100000000160014e3a61181dd569c0250f828b393839d81517c78cd22020000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d239de3e0b00000000160014e3a61181dd569c0250f828b393839d81517c78cd8813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e02483045022100e71f82d377aced4b452686476b8d881f89046cb5086f01500dd0d54ff84b23fe022058f9d5297e2113aebeb2ebe63de6583d47ceb9fdc88bb983e7abc7acc575b9e201210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101b5edc0088d0ae5942cbc635afbc7601c5d178c0a6799fe8406a3dd53fe2e28be0000000000ffffffff04ee240000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d222020000000000002251205b30284f60cd538523e7804650ba306950560fc228f85450c1e6e3d5c4c99701f2070000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d28813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e014103a7847983ef557f78c664bf020f73ffacffb1cdb8ae5de6aab782501714df08d82fe05dc2e8bd0d6d3a35a02b764de58d5cdf117b441c677c53c541a2f167880100000000', - ]; - const wallet = new Wallet(); - - const res = wallet.createRawTransaction(inputs, outputs, inputsRaw); - - // decode the transaction and check the inputs and outputs - const tx = bitcoinjs.Transaction.fromHex(res); - assert.ok(tx.ins[0].sequence < bitcoinjs.Transaction.DEFAULT_SEQUENCE - 1, 'RBF is enabled'); - - assert.equal(tx.ins.length, inputs.length); - for (let i = 0; i < inputs.length; i++) { - assert.equal(Buffer.from(tx.ins[i].hash).reverse().toString('hex'), inputs[i].hash); - assert.equal(tx.ins[i].index, inputs[i].index); - } - - assert.equal(tx.outs.length, outputs.length); - for (let i = 0; i < outputs.length; i++) { - const address = bitcoinjs.address.fromOutputScript(tx.outs[i].script); - const { value } = tx.outs[i]; - assert.equal(address, outputs[i].address); - assert.equal(value, outputs[i].value); - } - }); - - it('should throw when output address is invalid', () => { - const inputs = [ - { - hash: 'b69a64b2e8feebae77e4a67b908392c7cfc5c648e55e1949d3187c1c1b5e95e7', - index: 1, - }, - { - hash: 'c87146f7ffbd127c8daa7259f66f578a92eee5f4ed8afe690bb81e0c8de28092', - index: 1, - }, - { - hash: '4d0fc9a7afbcd0ff2c5a2e3091114e2dbbe23e6e737729dee3c975aadbef2b91', - index: 1, - }, - { - hash: '9217167d96de3cb45dc1b79f358901de3ad1778c7d6858cd03942fc59f92590c', - index: 0, - }, - ]; - const outputs = [ - { - address: 'invalidAddr', - value: 40546, - }, - { - address: '3LA964JzvWFDH9kG9uTZBHA7CZgPUB6doi', - value: 1007453, - }, - ]; - const inputsRaw = [ - '0200000000010131ee2bd58aef2d83580c1d3ff198e9dadf38d5559c1c286e9776aad25826d6de0000000000ffffffff04415a041900000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d2afab580400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202c63804b458f63827bb73d1f9e5eb74a20ca3c79f2bbf4907137a765a96547d302202d9f2fae0dceb1cf32411555a3bc8b6ee4299b8a5f17e75c169cfa2263d20aab01210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101a87b658b2f739a861e3cdc29224923e0240a2cd6fc7f830cc2c784d58b8b5e590300000000ffffffff042090f71000000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d26326f50400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202199a46d54e7f4fc3bde2868cfe93cc4d91c43a077e5826b44573bc59ff23ebc02202c036c942c6b9ddfb88bb96f37b2c19176334b4b791239c1d75a4d644c82346501210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '020000000001018dfd49e5ab4e6998290e9e0d93ef241fb0bede0402c4eaf174487726ea8ba0f10200000000ffffffff0428cec91100000000160014e3a61181dd569c0250f828b393839d81517c78cd22020000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d239de3e0b00000000160014e3a61181dd569c0250f828b393839d81517c78cd8813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e02483045022100e71f82d377aced4b452686476b8d881f89046cb5086f01500dd0d54ff84b23fe022058f9d5297e2113aebeb2ebe63de6583d47ceb9fdc88bb983e7abc7acc575b9e201210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101b5edc0088d0ae5942cbc635afbc7601c5d178c0a6799fe8406a3dd53fe2e28be0000000000ffffffff04ee240000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d222020000000000002251205b30284f60cd538523e7804650ba306950560fc228f85450c1e6e3d5c4c99701f2070000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d28813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e014103a7847983ef557f78c664bf020f73ffacffb1cdb8ae5de6aab782501714df08d82fe05dc2e8bd0d6d3a35a02b764de58d5cdf117b441c677c53c541a2f167880100000000', - ]; - const wallet = new Wallet(); - - assert.throws(() => wallet.createRawTransaction(inputs, outputs, inputsRaw)); - }); - - it('should work with a taproot address', () => { - const inputs = [ - { - hash: 'b69a64b2e8feebae77e4a67b908392c7cfc5c648e55e1949d3187c1c1b5e95e7', - index: 1, - }, - { - hash: 'c87146f7ffbd127c8daa7259f66f578a92eee5f4ed8afe690bb81e0c8de28092', - index: 1, - }, - { - hash: '4d0fc9a7afbcd0ff2c5a2e3091114e2dbbe23e6e737729dee3c975aadbef2b91', - index: 1, - }, - { - hash: '9217167d96de3cb45dc1b79f358901de3ad1778c7d6858cd03942fc59f92590c', - index: 0, - }, - ]; - const outputs = [ - { - address: 'bc1pjltse6tx48xk6zyc7ss85ndk7uflc5twq2g0sc3wcv4asxx7vzlsgvlz7k', - value: 40546, - }, - { - address: '3LA964JzvWFDH9kG9uTZBHA7CZgPUB6doi', - value: 1007453, - }, - ]; - const inputsRaw = [ - '0200000000010131ee2bd58aef2d83580c1d3ff198e9dadf38d5559c1c286e9776aad25826d6de0000000000ffffffff04415a041900000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d2afab580400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202c63804b458f63827bb73d1f9e5eb74a20ca3c79f2bbf4907137a765a96547d302202d9f2fae0dceb1cf32411555a3bc8b6ee4299b8a5f17e75c169cfa2263d20aab01210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101a87b658b2f739a861e3cdc29224923e0240a2cd6fc7f830cc2c784d58b8b5e590300000000ffffffff042090f71000000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d26326f50400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202199a46d54e7f4fc3bde2868cfe93cc4d91c43a077e5826b44573bc59ff23ebc02202c036c942c6b9ddfb88bb96f37b2c19176334b4b791239c1d75a4d644c82346501210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '020000000001018dfd49e5ab4e6998290e9e0d93ef241fb0bede0402c4eaf174487726ea8ba0f10200000000ffffffff0428cec91100000000160014e3a61181dd569c0250f828b393839d81517c78cd22020000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d239de3e0b00000000160014e3a61181dd569c0250f828b393839d81517c78cd8813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e02483045022100e71f82d377aced4b452686476b8d881f89046cb5086f01500dd0d54ff84b23fe022058f9d5297e2113aebeb2ebe63de6583d47ceb9fdc88bb983e7abc7acc575b9e201210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101b5edc0088d0ae5942cbc635afbc7601c5d178c0a6799fe8406a3dd53fe2e28be0000000000ffffffff04ee240000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d222020000000000002251205b30284f60cd538523e7804650ba306950560fc228f85450c1e6e3d5c4c99701f2070000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d28813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e014103a7847983ef557f78c664bf020f73ffacffb1cdb8ae5de6aab782501714df08d82fe05dc2e8bd0d6d3a35a02b764de58d5cdf117b441c677c53c541a2f167880100000000', - ]; - const wallet = new Wallet(); - - const res = wallet.createRawTransaction(inputs, outputs, inputsRaw); - - // decode the transaction and check the inputs and outputs - const tx = bitcoinjs.Transaction.fromHex(res); - assert.ok(tx.ins[0].sequence < bitcoinjs.Transaction.DEFAULT_SEQUENCE - 1, 'RBF is enabled'); - - assert.equal(tx.ins.length, inputs.length); - for (let i = 0; i < inputs.length; i++) { - assert.equal(Buffer.from(tx.ins[i].hash).reverse().toString('hex'), inputs[i].hash); - assert.equal(tx.ins[i].index, inputs[i].index); - } - - assert.equal(tx.outs.length, outputs.length); - for (let i = 0; i < outputs.length; i++) { - const address = bitcoinjs.address.fromOutputScript(tx.outs[i].script); - const { value } = tx.outs[i]; - assert.equal(address, outputs[i].address); - assert.equal(value, outputs[i].value); - } - }); - }); - - describe('#sendCustomTransaction()', () => { - it('should send a custom transaction', async () => { - const inputs = [ - { - hash: 'b69a64b2e8feebae77e4a67b908392c7cfc5c648e55e1949d3187c1c1b5e95e7', - index: 1, - }, - { - hash: 'c87146f7ffbd127c8daa7259f66f578a92eee5f4ed8afe690bb81e0c8de28092', - index: 1, - }, - { - hash: '4d0fc9a7afbcd0ff2c5a2e3091114e2dbbe23e6e737729dee3c975aadbef2b91', - index: 1, - }, - { - hash: '9217167d96de3cb45dc1b79f358901de3ad1778c7d6858cd03942fc59f92590c', - index: 0, - }, - ]; - const outputs = [ - { - address: 'bc1qkz6m0ynel5y8ra9x8vqf04v75ayweavwm9l5k5', - value: 40546, - }, - { - address: '3LA964JzvWFDH9kG9uTZBHA7CZgPUB6doi', - value: 1007453, - }, - ]; - const inputsRaw = [ - '0200000000010131ee2bd58aef2d83580c1d3ff198e9dadf38d5559c1c286e9776aad25826d6de0000000000ffffffff04415a041900000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d2afab580400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202c63804b458f63827bb73d1f9e5eb74a20ca3c79f2bbf4907137a765a96547d302202d9f2fae0dceb1cf32411555a3bc8b6ee4299b8a5f17e75c169cfa2263d20aab01210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101a87b658b2f739a861e3cdc29224923e0240a2cd6fc7f830cc2c784d58b8b5e590300000000ffffffff042090f71000000000160014e3a61181dd569c0250f828b393839d81517c78cd204e0000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d26326f50400000000160014e3a61181dd569c0250f828b393839d81517c78cd6842000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e0247304402202199a46d54e7f4fc3bde2868cfe93cc4d91c43a077e5826b44573bc59ff23ebc02202c036c942c6b9ddfb88bb96f37b2c19176334b4b791239c1d75a4d644c82346501210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '020000000001018dfd49e5ab4e6998290e9e0d93ef241fb0bede0402c4eaf174487726ea8ba0f10200000000ffffffff0428cec91100000000160014e3a61181dd569c0250f828b393839d81517c78cd22020000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d239de3e0b00000000160014e3a61181dd569c0250f828b393839d81517c78cd8813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e02483045022100e71f82d377aced4b452686476b8d881f89046cb5086f01500dd0d54ff84b23fe022058f9d5297e2113aebeb2ebe63de6583d47ceb9fdc88bb983e7abc7acc575b9e201210311997bb26d1fbffd12db78378480e0e6d2141df34e255419cd12e147530407c300000000', - '02000000000101b5edc0088d0ae5942cbc635afbc7601c5d178c0a6799fe8406a3dd53fe2e28be0000000000ffffffff04ee240000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d222020000000000002251205b30284f60cd538523e7804650ba306950560fc228f85450c1e6e3d5c4c99701f2070000000000002251203cfada579a653ed7efbbd325eed4f54733a8630e5ffa6e1e5f0d28eb7cada0d28813000000000000225120cc8a60669be807a976f45c629b396cdfdaf2a6f970b65aba5d91f5a7f9592d3e014103a7847983ef557f78c664bf020f73ffacffb1cdb8ae5de6aab782501714df08d82fe05dc2e8bd0d6d3a35a02b764de58d5cdf117b441c677c53c541a2f167880100000000', - ]; - - const txUnsignedHex = '0200000004e7955e1b1c7c18d349195ee548c6c5cfc79283907ba6e477aeebfee8b2649ab60100000000fdffffff9280e28d0c1eb80b69fe8aedf4e5ee928a576ff65972aa8d7c12bdfff74671c80100000000fdffffff912befdbaa75c9e3de2977736e3ee2bb2d4e1191302e5a2cffd0bcafa7c90f4d0100000000fdffffff0c59929fc52f9403cd58687d8c77d13ade0189359fb7c15db43cde967d1617920000000000fdffffff02629e000000000000160014b0b5b79279fd0871f4a63b0097d59ea748ecf58e5d5f0f000000000017a914ca9379ae1eafa125156661d90741e6417195e3dd8700000000'; - const signRes = { - hex: 'signed_tx_hex', - complete: true, - }; - const txid = 'txid'; - const mockBitcoinClient = new BitcoinClient(); - const stubRawTx = sinon.stub(mockBitcoinClient, 'getRawTransaction'); - for (let i = 0; i < inputs.length; i++) { - stubRawTx.withArgs(inputs[i].hash).resolves(inputsRaw[i]); - } - - const spySign = sinon.stub(mockBitcoinClient, 'signRawTransactionWithWallet').resolves(signRes); - const spySend = sinon.stub(mockBitcoinClient, 'sendRawTransaction').resolves(txid); - const wallet = new Wallet(mockBitcoinClient); - const spyCreateRawTx = sinon.spy(wallet, 'createRawTransaction'); - - const res = await wallet.sendCustomTransaction(inputs, outputs); - assert(spyCreateRawTx.calledWith(inputs, outputs)); - assert(spySign.calledWith(txUnsignedHex)); - assert(spySend.calledWith(signRes.hex)); - assert.equal(res, txid); - }); - }); -}); From 194d3be20401a6e4bb8e4306b3180e741d2092fe Mon Sep 17 00:00:00 2001 From: ordinariusprof Date: Fri, 22 Mar 2024 22:20:51 -0700 Subject: [PATCH 2/2] add verbose notifications --- .env.sample | 3 +++ conf/satminer.js | 3 +++ index.js | 3 ++- model/notifications/notificationService.js | 6 +++-- model/satminer.js | 5 ++++ test/satminer.js | 27 ++++++++++++++++++++-- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.env.sample b/.env.sample index 818fc22..2354d3b 100644 --- a/.env.sample +++ b/.env.sample @@ -61,6 +61,9 @@ OKCOIN_DEPOSIT_WALLET= # Notifications +# change to verbose to get all notifications +NOTIFICATION_LEVEL="info" + SLACK_WEB_HOOK= TELEGRAM_BOT_TOKEN= diff --git a/conf/satminer.js b/conf/satminer.js index 3750e57..c6e7bc9 100644 --- a/conf/satminer.js +++ b/conf/satminer.js @@ -144,6 +144,8 @@ if (!INCLUDE_SATRIBUTES) { console.log('only these satributes will be extracted!'); } +const { NOTIFICATION_LEVEL = 'info' } = process.env; + // The wallets will be loaded from ENV by priority from top to bottom const RARE_SAT_KNOWN_TYPES = [ 'uncommon', @@ -194,4 +196,5 @@ module.exports = { INCLUDE_SATRIBUTES, MIN_OUTPUT_SIZE, CUSTOM_SPECIAL_SAT_WALLETS, + NOTIFICATION_LEVEL, }; diff --git a/index.js b/index.js index b308802..3dd1c20 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,7 @@ const { CUSTOM_SPECIAL_SAT_WALLETS, ORDINALSBOT_API_KEY, INCLUDE_SATRIBUTES, + NOTIFICATION_LEVEL, } = require('./conf/satminer'); const NotificationService = require('./model/notifications/notificationService'); const SlackNotifications = require('./model/notifications/slack'); @@ -51,7 +52,7 @@ const satextractor = new Satextractor(ORDINALSBOT_API_KEY, "live"); const mempool = new Mempool(ORDINALSBOT_API_KEY, "live"); // initialize notifications -let notifications = new NotificationService(); +let notifications = new NotificationService(NOTIFICATION_LEVEL); if (SLACK_WEB_HOOK) { console.log('enabling slack webhook notifications'); const slackWebHook = new SlackNotifications(SLACK_WEB_HOOK); diff --git a/model/notifications/notificationService.js b/model/notifications/notificationService.js index 87c0cae..87c8f5c 100644 --- a/model/notifications/notificationService.js +++ b/model/notifications/notificationService.js @@ -1,13 +1,15 @@ class NotificationService { - constructor() { + constructor(notificationLevel = 'info') { this.notifiers = []; + this.notificationLevel = notificationLevel; } addNotifier(notifier) { this.notifiers.push(notifier); } - sendMessage(message) { + sendMessage(message, severity = 'info') { + if (this.notificationLevel === 'info' && severity === 'verbose') return; this.notifiers.forEach(notifier => { notifier.sendMessage(message); }); diff --git a/model/satminer.js b/model/satminer.js index e53ae55..00440d2 100644 --- a/model/satminer.js +++ b/model/satminer.js @@ -129,6 +129,7 @@ class Satminer { const fees = await this.wallet.estimateFee(); const { fastestFee } = fees; if (fastestFee > this.maxFeeAllowed) { + this.notificationService.sendMessage(`stopped: fee higher than max allowed ${this.maxFeeAllowed}`, 'verbose'); throw new Error(`fee higher than max allowed ${this.maxFeeAllowed}`); } @@ -152,6 +153,7 @@ class Satminer { console.log('tumbler wallet is empty'); return false; } else if (specialRanges.length === 0) { + this.notificationService.sendMessage('no special sats found, sending funds back to exchange', 'verbose'); console.log('no special sats found, sending funds back to exchange'); await this.checkLocalBalance(); } @@ -162,18 +164,21 @@ class Satminer { const outputsNok = decodedTransaction.vout.find((output) => !this.userControlledAddresses.includes(output.scriptPubKey.address)); console.log('outputsNok', outputsNok, this.userControlledAddresses); if (outputsNok) { + this.notificationService.sendMessage('stopped: not all outputs are user-controlled', 'verbose'); throw new Error('not all outputs are user-controlled'); } // check that what we are depositing to exchange is more than minDepositAmount const exchangeOutput = decodedTransaction.vout.find((output) => output.scriptPubKey.address === this.addressCommonSats); if (exchangeOutput.scriptPubKey.value < this.minDepositAmount) { + this.notificationService.sendMessage('stopped: deposit amount is less than minDepositAmount', 'verbose'); throw new Error('deposit amount is less than minDepositAmount'); } const signedTx = await this.wallet.signRawTransaction(tx); const txid = await this.wallet.sendRawTransaction(signedTx); console.log('sent txid', txid); + this.notificationService.sendMessage(`end of cycle. sent txid: ${txid}`, 'verbose'); if (specialRanges.length > 0) { const rangeSummary = this.specialRangesSummary(specialRanges); diff --git a/test/satminer.js b/test/satminer.js index 522a1e6..4e5142b 100644 --- a/test/satminer.js +++ b/test/satminer.js @@ -99,6 +99,7 @@ describe('Satminer', () => { const sendTxSpy = sinon.stub(wallet, 'sendRawTransaction').resolves(txid); const decodeRawTxSpy = sinon.stub(wallet, 'decodeRawTransaction').resolves({ vout: [{ scriptPubKey: { address: addressReceiveCommonSats, value: 0.1 } }] }); const signRawTxSpy = sinon.stub(wallet, 'signRawTransaction').resolves('signedtx'); + const notificationSpy = sinon.stub(notifications, 'sendMessage').resolves(true); const walletBalance = 1; const confirmationTargetBlocks = 2; @@ -113,7 +114,7 @@ describe('Satminer', () => { addressReceiveCommonSats, confirmationTargetBlocks, null, - null, + notifications, ['uncommon'], ); const res = await satminer.extractSatsAndRotateFunds(); @@ -133,6 +134,7 @@ describe('Satminer', () => { assert(signRawTxSpy.calledWith(mockSatExtractorApiResponse.tx)); assert(sendTxSpy.calledOnce); assert(sendTxSpy.calledWith('signedtx')); + assert(notificationSpy.calledTwice); }); it('should send common sats to exchange wallet', async () => { @@ -166,6 +168,7 @@ describe('Satminer', () => { const sendTxSpy = sinon.stub(mockWallet, 'sendRawTransaction').resolves(txid); const decodeRawTxSpy = sinon.stub(mockWallet, 'decodeRawTransaction').resolves({ vout: [{ scriptPubKey: { address: krakenDepoAddr, value: 0.1 } }] }); const signRawTxSpy = sinon.stub(mockWallet, 'signRawTransaction').resolves('signedtx'); + const notificationSpy = sinon.stub(notifications, 'sendMessage').resolves(true); satminer = new Satminer( mockWallet, @@ -176,6 +179,7 @@ describe('Satminer', () => { krakenDepoAddr, confirmationTargetBlocks, minDepositAmount, + notifications, ); const res = await satminer.extractSatsAndRotateFunds(); @@ -193,6 +197,7 @@ describe('Satminer', () => { assert(signRawTxSpy.calledWith(mockSatExtractorApiResponse.tx)); assert(sendTxSpy.calledOnce); assert(sendTxSpy.calledWith('signedtx')); + assert(notificationSpy.calledTwice); }); it('should throw if common sats sent to exchange wallet if below min deposit amount', async () => { @@ -226,6 +231,7 @@ describe('Satminer', () => { const sendTxSpy = sinon.stub(mockWallet, 'sendRawTransaction').resolves(txid); const decodeRawTxSpy = sinon.stub(mockWallet, 'decodeRawTransaction').resolves({ vout: [{ scriptPubKey: { address: krakenDepoAddr, value: 0.004 } }] }); const signRawTxSpy = sinon.stub(mockWallet, 'signRawTransaction').resolves('signedtx'); + const notificationSpy = sinon.stub(notifications, 'sendMessage').resolves(true); satminer = new Satminer( mockWallet, @@ -236,6 +242,7 @@ describe('Satminer', () => { krakenDepoAddr, confirmationTargetBlocks, minDepositAmount, + notifications, ); await assert.rejects(async () => satminer.extractSatsAndRotateFunds(), { message: 'deposit amount is less than minDepositAmount' }); @@ -253,6 +260,7 @@ describe('Satminer', () => { assert(signRawTxSpy.notCalled); assert(sendTxSpy.notCalled); assert(sendTxSpy.notCalled); + assert(notificationSpy.calledTwice); }); it('should throw if any funds go to non-user controlled addresses', async () => { @@ -287,6 +295,7 @@ describe('Satminer', () => { const sendTxSpy = sinon.stub(mockWallet, 'sendRawTransaction').resolves(txid); const decodeRawTxSpy = sinon.stub(mockWallet, 'decodeRawTransaction').resolves({ vout: [{ scriptPubKey: { address: randomAddress, value: 0.01 }}, { scriptPubKey: { address: krakenDepoAddr, value: 0.02 }} ] }); const signRawTxSpy = sinon.stub(mockWallet, 'signRawTransaction').resolves('signedtx'); + const notificationSpy = sinon.stub(notifications, 'sendMessage').resolves(true); satminer = new Satminer( mockWallet, @@ -297,6 +306,7 @@ describe('Satminer', () => { krakenDepoAddr, confirmationTargetBlocks, minDepositAmount, + notifications, ); await assert.rejects(async () => satminer.extractSatsAndRotateFunds(), { message: 'not all outputs are user-controlled' }); @@ -314,6 +324,7 @@ describe('Satminer', () => { assert(signRawTxSpy.notCalled); assert(sendTxSpy.notCalled); assert(sendTxSpy.notCalled); + assert(notificationSpy.calledTwice); }); it('should finish quietly when address is empty', async () => { @@ -366,6 +377,7 @@ describe('Satminer', () => { const mockMempoolApi = new MempoolApi(); sinon.stub(mockMempoolApi, 'getFeeEstimation').resolves(mockFeeEst); + const notificationSpy = sinon.stub(notifications, 'sendMessage').resolves(true); const scanner = new Satscanner(); @@ -374,9 +386,20 @@ describe('Satminer', () => { const inventoryWallet = 'inventorywalletaddr'; const krakenDepoAddr = 'krakendepoaddr'; const mockWallet = new Wallet(null, mockMempoolApi); - const satminer = new Satminer(mockWallet, scanner, satextractor, tumblerAddress, inventoryWallet, krakenDepoAddr, null, minDepositAmount); + const satminer = new Satminer( + mockWallet, + scanner, + satextractor, + tumblerAddress, + inventoryWallet, + krakenDepoAddr, + null, + minDepositAmount, + notifications, + ); await assert.rejects(async () => satminer.extractSatsAndRotateFunds(), { message: 'fee higher than max allowed 1000' }); + assert(notificationSpy.calledOnce); }); });