You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
5.9 KiB
238 lines
5.9 KiB
6 years ago
|
var debug = require("debug");
|
||
|
var debugLog = debug("btcexp:electrumx");
|
||
|
|
||
6 years ago
|
var config = require("./../config.js");
|
||
6 years ago
|
var coins = require("../coins.js");
|
||
|
var utils = require("../utils.js");
|
||
6 years ago
|
var sha256 = require("crypto-js/sha256");
|
||
|
var hexEnc = require("crypto-js/enc-hex");
|
||
|
|
||
6 years ago
|
var coinConfig = coins[config.coin];
|
||
6 years ago
|
|
||
|
const ElectrumClient = require('electrum-client');
|
||
|
|
||
|
var electrumClients = [];
|
||
|
|
||
|
function connectToServers() {
|
||
6 years ago
|
return new Promise(function(resolve, reject) {
|
||
|
var promises = [];
|
||
|
|
||
|
for (var i = 0; i < config.electrumXServers.length; i++) {
|
||
6 years ago
|
var { host, port, protocol } = config.electrumXServers[i];
|
||
|
promises.push(connectToServer(host, port, protocol));
|
||
6 years ago
|
}
|
||
|
|
||
|
Promise.all(promises).then(function() {
|
||
|
resolve();
|
||
|
|
||
|
}).catch(function(err) {
|
||
6 years ago
|
utils.logError("120387rygxx231gwe40", err);
|
||
6 years ago
|
|
||
|
reject(err);
|
||
|
});
|
||
|
});
|
||
6 years ago
|
}
|
||
|
|
||
6 years ago
|
function connectToServer(host, port, protocol) {
|
||
6 years ago
|
return new Promise(function(resolve, reject) {
|
||
6 years ago
|
debugLog("Connecting to ElectrumX Server: " + host + ":" + port);
|
||
6 years ago
|
|
||
6 years ago
|
// default protocol is 'tcp' if port is 50001, which is the default unencrypted port for electrumx
|
||
|
var defaultProtocol = port === 50001 ? 'tcp' : 'tls';
|
||
6 years ago
|
|
||
6 years ago
|
var electrumClient = new ElectrumClient(port, host, protocol || defaultProtocol);
|
||
6 years ago
|
electrumClient.initElectrum({client:"btc-rpc-explorer-v1.1", version:"1.4"}).then(function(res) {
|
||
|
debugLog("Connected to ElectrumX Server: " + host + ":" + port + ", versions: " + JSON.stringify(res));
|
||
6 years ago
|
|
||
|
electrumClients.push(electrumClient);
|
||
6 years ago
|
|
||
6 years ago
|
resolve();
|
||
6 years ago
|
|
||
6 years ago
|
}).catch(function(err) {
|
||
6 years ago
|
utils.logError("137rg023xx7gerfwdd", err, {host:host, port:port, protocol:protocol});
|
||
6 years ago
|
|
||
|
reject(err);
|
||
|
});
|
||
6 years ago
|
});
|
||
6 years ago
|
|
||
6 years ago
|
}
|
||
|
|
||
|
function runOnServer(electrumClient, f) {
|
||
|
return new Promise(function(resolve, reject) {
|
||
|
f(electrumClient).then(function(result) {
|
||
6 years ago
|
if (result.success) {
|
||
|
resolve({result:result.response, server:electrumClient.host});
|
||
6 years ago
|
|
||
6 years ago
|
} else {
|
||
|
reject({error:result.error, server:electrumClient.host});
|
||
|
}
|
||
6 years ago
|
}).catch(function(err) {
|
||
6 years ago
|
utils.logError("dif0e21qdh", err, {host:electrumClient.host, port:electrumClient.port});
|
||
6 years ago
|
|
||
|
reject(err);
|
||
6 years ago
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function runOnAllServers(f) {
|
||
|
return new Promise(function(resolve, reject) {
|
||
|
var promises = [];
|
||
|
|
||
|
for (var i = 0; i < electrumClients.length; i++) {
|
||
|
promises.push(runOnServer(electrumClients[i], f));
|
||
|
}
|
||
|
|
||
|
Promise.all(promises).then(function(results) {
|
||
|
resolve(results);
|
||
|
|
||
|
}).catch(function(err) {
|
||
|
reject(err);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
6 years ago
|
function getAddressDetails(address, scriptPubkey, sort, limit, offset) {
|
||
|
return new Promise(function(resolve, reject) {
|
||
|
var addrScripthash = hexEnc.stringify(sha256(hexEnc.parse(scriptPubkey)));
|
||
|
addrScripthash = addrScripthash.match(/.{2}/g).reverse().join("");
|
||
|
|
||
|
var promises = [];
|
||
|
|
||
|
var txidData = null;
|
||
|
var balanceData = null;
|
||
|
|
||
|
promises.push(new Promise(function(resolve, reject) {
|
||
|
getAddressTxids(addrScripthash).then(function(result) {
|
||
|
txidData = result.result;
|
||
|
|
||
|
resolve();
|
||
|
|
||
|
}).catch(function(err) {
|
||
|
utils.logError("2397wgs0sgse", err);
|
||
|
|
||
|
reject(err);
|
||
|
});
|
||
|
}));
|
||
|
|
||
|
promises.push(new Promise(function(resolve, reject) {
|
||
|
getAddressBalance(addrScripthash).then(function(result) {
|
||
|
balanceData = result.result;
|
||
|
|
||
|
resolve();
|
||
|
|
||
|
}).catch(function(err) {
|
||
|
utils.logError("21307ws70sg", err);
|
||
|
|
||
|
reject(err);
|
||
|
});
|
||
|
}));
|
||
|
|
||
|
Promise.all(promises.map(utils.reflectPromise)).then(function(results) {
|
||
|
var addressDetails = {};
|
||
|
|
||
|
if (txidData) {
|
||
|
addressDetails.txCount = txidData.length;
|
||
|
|
||
|
addressDetails.txids = [];
|
||
|
addressDetails.blockHeightsByTxid = {};
|
||
|
|
||
|
if (sort == "desc") {
|
||
|
txidData.reverse();
|
||
|
}
|
||
|
|
||
|
for (var i = offset; i < Math.min(txidData.length, limit + offset); i++) {
|
||
|
addressDetails.txids.push(txidData[i].tx_hash);
|
||
|
addressDetails.blockHeightsByTxid[txidData[i].tx_hash] = txidData[i].height;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (balanceData) {
|
||
|
addressDetails.balanceSat = balanceData.confirmed;
|
||
|
}
|
||
|
|
||
|
var errors = [];
|
||
|
results.forEach(function(x) {
|
||
|
if (x.status == "rejected") {
|
||
|
errors.push(x);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
resolve({addressDetails:addressDetails, errors:errors});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
6 years ago
|
function getAddressTxids(addrScripthash) {
|
||
|
return new Promise(function(resolve, reject) {
|
||
|
runOnAllServers(function(electrumClient) {
|
||
|
return electrumClient.blockchainScripthash_getHistory(addrScripthash);
|
||
|
|
||
|
}).then(function(results) {
|
||
6 years ago
|
debugLog(`getAddressTxids=${utils.ellipsize(JSON.stringify(results), 200)}`);
|
||
|
|
||
6 years ago
|
if (addrScripthash == coinConfig.genesisCoinbaseOutputAddressScripthash) {
|
||
|
for (var i = 0; i < results.length; i++) {
|
||
|
results[i].result.unshift({tx_hash:coinConfig.genesisCoinbaseTransactionId, height:0});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var first = results[0];
|
||
|
var done = false;
|
||
|
|
||
|
for (var i = 1; i < results.length; i++) {
|
||
|
if (results[i].length != first.length) {
|
||
|
resolve({conflictedResults:results});
|
||
6 years ago
|
|
||
6 years ago
|
done = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!done) {
|
||
|
resolve(results[0]);
|
||
|
}
|
||
6 years ago
|
}).catch(function(err) {
|
||
|
reject(err);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getAddressBalance(addrScripthash) {
|
||
|
return new Promise(function(resolve, reject) {
|
||
|
runOnAllServers(function(electrumClient) {
|
||
|
return electrumClient.blockchainScripthash_getBalance(addrScripthash);
|
||
|
|
||
|
}).then(function(results) {
|
||
6 years ago
|
debugLog(`getAddressBalance=${JSON.stringify(results)}`);
|
||
|
|
||
6 years ago
|
if (addrScripthash == coinConfig.genesisCoinbaseOutputAddressScripthash) {
|
||
|
for (var i = 0; i < results.length; i++) {
|
||
|
var coinbaseBlockReward = coinConfig.blockRewardFunction(0);
|
||
|
|
||
|
results[i].result.confirmed += (coinbaseBlockReward * coinConfig.baseCurrencyUnit.multiplier);
|
||
|
}
|
||
|
}
|
||
|
|
||
6 years ago
|
var first = results[0];
|
||
|
var done = false;
|
||
|
|
||
|
for (var i = 1; i < results.length; i++) {
|
||
|
if (results[i].confirmed != first.confirmed) {
|
||
|
resolve({conflictedResults:results});
|
||
6 years ago
|
|
||
6 years ago
|
done = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!done) {
|
||
|
resolve(results[0]);
|
||
|
}
|
||
|
}).catch(function(err) {
|
||
|
reject(err);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
connectToServers: connectToServers,
|
||
6 years ago
|
getAddressDetails: getAddressDetails
|
||
6 years ago
|
};
|