From af118f8a629e6c0add17b413aeee898256b39344 Mon Sep 17 00:00:00 2001 From: Stoyan Zhekov Date: Wed, 7 Oct 2020 17:14:30 +0900 Subject: [PATCH 1/2] Add Electrumx Broadcast TX method --- src/electrumx.js | 40 +++++++++++++++++++++++++++ test/integration/electrumx.js | 14 ++++++++++ test/integration/testnet/electrumx.js | 14 ++++++++++ test/unit/electrumx.js | 31 +++++++++++++++++++++ test/unit/fixtures/electrumx-mock.js | 8 +++++- 5 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/electrumx.js b/src/electrumx.js index 0bccf05..0c68d15 100644 --- a/src/electrumx.js +++ b/src/electrumx.js @@ -543,6 +543,46 @@ class ElectrumX { else throw error } } + + /** + * @api Electrumx.broadcast() broadcast() + * @apiName ElectrumX Broadcast + * @apiGroup ElectrumX + * @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure. + * + * (async () => { + * try { + * const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + * let result = await bchjs.Electrumx.broadcast(txHex) + * console.log(result); + * } catch(error) { + * console.error(error) + * } + * })() + * + * result = { + * "success": true, + * "txid": "..." + * } + */ + async broadcast(txHex) { + try { + if (typeof txHex === "string") { + const response = await axios.post( + `${this.restURL}electrumx/tx/broadcast`, + { txHex }, + _this.axiosOptions + ) + + return response.data + } + + throw new Error(`Input txHex must be a string.`) + } catch (error) { + if (error.response && error.response.data) throw error.response.data + else throw error + } + } } module.exports = ElectrumX diff --git a/test/integration/electrumx.js b/test/integration/electrumx.js index 02f07eb..8848c66 100644 --- a/test/integration/electrumx.js +++ b/test/integration/electrumx.js @@ -331,4 +331,18 @@ describe(`#ElectrumX`, () => { } }) }) + + describe(`#broadcast`, () => { + it(`should broadcast a single transaction`, async () => { + const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + + try { + await bchjs.Electrumx.broadcast(txHex) + } catch (err) { + assert.property(err, "success") + assert.equal(err.success, false) + assert.include(err.error, "Missing inputs") + } + }) + }) }) diff --git a/test/integration/testnet/electrumx.js b/test/integration/testnet/electrumx.js index 491ff38..1bedb8c 100644 --- a/test/integration/testnet/electrumx.js +++ b/test/integration/testnet/electrumx.js @@ -335,4 +335,18 @@ describe(`#ElectrumX`, () => { } }) }) + + describe(`#broadcast`, () => { + it(`should broadcast a single transaction`, async () => { + const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + + try { + await bchjs.Electrumx.broadcast(txHex) + } catch (err) { + assert.property(err, "success") + assert.equal(err.success, false) + assert.include(err.error, "Missing inputs") + } + }) + }) }) diff --git a/test/unit/electrumx.js b/test/unit/electrumx.js index 67d1aad..a203c21 100644 --- a/test/unit/electrumx.js +++ b/test/unit/electrumx.js @@ -368,4 +368,35 @@ describe(`#ElectrumX`, () => { assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs") }) }) + describe(`#broadcast`, () => { + it(`should throw an error for improper input`, async () => { + try { + const txHex = 12345 + + await bchjs.Electrumx.broadcast(txHex) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + // console.log(`err: `, err) + assert.include( + err.message, + `Input txHex must be a string.` + ) + } + }) + it(`should broadcast a single transaction`, async () => { + // Stub the network call. + sandbox.stub(axios, "post").resolves({ data: mockData.broadcast }) + + const tx = mockData.details + const txid = tx.details.txid + const result = await bchjs.Electrumx.broadcast(tx.details.hex) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "txid") + assert.equal(result.txid, txid) + }) + }) }) diff --git a/test/unit/fixtures/electrumx-mock.js b/test/unit/fixtures/electrumx-mock.js index 71774fc..1ca4819 100644 --- a/test/unit/fixtures/electrumx-mock.js +++ b/test/unit/fixtures/electrumx-mock.js @@ -250,6 +250,11 @@ const detailsArray = { ] } +const broadcast = { + success: true, + txid: txDetails.txid +} + module.exports = { utxo, utxos, @@ -261,5 +266,6 @@ module.exports = { unconfirmedArray, blockHeaders, details, - detailsArray + detailsArray, + broadcast } From 79088fc68dffaf69fe539e3381af556600bcc91b Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 9 Oct 2020 11:30:07 -0700 Subject: [PATCH 2/2] linting --- test/integration/electrumx.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/integration/electrumx.js b/test/integration/electrumx.js index 8848c66..80e3eee 100644 --- a/test/integration/electrumx.js +++ b/test/integration/electrumx.js @@ -271,7 +271,8 @@ describe(`#ElectrumX`, () => { describe(`#txData`, () => { it(`should GET details for a single transaction`, async () => { - const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + const txid = + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" const result = await bchjs.Electrumx.txData(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) @@ -318,8 +319,11 @@ describe(`#ElectrumX`, () => { it(`should throw error on array size rate limit`, async () => { try { const txids = [] - for (let i = 0; i < 25; i++) - txids.push("4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251") + for (let i = 0; i < 25; i++) { + txids.push( + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + ) + } await bchjs.Electrumx.txData(txids) @@ -334,7 +338,8 @@ describe(`#ElectrumX`, () => { describe(`#broadcast`, () => { it(`should broadcast a single transaction`, async () => { - const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + const txHex = + "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" try { await bchjs.Electrumx.broadcast(txHex)