Merge pull request #134 from Permissionless-Software-Foundation/ct-unstable

feat(send transaction): Allowing specific node for broadcasting txs
This commit is contained in:
Chris Troutner
2021-05-13 09:38:34 -07:00
committed by GitHub
3 changed files with 39 additions and 7 deletions
+31 -7
View File
@@ -140,7 +140,7 @@ class RawTransactions {
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
// Loop through each height and creates an array of requests to call in parallel // Loop through each height and creates an array of requests to call in parallel
const promises = hexes.map(async hex => { const promises = hexes.map(async (hex) => {
options.data.id = 'decoderawtransaction' options.data.id = 'decoderawtransaction'
options.data.method = 'decoderawtransaction' options.data.method = 'decoderawtransaction'
options.data.params = [hex] options.data.params = [hex]
@@ -152,7 +152,7 @@ class RawTransactions {
const axiosResult = await _this.axios.all(promises) const axiosResult = await _this.axios.all(promises)
// Retrieve the data part of the result. // Retrieve the data part of the result.
const result = axiosResult.map(x => x.data.result) const result = axiosResult.map((x) => x.data.result)
res.status(200) res.status(200)
return res.json(result) return res.json(result)
@@ -254,7 +254,7 @@ class RawTransactions {
const options = _this.routeUtils.getAxiosOptions() const options = _this.routeUtils.getAxiosOptions()
// Loop through each hex and create an array of promises // Loop through each hex and create an array of promises
const promises = hexes.map(async hex => { const promises = hexes.map(async (hex) => {
options.data.id = 'decodescript' options.data.id = 'decodescript'
options.data.method = 'decodescript' options.data.method = 'decodescript'
options.data.params = [hex] options.data.params = [hex]
@@ -267,7 +267,7 @@ class RawTransactions {
const resolved = await Promise.all(promises) const resolved = await Promise.all(promises)
// Retrieve the data from each resolved promise. // Retrieve the data from each resolved promise.
const result = resolved.map(x => x.data.result) const result = resolved.map((x) => x.data.result)
res.status(200) res.status(200)
return res.json(result) return res.json(result)
@@ -348,7 +348,7 @@ class RawTransactions {
} }
// Loop through each txid and create an array of promises // Loop through each txid and create an array of promises
const promises = txids.map(async txid => const promises = txids.map(async (txid) =>
_this.getRawTransactionsFromNode(txid, verbose) _this.getRawTransactionsFromNode(txid, verbose)
) )
@@ -436,7 +436,8 @@ class RawTransactions {
return res.json({ error: 'hex must be an array' }) return res.json({ error: 'hex must be an array' })
} }
const options = _this.routeUtils.getAxiosOptions() let options = _this.routeUtils.getAxiosOptions()
options = _this.sendTxOptions(options)
// Enforce array size rate limits // Enforce array size rate limits
if (!_this.routeUtils.validateArraySize(req, hexes)) { if (!_this.routeUtils.validateArraySize(req, hexes)) {
@@ -536,7 +537,8 @@ class RawTransactions {
}) })
} }
const options = _this.routeUtils.getAxiosOptions() let options = _this.routeUtils.getAxiosOptions()
options = _this.sendTxOptions(options)
// RPC call // RPC call
options.data.id = 'sendrawtransaction' options.data.id = 'sendrawtransaction'
@@ -557,6 +559,28 @@ class RawTransactions {
return _this.errorHandler(err, res) return _this.errorHandler(err, res)
} }
} }
// This method modifies the default axios options. It attempts to inject
// a specific full node to use when broadcasting transactions. This is useful
// because it leverages the built-in protections that a full node has against
// accidental double spends. It mitigates a corner-case when rapidly spending
// TXs on load balanced nodes. By piping all TX sends through a single node,
// accidental double spends can be reduced.
sendTxOptions (options) {
try {
if (process.env.RPC_SENDURL) {
console.log(`original options: ${JSON.stringify(options, null, 2)}`)
options.baseURL = process.env.RPC_SENDURL
console.log(`modified options: ${JSON.stringify(options, null, 2)}`)
}
return options
} catch (err) {
wlogger.error('Error in rawtransactions.js/sendTxOptions()')
throw err
}
}
} }
module.exports = RawTransactions module.exports = RawTransactions
+5
View File
@@ -15,6 +15,11 @@ export RPC_BASEURL=http://$RPC_IP/
export RPC_USERNAME=bitcoin export RPC_USERNAME=bitcoin
export RPC_PASSWORD=password export RPC_PASSWORD=password
# (optional) If load-balancing multiple full nodes, it's best to pick a single
# one for broadcasting transactions to the network. Prevents accidental
# double spends.
#export RPC_SENDURL=http://$RPC_IP/
# SLPDB # SLPDB
export SLPDB_PASS_GP=somelongpassword export SLPDB_PASS_GP=somelongpassword
export SLPDB_URL=http://<SLPDB IP>:12300/ export SLPDB_URL=http://<SLPDB IP>:12300/
+3
View File
@@ -1047,10 +1047,12 @@ describe('#Raw-Transactions', () => {
// Save the existing RPC URL. // Save the existing RPC URL.
const savedUrl = process.env.BITCOINCOM_BASEURL const savedUrl = process.env.BITCOINCOM_BASEURL
const savedUrl2 = process.env.RPC_BASEURL const savedUrl2 = process.env.RPC_BASEURL
const savedUrl3 = process.env.RPC_SENDURL
// Manipulate the URL to cause a 500 network error. // Manipulate the URL to cause a 500 network error.
process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/' process.env.BITCOINCOM_BASEURL = 'http://fakeurl/api/'
process.env.RPC_BASEURL = 'http://fakeurl/api/' process.env.RPC_BASEURL = 'http://fakeurl/api/'
process.env.RPC_SENDURL = 'http://fakeurl/api/'
req.params.hex = req.params.hex =
'020000000136697692fed77bc4f5b6885295d0c56d1d0280fb578f445ce42be4eb6db381f2010000006a4730440220473adba0e7da14f0abf4817bbd591741ecb8da6544b998f10341f6704f5f05280220405221c626cb7edcf333367ebd469aff3f5a2169e37ee58eebb811ffc2fbc9e0412102202ff86325c5d903171fa5a2895c4efb3765105115460dc96f113048ddb69b47feffffff027a621b00000000001976a914e0a8ffc3b91e35f46618d6db90f66397989abf0588ac38041300000000001976a914a741f282af390bc7ea8c4375a3a56401d668564288ac2c330900' '020000000136697692fed77bc4f5b6885295d0c56d1d0280fb578f445ce42be4eb6db381f2010000006a4730440220473adba0e7da14f0abf4817bbd591741ecb8da6544b998f10341f6704f5f05280220405221c626cb7edcf333367ebd469aff3f5a2169e37ee58eebb811ffc2fbc9e0412102202ff86325c5d903171fa5a2895c4efb3765105115460dc96f113048ddb69b47feffffff027a621b00000000001976a914e0a8ffc3b91e35f46618d6db90f66397989abf0588ac38041300000000001976a914a741f282af390bc7ea8c4375a3a56401d668564288ac2c330900'
@@ -1060,6 +1062,7 @@ describe('#Raw-Transactions', () => {
// Restore the saved URL. // Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl process.env.BITCOINCOM_BASEURL = savedUrl
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
process.env.RPC_SENDURL = savedUrl3
assert.isAbove( assert.isAbove(
res.statusCode, res.statusCode,