mirror of
https://github.com/alexgo-io/electrum-client.git
synced 2026-01-12 16:53:05 +08:00
spaces -> tabs
This commit is contained in:
384
index.js
384
index.js
@@ -6,209 +6,209 @@ const debug = require("debug");
|
||||
const debugLog = debug("electrumClient");
|
||||
|
||||
class ElectrumClient extends Client {
|
||||
constructor(port, host, protocol, options) {
|
||||
super(port, host, protocol, options);
|
||||
this.timeLastCall = 0;
|
||||
}
|
||||
constructor(port, host, protocol, options) {
|
||||
super(port, host, protocol, options);
|
||||
this.timeLastCall = 0;
|
||||
}
|
||||
|
||||
initElectrum(electrumConfig, persistencePolicy = { retryPeriod: 5000, maxRetry: 1000, callback: null }) {
|
||||
this.persistencePolicy = persistencePolicy;
|
||||
this.electrumConfig = electrumConfig;
|
||||
this.timeLastCall = 0;
|
||||
return this.connect().then(() => this.server_version(this.electrumConfig.client, this.electrumConfig.version)).catch((err) => {
|
||||
debugLog("Error connecting to Electrum: " + err);
|
||||
});
|
||||
}
|
||||
initElectrum(electrumConfig, persistencePolicy = { retryPeriod: 5000, maxRetry: 1000, callback: null }) {
|
||||
this.persistencePolicy = persistencePolicy;
|
||||
this.electrumConfig = electrumConfig;
|
||||
this.timeLastCall = 0;
|
||||
return this.connect().then(() => this.server_version(this.electrumConfig.client, this.electrumConfig.version)).catch((err) => {
|
||||
debugLog("Error connecting to Electrum: " + 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;
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
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));
|
||||
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 = 5000;
|
||||
if (this.persistencePolicy != null && this.persistencePolicy.retryPeriod > 0) {
|
||||
retryPeriod = this.persistencePolicy.retryPeriod;
|
||||
}
|
||||
var retryPeriod = 5000;
|
||||
if (this.persistencePolicy != null && this.persistencePolicy.retryPeriod > 0) {
|
||||
retryPeriod = this.persistencePolicy.retryPeriod;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.persistencePolicy != null && this.persistencePolicy.maxRetry > 0) {
|
||||
this.reconnect();
|
||||
this.persistencePolicy.maxRetry -= 1;
|
||||
} else if (this.persistencePolicy != null && this.persistencePolicy.callback != null) {
|
||||
this.persistencePolicy.callback();
|
||||
} else if (this.persistencePolicy == null) {
|
||||
this.reconnect();
|
||||
}
|
||||
}, retryPeriod);
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.persistencePolicy != null && this.persistencePolicy.maxRetry > 0) {
|
||||
this.reconnect();
|
||||
this.persistencePolicy.maxRetry -= 1;
|
||||
} else if (this.persistencePolicy != null && this.persistencePolicy.callback != null) {
|
||||
this.persistencePolicy.callback();
|
||||
} else if (this.persistencePolicy == null) {
|
||||
this.reconnect();
|
||||
}
|
||||
}, retryPeriod);
|
||||
}
|
||||
|
||||
// ElectrumX persistancy
|
||||
keepAlive() {
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
this.timeout = setTimeout(() => {
|
||||
if (this.timeLastCall !== 0 && new Date().getTime() > this.timeLastCall + 5000) {
|
||||
this.server_ping().catch((reason) => {
|
||||
debugLog('Keep-Alive ping failed: ', reason);
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
// ElectrumX persistancy
|
||||
keepAlive() {
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
this.timeout = setTimeout(() => {
|
||||
if (this.timeLastCall !== 0 && new Date().getTime() > this.timeLastCall + 5000) {
|
||||
this.server_ping().catch((reason) => {
|
||||
debugLog('Keep-Alive ping failed: ', reason);
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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() {
|
||||
debugLog("Electrum reconnecting...");
|
||||
this.initSocket();
|
||||
reconnect() {
|
||||
debugLog("Electrum reconnecting...");
|
||||
this.initSocket();
|
||||
|
||||
if (this.persistencePolicy != null) {
|
||||
return this.initElectrum(this.electrumConfig, this.persistencePolicy);
|
||||
|
||||
} else {
|
||||
return this.initElectrum(this.electrumConfig);
|
||||
}
|
||||
}
|
||||
if (this.persistencePolicy != null) {
|
||||
return this.initElectrum(this.electrumConfig, this.persistencePolicy);
|
||||
|
||||
} else {
|
||||
return this.initElectrum(this.electrumConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// 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(raw) {
|
||||
return this.request('blockchain.headers.subscribe', [raw || false]);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
// 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(raw) {
|
||||
return this.request('blockchain.headers.subscribe', [raw || false]);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -7,123 +7,123 @@ const debugLog = debug("electrumClient");
|
||||
* real TLS socket will be created (TLS socket created after connection).
|
||||
*/
|
||||
class TlsSocketWrapper {
|
||||
constructor(tls) {
|
||||
this._tls = tls; // dependency injection lol
|
||||
this._socket = false;
|
||||
// defaults:
|
||||
this._timeout = 5000;
|
||||
this._encoding = 'utf8';
|
||||
this._keepAliveEneblad = true;
|
||||
this._keepAliveinitialDelay = 0;
|
||||
this._noDelay = true;
|
||||
this._listeners = {};
|
||||
}
|
||||
constructor(tls) {
|
||||
this._tls = tls; // dependency injection lol
|
||||
this._socket = false;
|
||||
// defaults:
|
||||
this._timeout = 5000;
|
||||
this._encoding = 'utf8';
|
||||
this._keepAliveEneblad = true;
|
||||
this._keepAliveinitialDelay = 0;
|
||||
this._noDelay = true;
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
setTimeout(timeout) {
|
||||
if (this._socket) this._socket.setTimeout(timeout);
|
||||
this._timeout = timeout;
|
||||
}
|
||||
setTimeout(timeout) {
|
||||
if (this._socket) this._socket.setTimeout(timeout);
|
||||
this._timeout = timeout;
|
||||
}
|
||||
|
||||
setEncoding(encoding) {
|
||||
if (this._socket) this._socket.setEncoding(encoding);
|
||||
this._encoding = encoding;
|
||||
}
|
||||
setEncoding(encoding) {
|
||||
if (this._socket) this._socket.setEncoding(encoding);
|
||||
this._encoding = encoding;
|
||||
}
|
||||
|
||||
setKeepAlive(enabled, initialDelay) {
|
||||
if (this._socket) this._socket.setKeepAlive(enabled, initialDelay);
|
||||
this._keepAliveEneblad = enabled;
|
||||
this._keepAliveinitialDelay = initialDelay;
|
||||
}
|
||||
setKeepAlive(enabled, initialDelay) {
|
||||
if (this._socket) this._socket.setKeepAlive(enabled, initialDelay);
|
||||
this._keepAliveEneblad = enabled;
|
||||
this._keepAliveinitialDelay = initialDelay;
|
||||
}
|
||||
|
||||
setNoDelay(noDelay) {
|
||||
if (this._socket) this._socket.setNoDelay(noDelay);
|
||||
this._noDelay = noDelay;
|
||||
}
|
||||
setNoDelay(noDelay) {
|
||||
if (this._socket) this._socket.setNoDelay(noDelay);
|
||||
this._noDelay = noDelay;
|
||||
}
|
||||
|
||||
on(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
this._listeners[event].push(listener);
|
||||
}
|
||||
on(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
this._listeners[event].push(listener);
|
||||
}
|
||||
|
||||
removeListener(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
let newListeners = [];
|
||||
removeListener(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
let newListeners = [];
|
||||
|
||||
let found = false;
|
||||
for (let savedListener of this._listeners[event]) {
|
||||
if (savedListener == listener) {
|
||||
// found our listener
|
||||
found = true;
|
||||
// we just skip it
|
||||
} else {
|
||||
// other listeners should go back to original array
|
||||
newListeners.push(savedListener);
|
||||
}
|
||||
}
|
||||
let found = false;
|
||||
for (let savedListener of this._listeners[event]) {
|
||||
if (savedListener == listener) {
|
||||
// found our listener
|
||||
found = true;
|
||||
// we just skip it
|
||||
} else {
|
||||
// other listeners should go back to original array
|
||||
newListeners.push(savedListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
this._listeners[event] = newListeners;
|
||||
} else {
|
||||
// something went wrong, lets just cleanup all listeners
|
||||
this._listeners[event] = [];
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
this._listeners[event] = newListeners;
|
||||
} else {
|
||||
// something went wrong, lets just cleanup all listeners
|
||||
this._listeners[event] = [];
|
||||
}
|
||||
}
|
||||
|
||||
connect(port, host, callback) {
|
||||
// resulting TLSSocket extends <net.Socket>
|
||||
this._socket = this._tls.connect({ port: port, host: host, rejectUnauthorized: false }, () => {
|
||||
debugLog('TLS Connected to ', host, port);
|
||||
return callback();
|
||||
});
|
||||
connect(port, host, callback) {
|
||||
// resulting TLSSocket extends <net.Socket>
|
||||
this._socket = this._tls.connect({ port: port, host: host, rejectUnauthorized: false }, () => {
|
||||
debugLog('TLS Connected to ', host, port);
|
||||
return callback();
|
||||
});
|
||||
|
||||
// setting everything that was set to this proxy class
|
||||
// setting everything that was set to this proxy class
|
||||
|
||||
this._socket.setTimeout(this._timeout);
|
||||
this._socket.setEncoding(this._encoding);
|
||||
this._socket.setKeepAlive(this._keepAliveEneblad, this._keepAliveinitialDelay);
|
||||
this._socket.setNoDelay(this._noDelay);
|
||||
this._socket.setTimeout(this._timeout);
|
||||
this._socket.setEncoding(this._encoding);
|
||||
this._socket.setKeepAlive(this._keepAliveEneblad, this._keepAliveinitialDelay);
|
||||
this._socket.setNoDelay(this._noDelay);
|
||||
|
||||
// resubscribing to events on newly created socket so we could proxy them to already established listeners
|
||||
// resubscribing to events on newly created socket so we could proxy them to already established listeners
|
||||
|
||||
this._socket.on('data', data => {
|
||||
this._passOnEvent('data', data);
|
||||
});
|
||||
this._socket.on('error', data => {
|
||||
this._passOnEvent('error', data);
|
||||
});
|
||||
this._socket.on('close', data => {
|
||||
this._passOnEvent('close', data);
|
||||
});
|
||||
this._socket.on('connect', data => {
|
||||
this._passOnEvent('connect', data);
|
||||
});
|
||||
this._socket.on('connection', data => {
|
||||
this._passOnEvent('connection', data);
|
||||
});
|
||||
}
|
||||
this._socket.on('data', data => {
|
||||
this._passOnEvent('data', data);
|
||||
});
|
||||
this._socket.on('error', data => {
|
||||
this._passOnEvent('error', data);
|
||||
});
|
||||
this._socket.on('close', data => {
|
||||
this._passOnEvent('close', data);
|
||||
});
|
||||
this._socket.on('connect', data => {
|
||||
this._passOnEvent('connect', data);
|
||||
});
|
||||
this._socket.on('connection', data => {
|
||||
this._passOnEvent('connection', data);
|
||||
});
|
||||
}
|
||||
|
||||
_passOnEvent(event, data) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
for (let savedListener of this._listeners[event]) {
|
||||
savedListener(data);
|
||||
}
|
||||
}
|
||||
_passOnEvent(event, data) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
for (let savedListener of this._listeners[event]) {
|
||||
savedListener(data);
|
||||
}
|
||||
}
|
||||
|
||||
emit(event, data) {
|
||||
this._socket.emit(event, data);
|
||||
}
|
||||
emit(event, data) {
|
||||
this._socket.emit(event, data);
|
||||
}
|
||||
|
||||
end() {
|
||||
this._socket.end();
|
||||
}
|
||||
end() {
|
||||
this._socket.end();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._socket.destroy();
|
||||
}
|
||||
destroy() {
|
||||
this._socket.destroy();
|
||||
}
|
||||
|
||||
write(data) {
|
||||
this._socket.write(data);
|
||||
}
|
||||
write(data) {
|
||||
this._socket.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TlsSocketWrapper;
|
||||
|
||||
344
lib/client.js
344
lib/client.js
@@ -2,12 +2,12 @@
|
||||
/**
|
||||
* expecting NET & TLS to be injected from outside:
|
||||
* for RN it should be in shim.js:
|
||||
* global.net = require('react-native-tcp');
|
||||
* global.tls = require('react-native-tcp/tls');
|
||||
* global.net = require('react-native-tcp');
|
||||
* global.tls = require('react-native-tcp/tls');
|
||||
*
|
||||
* for nodejs tests it should be provided before tests:
|
||||
* global.net = require('net');
|
||||
* global.tls = require('tls');
|
||||
* global.net = require('net');
|
||||
* global.tls = require('tls');
|
||||
* */
|
||||
let net = global.net;
|
||||
let tls = global.tls;
|
||||
@@ -21,192 +21,192 @@ const debug = require("debug");
|
||||
const debugLog = debug("electrumClient");
|
||||
|
||||
class Client {
|
||||
constructor(port, host, protocol, options) {
|
||||
this.id = 0;
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.callback_message_queue = {};
|
||||
this.subscribe = new EventEmitter();
|
||||
this.mp = new util.MessageParser((body, n) => {
|
||||
this.onMessage(body, n);
|
||||
});
|
||||
this._protocol = protocol; // saving defaults
|
||||
this._options = options;
|
||||
this.afterConnect = null;
|
||||
this.afterClose = null;
|
||||
this.initSocket(protocol, options);
|
||||
}
|
||||
constructor(port, host, protocol, options) {
|
||||
this.id = 0;
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.callback_message_queue = {};
|
||||
this.subscribe = new EventEmitter();
|
||||
this.mp = new util.MessageParser((body, n) => {
|
||||
this.onMessage(body, n);
|
||||
});
|
||||
this._protocol = protocol; // saving defaults
|
||||
this._options = options;
|
||||
this.afterConnect = null;
|
||||
this.afterClose = null;
|
||||
this.initSocket(protocol, options);
|
||||
}
|
||||
|
||||
initSocket(protocol, options) {
|
||||
protocol = protocol || this._protocol;
|
||||
options = options || this._options;
|
||||
switch (protocol) {
|
||||
case 'tcp':
|
||||
this.conn = new net.Socket();
|
||||
break;
|
||||
case 'tls':
|
||||
case 'ssl':
|
||||
if (!tls) {
|
||||
throw new Error('tls package could not be loaded');
|
||||
}
|
||||
this.conn = new TlsSocketWrapper(tls);
|
||||
break;
|
||||
default:
|
||||
throw new Error('unknown protocol');
|
||||
}
|
||||
initSocket(protocol, options) {
|
||||
protocol = protocol || this._protocol;
|
||||
options = options || this._options;
|
||||
switch (protocol) {
|
||||
case 'tcp':
|
||||
this.conn = new net.Socket();
|
||||
break;
|
||||
case 'tls':
|
||||
case 'ssl':
|
||||
if (!tls) {
|
||||
throw new Error('tls package could not be loaded');
|
||||
}
|
||||
this.conn = new TlsSocketWrapper(tls);
|
||||
break;
|
||||
default:
|
||||
throw new Error('unknown protocol');
|
||||
}
|
||||
|
||||
this.conn.setTimeout(TIMEOUT);
|
||||
this.conn.setEncoding('utf8');
|
||||
this.conn.setKeepAlive(true, 0);
|
||||
this.conn.setNoDelay(true);
|
||||
this.conn.on('connect', () => {
|
||||
this.conn.setTimeout(0);
|
||||
this.onConnect();
|
||||
});
|
||||
this.conn.on('close', e => {
|
||||
this.onClose(e);
|
||||
});
|
||||
this.conn.on('data', chunk => {
|
||||
this.conn.setTimeout(0);
|
||||
this.onRecv(chunk);
|
||||
});
|
||||
this.conn.on('error', e => {
|
||||
this.onError(e);
|
||||
});
|
||||
this.status = 0;
|
||||
}
|
||||
this.conn.setTimeout(TIMEOUT);
|
||||
this.conn.setEncoding('utf8');
|
||||
this.conn.setKeepAlive(true, 0);
|
||||
this.conn.setNoDelay(true);
|
||||
this.conn.on('connect', () => {
|
||||
this.conn.setTimeout(0);
|
||||
this.onConnect();
|
||||
});
|
||||
this.conn.on('close', e => {
|
||||
this.onClose(e);
|
||||
});
|
||||
this.conn.on('data', chunk => {
|
||||
this.conn.setTimeout(0);
|
||||
this.onRecv(chunk);
|
||||
});
|
||||
this.conn.on('error', e => {
|
||||
this.onError(e);
|
||||
});
|
||||
this.status = 0;
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (this.status === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.status = 1;
|
||||
return this.connectSocket(this.conn, this.port, this.host);
|
||||
}
|
||||
connect() {
|
||||
if (this.status === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.status = 1;
|
||||
return this.connectSocket(this.conn, this.port, this.host);
|
||||
}
|
||||
|
||||
connectSocket(conn, port, host) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const errorHandler = e => reject(e);
|
||||
|
||||
conn.on('error', errorHandler);
|
||||
connectSocket(conn, port, host) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const errorHandler = e => reject(e);
|
||||
|
||||
conn.on('error', errorHandler);
|
||||
|
||||
conn.connect(port, host, () => {
|
||||
conn.removeListener('error', errorHandler);
|
||||
conn.connect(port, host, () => {
|
||||
conn.removeListener('error', errorHandler);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.status === 0) {
|
||||
return;
|
||||
}
|
||||
this.conn.end();
|
||||
this.conn.destroy();
|
||||
this.status = 0;
|
||||
}
|
||||
close() {
|
||||
if (this.status === 0) {
|
||||
return;
|
||||
}
|
||||
this.conn.end();
|
||||
this.conn.destroy();
|
||||
this.status = 0;
|
||||
}
|
||||
|
||||
request(method, params) {
|
||||
if (this.status === 0) {
|
||||
return Promise.reject(new Error('Connection to server lost, please retry'));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = ++this.id;
|
||||
const content = util.makeRequest(method, params, id);
|
||||
this.callback_message_queue[id] = util.createPromiseResult(resolve, reject);
|
||||
this.conn.write(content + '\n');
|
||||
});
|
||||
}
|
||||
request(method, params) {
|
||||
if (this.status === 0) {
|
||||
return Promise.reject(new Error('Connection to server lost, please retry'));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = ++this.id;
|
||||
const content = util.makeRequest(method, params, id);
|
||||
this.callback_message_queue[id] = util.createPromiseResult(resolve, reject);
|
||||
this.conn.write(content + '\n');
|
||||
});
|
||||
}
|
||||
|
||||
requestBatch(method, params, secondParam) {
|
||||
if (this.status === 0) {
|
||||
return Promise.reject(new Error('Connection to server lost, please retry'));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let arguments_far_calls = {};
|
||||
let contents = [];
|
||||
for (let param of params) {
|
||||
const id = ++this.id;
|
||||
if (secondParam !== undefined) {
|
||||
contents.push(util.makeRequest(method, [param, secondParam], id));
|
||||
} else {
|
||||
contents.push(util.makeRequest(method, [param], id));
|
||||
}
|
||||
arguments_far_calls[id] = param;
|
||||
}
|
||||
const content = '[' + contents.join(',') + ']';
|
||||
this.callback_message_queue[this.id] = util.createPromiseResultBatch(resolve, reject, arguments_far_calls);
|
||||
// callback will exist only for max id
|
||||
this.conn.write(content + '\n');
|
||||
});
|
||||
}
|
||||
requestBatch(method, params, secondParam) {
|
||||
if (this.status === 0) {
|
||||
return Promise.reject(new Error('Connection to server lost, please retry'));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let arguments_far_calls = {};
|
||||
let contents = [];
|
||||
for (let param of params) {
|
||||
const id = ++this.id;
|
||||
if (secondParam !== undefined) {
|
||||
contents.push(util.makeRequest(method, [param, secondParam], id));
|
||||
} else {
|
||||
contents.push(util.makeRequest(method, [param], id));
|
||||
}
|
||||
arguments_far_calls[id] = param;
|
||||
}
|
||||
const content = '[' + contents.join(',') + ']';
|
||||
this.callback_message_queue[this.id] = util.createPromiseResultBatch(resolve, reject, arguments_far_calls);
|
||||
// callback will exist only for max id
|
||||
this.conn.write(content + '\n');
|
||||
});
|
||||
}
|
||||
|
||||
response(msg) {
|
||||
let callback;
|
||||
if (!msg.id && msg[0] && msg[0].id) {
|
||||
// this is a response from batch request
|
||||
for (let m of msg) {
|
||||
if (m.id && this.callback_message_queue[m.id]) {
|
||||
callback = this.callback_message_queue[m.id];
|
||||
delete this.callback_message_queue[m.id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback = this.callback_message_queue[msg.id];
|
||||
}
|
||||
response(msg) {
|
||||
let callback;
|
||||
if (!msg.id && msg[0] && msg[0].id) {
|
||||
// this is a response from batch request
|
||||
for (let m of msg) {
|
||||
if (m.id && this.callback_message_queue[m.id]) {
|
||||
callback = this.callback_message_queue[m.id];
|
||||
delete this.callback_message_queue[m.id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback = this.callback_message_queue[msg.id];
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
delete this.callback_message_queue[msg.id];
|
||||
if (msg.error) {
|
||||
callback(msg.error);
|
||||
} else {
|
||||
callback(null, msg.result || msg);
|
||||
}
|
||||
} else {
|
||||
debugLog("Error getting callback while handling response.");
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
delete this.callback_message_queue[msg.id];
|
||||
if (msg.error) {
|
||||
callback(msg.error);
|
||||
} else {
|
||||
callback(null, msg.result || msg);
|
||||
}
|
||||
} else {
|
||||
debugLog("Error getting callback while handling response.");
|
||||
}
|
||||
}
|
||||
|
||||
onMessage(body, n) {
|
||||
const msg = JSON.parse(body);
|
||||
if (msg instanceof Array) {
|
||||
this.response(msg);
|
||||
} else {
|
||||
if (msg.id !== void 0) {
|
||||
this.response(msg);
|
||||
} else {
|
||||
this.subscribe.emit(msg.method, msg.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
onMessage(body, n) {
|
||||
const msg = JSON.parse(body);
|
||||
if (msg instanceof Array) {
|
||||
this.response(msg);
|
||||
} else {
|
||||
if (msg.id !== void 0) {
|
||||
this.response(msg);
|
||||
} else {
|
||||
this.subscribe.emit(msg.method, msg.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
if (this.afterConnect != null) {
|
||||
this.afterConnect(this);
|
||||
}
|
||||
}
|
||||
onConnect() {
|
||||
if (this.afterConnect != null) {
|
||||
this.afterConnect(this);
|
||||
}
|
||||
}
|
||||
|
||||
onClose(e) {
|
||||
this.status = 0;
|
||||
onClose(e) {
|
||||
this.status = 0;
|
||||
|
||||
if (this.afterClose != null) {
|
||||
this.afterClose(this);
|
||||
}
|
||||
if (this.afterClose != null) {
|
||||
this.afterClose(this);
|
||||
}
|
||||
|
||||
Object.keys(this.callback_message_queue).forEach(key => {
|
||||
this.callback_message_queue[key](new Error('close connect'));
|
||||
delete this.callback_message_queue[key];
|
||||
});
|
||||
}
|
||||
Object.keys(this.callback_message_queue).forEach(key => {
|
||||
this.callback_message_queue[key](new Error('close connect'));
|
||||
delete this.callback_message_queue[key];
|
||||
});
|
||||
}
|
||||
|
||||
onRecv(chunk) {
|
||||
this.mp.run(chunk);
|
||||
}
|
||||
onRecv(chunk) {
|
||||
this.mp.run(chunk);
|
||||
}
|
||||
|
||||
onError(e) {
|
||||
debugLog('OnError: ' + e);
|
||||
}
|
||||
onError(e) {
|
||||
debugLog('OnError: ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
|
||||
104
lib/util.js
104
lib/util.js
@@ -1,69 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
const makeRequest = (exports.makeRequest = (method, params, id) => {
|
||||
return JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params,
|
||||
id: id,
|
||||
});
|
||||
return JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params,
|
||||
id: id,
|
||||
});
|
||||
});
|
||||
|
||||
const createRecuesiveParser = (exports.createRecuesiveParser = (max_depth, delimiter) => {
|
||||
const MAX_DEPTH = max_depth;
|
||||
const DELIMITER = delimiter;
|
||||
const recursiveParser = (n, buffer, callback) => {
|
||||
if (buffer.length === 0) {
|
||||
return { code: 0, buffer: buffer };
|
||||
}
|
||||
if (n > MAX_DEPTH) {
|
||||
return { code: 1, buffer: buffer };
|
||||
}
|
||||
const xs = buffer.split(DELIMITER);
|
||||
if (xs.length === 1) {
|
||||
return { code: 0, buffer: buffer };
|
||||
}
|
||||
callback(xs.shift(), n);
|
||||
return recursiveParser(n + 1, xs.join(DELIMITER), callback);
|
||||
};
|
||||
return recursiveParser;
|
||||
const MAX_DEPTH = max_depth;
|
||||
const DELIMITER = delimiter;
|
||||
const recursiveParser = (n, buffer, callback) => {
|
||||
if (buffer.length === 0) {
|
||||
return { code: 0, buffer: buffer };
|
||||
}
|
||||
if (n > MAX_DEPTH) {
|
||||
return { code: 1, buffer: buffer };
|
||||
}
|
||||
const xs = buffer.split(DELIMITER);
|
||||
if (xs.length === 1) {
|
||||
return { code: 0, buffer: buffer };
|
||||
}
|
||||
callback(xs.shift(), n);
|
||||
return recursiveParser(n + 1, xs.join(DELIMITER), callback);
|
||||
};
|
||||
return recursiveParser;
|
||||
});
|
||||
|
||||
const createPromiseResult = (exports.createPromiseResult = (resolve, reject) => {
|
||||
return (err, result) => {
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
};
|
||||
return (err, result) => {
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
};
|
||||
});
|
||||
|
||||
const createPromiseResultBatch = (exports.createPromiseResultBatch = (resolve, reject, argz) => {
|
||||
return (err, result) => {
|
||||
if (result && result[0] && result[0].id) {
|
||||
// this is a batch request response
|
||||
for (let r of result) {
|
||||
r.param = argz[r.id];
|
||||
}
|
||||
}
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
};
|
||||
return (err, result) => {
|
||||
if (result && result[0] && result[0].id) {
|
||||
// this is a batch request response
|
||||
for (let r of result) {
|
||||
r.param = argz[r.id];
|
||||
}
|
||||
}
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
};
|
||||
});
|
||||
|
||||
class MessageParser {
|
||||
constructor(callback) {
|
||||
this.buffer = '';
|
||||
this.callback = callback;
|
||||
this.recursiveParser = createRecuesiveParser(20, '\n');
|
||||
}
|
||||
run(chunk) {
|
||||
this.buffer += chunk;
|
||||
while (true) {
|
||||
const res = this.recursiveParser(0, this.buffer, this.callback);
|
||||
this.buffer = res.buffer;
|
||||
if (res.code === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
constructor(callback) {
|
||||
this.buffer = '';
|
||||
this.callback = callback;
|
||||
this.recursiveParser = createRecuesiveParser(20, '\n');
|
||||
}
|
||||
run(chunk) {
|
||||
this.buffer += chunk;
|
||||
while (true) {
|
||||
const res = this.recursiveParser(0, this.buffer, this.callback);
|
||||
this.buffer = res.buffer;
|
||||
if (res.code === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.MessageParser = MessageParser;
|
||||
|
||||
66
package.json
66
package.json
@@ -1,35 +1,35 @@
|
||||
{
|
||||
"name": "electrum-client",
|
||||
"version": "1.1.4",
|
||||
"description": "Electrum protocol client for React Native & Node.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "~4.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/janoside/electrum-client.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/janoside/electrum-client/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"bitcoin",
|
||||
"electrum",
|
||||
"electrumx"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"contributors": [
|
||||
{ "name": "Yuki Akiyama" },
|
||||
{ "name": "7kharov" },
|
||||
{ "name": "overtorment" },
|
||||
{ "name": "janoside" },
|
||||
],
|
||||
"license": "MIT"
|
||||
"name": "electrum-client",
|
||||
"version": "1.1.4",
|
||||
"description": "Electrum protocol client for React Native & Node.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "~4.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/janoside/electrum-client.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/janoside/electrum-client/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"bitcoin",
|
||||
"electrum",
|
||||
"electrumx"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"contributors": [
|
||||
{ "name": "Yuki Akiyama" },
|
||||
{ "name": "7kharov" },
|
||||
{ "name": "overtorment" },
|
||||
{ "name": "janoside" },
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user