Browse Source
Move closer to the original vision of supporting any address-querying implementation desired. Current options include ElectrumX (as before) and now blockchain.com and blockcypher.com since they were easy to support and are publicly/easily available (though ridiculously neither is yet to support bc1 addresses). - new env var option BTCEXP_ADDRESS_API, value can be electrumx, blockchain.com, blockcypher.com - update ElectrumX client connect to request v1.4 of API as all clients are now supposed to do - misc frontend improvements/cleanup for addressesfix-133-memory-crash
12 changed files with 519 additions and 187 deletions
@ -0,0 +1,79 @@ |
|||
var config = require("./../config.js"); |
|||
var coins = require("../coins.js"); |
|||
var utils = require("../utils.js"); |
|||
|
|||
var coinConfig = coins[config.coin]; |
|||
|
|||
var electrumAddressApi = require("./electrumAddressApi.js"); |
|||
var blockchainAddressApi = require("./blockchainAddressApi.js"); |
|||
var blockcypherAddressApi = require("./blockcypherAddressApi.js"); |
|||
|
|||
function getSupportedAddressApis() { |
|||
return ["blockchain.com", "blockcypher.com", "electrumx"]; |
|||
} |
|||
|
|||
function getCurrentAddressApiFeatureSupport() { |
|||
if (config.addressApi == "blockchain.com") { |
|||
return { |
|||
pageNumbers: true, |
|||
sortDesc: true, |
|||
sortAsc: true |
|||
}; |
|||
|
|||
} else if (config.addressApi == "blockcypher.com") { |
|||
return { |
|||
pageNumbers: true, |
|||
sortDesc: true, |
|||
sortAsc: false |
|||
}; |
|||
|
|||
} else if (config.addressApi == "electrumx") { |
|||
return { |
|||
pageNumbers: true, |
|||
sortDesc: true, |
|||
sortAsc: true |
|||
}; |
|||
} |
|||
} |
|||
|
|||
function getAddressDetails(address, scriptPubkey, sort, limit, offset) { |
|||
return new Promise(function(resolve, reject) { |
|||
var promises = []; |
|||
|
|||
if (config.addressApi == "blockchain.com") { |
|||
promises.push(blockchainAddressApi.getAddressDetails(address, scriptPubkey, sort, limit, offset)); |
|||
|
|||
} else if (config.addressApi == "blockcypher.com") { |
|||
promises.push(blockcypherAddressApi.getAddressDetails(address, scriptPubkey, sort, limit, offset)); |
|||
|
|||
} else if (config.addressApi == "electrumx") { |
|||
promises.push(electrumAddressApi.getAddressDetails(address, scriptPubkey, sort, limit, offset)); |
|||
|
|||
} else { |
|||
promises.push(new Promise(function(resolve, reject) { |
|||
resolve({addressDetails:null, errors:["No address API configured"]}); |
|||
})); |
|||
} |
|||
|
|||
Promise.all(promises).then(function(results) { |
|||
if (results && results.length > 0) { |
|||
resolve(results[0]); |
|||
|
|||
} else { |
|||
resolve(null); |
|||
} |
|||
}).catch(function(err) { |
|||
utils.logError("239x7rhsd0gs", err); |
|||
|
|||
reject(err); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
|
|||
|
|||
module.exports = { |
|||
getSupportedAddressApis: getSupportedAddressApis, |
|||
getCurrentAddressApiFeatureSupport: getCurrentAddressApiFeatureSupport, |
|||
getAddressDetails: getAddressDetails |
|||
}; |
@ -0,0 +1,116 @@ |
|||
var request = require("request"); |
|||
var utils = require("./../utils.js"); |
|||
|
|||
|
|||
function getAddressDetails(address, scriptPubkey, sort, limit, offset) { |
|||
return new Promise(function(resolve, reject) { |
|||
if (address.startsWith("bc1")) { |
|||
reject({userText:"blockchain.com API does not support bc1 (native Segwit) addresses"}); |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (sort == "asc") { |
|||
// need to query the total number of tx first, then build paging info from that value
|
|||
var options = { |
|||
url: `https://blockchain.info/rawaddr/${address}?limit=1`, |
|||
headers: { |
|||
'User-Agent': 'request' |
|||
} |
|||
}; |
|||
|
|||
request(options, function(error, response, body) { |
|||
if (error == null && response && response.statusCode && response.statusCode == 200) { |
|||
var blockchainJson = JSON.parse(body); |
|||
|
|||
var txCount = blockchainJson.n_tx; |
|||
var pageCount = parseInt(txCount / limit); |
|||
var lastPageSize = limit; |
|||
if (pageCount * limit < txCount) { |
|||
lastPageSize = txCount - pageCount * limit; |
|||
} |
|||
|
|||
var dynamicOffset = txCount - limit - offset; |
|||
if (dynamicOffset < 0) { |
|||
limit += dynamicOffset; |
|||
dynamicOffset += limit; |
|||
} |
|||
|
|||
getAddressDetailsSortDesc(address, limit, dynamicOffset).then(function(result) { |
|||
result.txids.reverse(); |
|||
|
|||
resolve({addressDetails:result}); |
|||
|
|||
}).catch(function(err) { |
|||
utils.logError("2308hsghse", err); |
|||
|
|||
reject(err); |
|||
}); |
|||
|
|||
} else { |
|||
var fullError = {error:error, response:response, body:body}; |
|||
|
|||
utils.logError("we0f8hasd0fhas", fullError); |
|||
|
|||
reject(fullError); |
|||
} |
|||
}); |
|||
} else { |
|||
getAddressDetailsSortDesc(address, limit, offset).then(function(result) { |
|||
resolve({addressDetails:result}); |
|||
|
|||
}).catch(function(err) { |
|||
utils.logError("3208hwssse", err); |
|||
|
|||
reject(err); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function getAddressDetailsSortDesc(address, limit, offset) { |
|||
return new Promise(function(resolve, reject) { |
|||
var options = { |
|||
url: `https://blockchain.info/rawaddr/${address}?limit=${limit}&offset=${offset}`, |
|||
headers: { |
|||
'User-Agent': 'request' |
|||
} |
|||
}; |
|||
|
|||
request(options, function(error, response, body) { |
|||
if (error == null && response && response.statusCode && response.statusCode == 200) { |
|||
var blockchainJson = JSON.parse(body); |
|||
|
|||
var response = {}; |
|||
|
|||
response.txids = []; |
|||
response.blockHeightsByTxid = {}; |
|||
blockchainJson.txs.forEach(function(tx) { |
|||
response.txids.push(tx.hash); |
|||
response.blockHeightsByTxid[tx.hash] = tx.block_height; |
|||
}); |
|||
|
|||
response.txCount = blockchainJson.n_tx; |
|||
response.hash160 = blockchainJson.hash160; |
|||
response.totalReceivedSat = blockchainJson.total_received; |
|||
response.totalSentSat = blockchainJson.total_sent; |
|||
response.balanceSat = blockchainJson.final_balance; |
|||
response.source = "blockchain.com"; |
|||
|
|||
resolve(response); |
|||
|
|||
} else { |
|||
var fullError = {error:error, response:response, body:body}; |
|||
|
|||
utils.logError("32907shsghs", fullError); |
|||
|
|||
reject(fullError); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
|
|||
module.exports = { |
|||
getAddressDetails: getAddressDetails |
|||
}; |
@ -0,0 +1,60 @@ |
|||
var request = require("request"); |
|||
|
|||
|
|||
function getAddressDetails(address, scriptPubkey, sort, limit, offset) { |
|||
return new Promise(function(resolve, reject) { |
|||
if (address.startsWith("bc1")) { |
|||
reject({userText:"blockcypher.com API does not support bc1 (native Segwit) addresses"}); |
|||
|
|||
return; |
|||
} |
|||
|
|||
var limitOffset = limit + offset; |
|||
|
|||
var options = { |
|||
url: `https://api.blockcypher.com/v1/btc/main/addrs/${address}?limit=${limitOffset}`, |
|||
headers: { |
|||
'User-Agent': 'request' |
|||
} |
|||
}; |
|||
|
|||
request(options, function(error, response, body) { |
|||
if (error == null && response && response.statusCode && response.statusCode == 200) { |
|||
var blockcypherJson = JSON.parse(body); |
|||
|
|||
var response = {}; |
|||
|
|||
response.txids = []; |
|||
response.blockHeightsByTxid = {}; |
|||
|
|||
// blockcypher doesn't support offset for paging, so simulate up to the hard cap of 2,000
|
|||
for (var i = offset; i < Math.min(blockcypherJson.txrefs.length, limitOffset); i++) { |
|||
var tx = blockcypherJson.txrefs[i]; |
|||
|
|||
response.txids.push(tx.tx_hash); |
|||
response.blockHeightsByTxid[tx.tx_hash] = tx.block_height; |
|||
} |
|||
|
|||
response.txCount = blockcypherJson.n_tx; |
|||
response.totalReceivedSat = blockcypherJson.total_received; |
|||
response.totalSentSat = blockcypherJson.total_sent; |
|||
response.balanceSat = blockcypherJson.final_balance; |
|||
response.source = "blockcypher.com"; |
|||
|
|||
resolve({addressDetails:response}); |
|||
|
|||
} else { |
|||
var fullError = {error:error, response:response, body:body}; |
|||
|
|||
utils.logError("097wef0adsgadgs", fullError); |
|||
|
|||
reject(fullError); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
|
|||
module.exports = { |
|||
getAddressDetails: getAddressDetails |
|||
}; |
Loading…
Reference in new issue