@lily-technologies/electrum-client
Version:
Electrum protocol client for React Native & Node.js
267 lines (227 loc) • 7.72 kB
JavaScript
'use strict';
const Client = require('./lib/client');
class ElectrumClient extends Client {
constructor(port, host, protocol, options, callbacks) {
super(port, host, protocol, options, callbacks);
this.onConnectCallback = (callbacks && callbacks.onConnect) ? callbacks.onConnect : null;
this.onCloseCallback = (callbacks && callbacks.onClose) ? callbacks.onClose : null;
this.onLogCallback = (callbacks && callbacks.onLog) ? callbacks.onLog : function(str) {
console.log(str);
};
this.timeLastCall = 0;
}
initElectrum(electrumConfig, persistencePolicy = { retryPeriod: 10000, maxRetry: 1000, pingPeriod: 120000, callback: null }) {
this.persistencePolicy = persistencePolicy;
this.electrumConfig = electrumConfig;
this.timeLastCall = 0;
return new Promise((resolve, reject) => {
this.connect().then(() => {
this.server_version(this.electrumConfig.client, this.electrumConfig.version).then((versionInfo) => {
this.versionInfo = versionInfo;
if (this.onConnectCallback != null) {
this.onConnectCallback(this, this.versionInfo);
}
resolve(this);
}).catch((err) => {
reject(err);
});
}).catch((err) => {
reject(err);
});
});
}
// Override parent
request(method, params) {
this.timeLastCall = new Date().getTime();
const parentPromise = super.request(method, params);
return parentPromise.then(response => {
this.keepAlive();
return response;
});
}
requestBatch(method, params, secondParam) {
this.timeLastCall = new Date().getTime();
const parentPromise = super.requestBatch(method, params, secondParam);
return parentPromise.then(response => {
this.keepAlive();
return response;
});
}
onClose() {
super.onClose();
const list = [
'server.peers.subscribe',
'blockchain.numblocks.subscribe',
'blockchain.headers.subscribe',
'blockchain.address.subscribe',
];
list.forEach(event => this.subscribe.removeAllListeners(event));
var retryPeriod = 10000;
if (this.persistencePolicy != null && this.persistencePolicy.retryPeriod > 0) {
retryPeriod = this.persistencePolicy.retryPeriod;
}
if (this.onCloseCallback != null) {
this.onCloseCallback(this);
}
setTimeout(() => {
if (this.persistencePolicy != null && this.persistencePolicy.maxRetry > 0) {
this.reconnect().catch((err) => {
this.onError(err);
});
this.persistencePolicy.maxRetry -= 1;
} else if (this.persistencePolicy != null && this.persistencePolicy.callback != null) {
this.persistencePolicy.callback();
} else if (this.persistencePolicy == null) {
this.reconnect().catch((err) => {
this.onError(err);
});
}
}, retryPeriod);
}
// ElectrumX persistancy
keepAlive() {
if (this.timeout != null) {
clearTimeout(this.timeout);
}
var pingPeriod = 120000;
if (this.persistencePolicy != null && this.persistencePolicy.pingPeriod > 0) {
pingPeriod = this.persistencePolicy.pingPeriod;
}
this.timeout = setTimeout(() => {
if (this.timeLastCall !== 0 && new Date().getTime() > this.timeLastCall + pingPeriod) {
this.server_ping().catch((reason) => {
this.log('Keep-Alive ping failed: ', reason);
});
}
}, pingPeriod);
}
close() {
super.close();
if (this.timeout != null) {
clearTimeout(this.timeout);
}
this.reconnect = this.reconnect = this.onClose = this.keepAlive = () => {}; // dirty hack to make it stop reconnecting
}
reconnect() {
this.log("Electrum attempting reconnect...");
this.initSocket();
if (this.persistencePolicy != null) {
return this.initElectrum(this.electrumConfig, this.persistencePolicy);
} else {
return this.initElectrum(this.electrumConfig);
}
}
log(str) {
this.onLogCallback(str);
}
// ElectrumX API
server_version(client_name, protocol_version) {
return this.request('server.version', [client_name, protocol_version]);
}
server_banner() {
return this.request('server.banner', []);
}
server_features() {
return this.request('server.features', []);
}
server_ping() {
return this.request('server.ping', []);
}
server_addPeer(features) {
return this.request('server.add_peer', [features]);
}
serverDonation_address() {
return this.request('server.donation_address', []);
}
serverPeers_subscribe() {
return this.request('server.peers.subscribe', []);
}
blockchainAddress_getProof(address) {
return this.request('blockchain.address.get_proof', [address]);
}
blockchainScripthash_getBalance(scripthash) {
return this.request('blockchain.scripthash.get_balance', [scripthash]);
}
blockchainScripthash_getBalanceBatch(scripthash) {
return this.requestBatch('blockchain.scripthash.get_balance', scripthash);
}
blockchainScripthash_listunspentBatch(scripthash) {
return this.requestBatch('blockchain.scripthash.listunspent', scripthash);
}
blockchainScripthash_getHistory(scripthash) {
return this.request('blockchain.scripthash.get_history', [scripthash]);
}
blockchainScripthash_getHistoryBatch(scripthash) {
return this.requestBatch('blockchain.scripthash.get_history', scripthash);
}
blockchainScripthash_getMempool(scripthash) {
return this.request('blockchain.scripthash.get_mempool', [scripthash]);
}
blockchainScripthash_listunspent(scripthash) {
return this.request('blockchain.scripthash.listunspent', [scripthash]);
}
blockchainScripthash_subscribe(scripthash) {
return this.request('blockchain.scripthash.subscribe', [scripthash]);
}
blockchainBlock_getHeader(height) {
return this.request('blockchain.block.get_header', [height]);
}
blockchainBlock_headers(start_height, count) {
return this.request('blockchain.block.headeres', [start_height, count]);
}
blockchainEstimatefee(number) {
return this.request('blockchain.estimatefee', [number]);
}
blockchainHeaders_subscribe() {
return this.request('blockchain.headers.subscribe');
}
blockchain_relayfee() {
return this.request('blockchain.relayfee', []);
}
blockchainTransaction_broadcast(rawtx) {
return this.request('blockchain.transaction.broadcast', [rawtx]);
}
blockchainTransaction_get(tx_hash, verbose) {
return this.request('blockchain.transaction.get', [tx_hash, verbose || false]);
}
blockchainTransaction_getBatch(tx_hash, verbose) {
return this.requestBatch('blockchain.transaction.get', tx_hash, verbose);
}
blockchainTransaction_getMerkle(tx_hash, height) {
return this.request('blockchain.transaction.get_merkle', [tx_hash, height]);
}
mempool_getFeeHistogram() {
return this.request('mempool.get_fee_histogram', []);
}
// ---------------------------------
// protocol 1.1 deprecated method
// ---------------------------------
blockchainUtxo_getAddress(tx_hash, index) {
return this.request('blockchain.utxo.get_address', [tx_hash, index]);
}
blockchainNumblocks_subscribe() {
return this.request('blockchain.numblocks.subscribe', []);
}
// ---------------------------------
// protocol 1.2 deprecated method
// ---------------------------------
blockchainBlock_getChunk(index) {
return this.request('blockchain.block.get_chunk', [index]);
}
blockchainAddress_getBalance(address) {
return this.request('blockchain.address.get_balance', [address]);
}
blockchainAddress_getHistory(address) {
return this.request('blockchain.address.get_history', [address]);
}
blockchainAddress_getMempool(address) {
return this.request('blockchain.address.get_mempool', [address]);
}
blockchainAddress_listunspent(address) {
return this.request('blockchain.address.listunspent', [address]);
}
blockchainAddress_subscribe(address) {
return this.request('blockchain.address.subscribe', [address]);
}
}
module.exports = ElectrumClient;