Browse Source

raw electrum api

v0.25
pbca26 7 years ago
parent
commit
6706474163
  1. 1
      package.json
  2. 355
      routes/electrumjs/electrumjs.core.js
  3. 205
      routes/electrumjs/electrumjs.networks.js
  4. 110
      routes/electrumjs/electrumjs.txdecoder.js
  5. 916
      routes/shepherd.js

1
package.json

@ -22,6 +22,7 @@
},
"dependencies": {
"adm-zip": "^0.4.7",
"bitcoinjs-lib": "^3.2.0",
"bluebird": "^3.4.7",
"body-parser": "^1.15.2",
"electron": "1.6.5",

355
routes/electrumjs/electrumjs.core.js

@ -0,0 +1,355 @@
/*
MIT License
Copyright (c) 2017 Yuki Akiyama, SuperNET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const tls = require('tls');
const net = require('net');
const EventEmitter = require('events').EventEmitter;
const makeRequest = function(method, params, id) {
return JSON.stringify({
jsonrpc : '2.0',
method : method,
params : params,
id : id,
});
}
const createRecursiveParser = function(maxDepth, delimiter) {
const MAX_DEPTH = maxDepth;
const DELIMITER = delimiter;
const recursiveParser = function(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 = function(resolve, reject) {
return (err, result) => {
if (err) {
console.log('electrum error:');
console.log(err);
reject(err);
} else {
resolve(result);
}
}
}
class MessageParser {
constructor(callback) {
this.buffer = '';
this.callback = callback;
this.recursiveParser = createRecursiveParser(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;
}
}
}
}
const util = {
makeRequest,
createRecursiveParser,
createPromiseResult,
MessageParser,
};
const getSocket = function(protocol, options) {
switch (protocol) {
case 'tcp':
return new net.Socket();
case 'tls':
// todo
case 'ssl':
return new tls.TLSSocket(options);
}
throw new Error('unknown protocol');
}
const initSocket = function(self, protocol, options) {
const conn = getSocket(protocol, options);
conn.setEncoding('utf8');
conn.setKeepAlive(true, 0);
conn.setNoDelay(true);
conn.on('connect', () => {
self.onConnect();
});
conn.on('close', (e) => {
self.onClose(e);
});
conn.on('data', (chunk) => {
self.onReceive(chunk);
});
conn.on('end', (e) => {
self.onEnd(e);
});
conn.on('error', (e) => {
self.onError(e);
});
return conn;
}
class Client {
constructor(port, host, protocol = 'tcp', options = void 0) {
this.id = 0;
this.port = port;
this.host = host;
this.callbackMessageQueue = {};
this.subscribe = new EventEmitter();
this.conn = initSocket(this, protocol, options);
this.mp = new util.MessageParser((body, n) => {
this.onMessage(body, n);
});
this.status = 0;
}
connect() {
if (this.status) {
return Promise.resolve();
}
this.status = 1;
return new Promise((resolve, reject) => {
const errorHandler = (e) => reject(e)
this.conn.connect(this.port, this.host, () => {
this.conn.removeListener('error', errorHandler);
resolve();
});
this.conn.on('error', errorHandler);
});
}
close() {
if (!this.status) {
return
}
this.conn.end();
this.conn.destroy();
this.status = 0;
}
request(method, params) {
if (!this.status) {
return Promise.reject(new Error('ESOCKET'));
}
return new Promise((resolve, reject) => {
const id = ++this.id;
const content = util.makeRequest(method, params, id);
this.callbackMessageQueue[id] = util.createPromiseResult(resolve, reject);
this.conn.write(`${content}\n`);
});
}
response(msg) {
const callback = this.callbackMessageQueue[msg.id];
if (callback) {
delete this.callbackMessageQueue[msg.id];
if (msg.error) {
callback(msg.error);
} else {
callback(null, msg.result);
}
} else {
// can't get callback
}
}
onMessage(body, n) {
const msg = JSON.parse(body);
if (msg instanceof Array) {
// don't support batch request
} else {
if (msg.id !== void 0) {
this.response(msg);
} else {
this.subscribe.emit(msg.method, msg.params);
}
}
}
onConnect() {
}
onClose() {
Object.keys(this.callbackMessageQueue).forEach((key) => {
this.callbackMessageQueue[key](new Error('close connect'));
delete this.callbackMessageQueue[key];
});
}
onReceive(chunk) {
this.mp.run(chunk);
}
onEnd() {
}
onError(e) {
}
}
class ElectrumJSCore extends Client {
constructor(protocol, port, host, options) {
super(protocol, port, host, options);
}
onClose() {
super.onClose();
const list = [
'server.peers.subscribe',
'blockchain.numblocks.subscribe',
'blockchain.headers.subscribe',
'blockchain.address.subscribe'
];
list.forEach(event => this.subscribe.removeAllListeners(event));
}
// ref: http://docs.electrum.org/en/latest/protocol.html
serverVersion(client_name, protocol_version) {
return this.request('server.version', [client_name, protocol_version]);
}
serverBanner() {
return this.request('server.banner', []);
}
serverDonationAddress() {
return this.request('server.donation_address', []);
}
serverPeersSubscribe() {
return this.request('server.peers.subscribe', []);
}
blockchainAddressGetBalance(address) {
return this.request('blockchain.address.get_balance', [address]);
}
blockchainAddressGetHistory(address) {
return this.request('blockchain.address.get_history', [address]);
}
blockchainAddressGetMempool(address) {
return this.request('blockchain.address.get_mempool', [address]);
}
blockchainAddressGetProof(address) {
return this.request('blockchain.address.get_proof', [address]);
}
blockchainAddressListunspent(address) {
return this.request('blockchain.address.listunspent', [address]);
}
blockchainBlockGetHeader(height) {
return this.request('blockchain.block.get_header', [height]);
}
blockchainBlockGetChunk(index) {
return this.request('blockchain.block.get_chunk', [index]);
}
blockchainEstimatefee(number) {
return this.request('blockchain.estimatefee', [number]);
}
blockchainHeadersSubscribe() {
return this.request('blockchain.headers.subscribe', []);
}
blockchainNumblocksSubscribe() {
return this.request('blockchain.numblocks.subscribe', []);
}
blockchainRelayfee() {
return this.request('blockchain.relayfee', []);
}
blockchainTransactionBroadcast(rawtx) {
return this.request('blockchain.transaction.broadcast', [rawtx]);
}
blockchainTransactionGet(tx_hash, height) {
return this.request('blockchain.transaction.get', [tx_hash, height]);
}
blockchainTransactionGetMerkle(tx_hash, height) {
return this.request('blockchain.transaction.get_merkle', [tx_hash, height]);
}
blockchainUtxoGetAddress(tx_hash, index) {
return this.request('blockchain.utxo.get_address', [tx_hash, index]);
}
}
module.exports = ElectrumJSCore;

205
routes/electrumjs/electrumjs.networks.js

@ -0,0 +1,205 @@
'use strict'
var bitcoin = require('bitcoinjs-lib');
var networks = exports;
Object.keys(bitcoin.networks).forEach(function(key){
networks[key] = bitcoin.networks[key]
});
networks.litecoin = {
messagePrefix: '\x19Litecoin Signed Message:\n',
bip32: {
public: 0x019da462,
private: 0x019d9cfe
},
pubKeyHash: 0x30,
scriptHash: 0x32,
wif: 0xb0,
dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
}
networks.dogecoin = {
messagePrefix: '\x19Dogecoin Signed Message:\n',
bip32: {
public: 0x02facafd,
private: 0x02fac398,
},
pubKeyHash: 0x1e,
scriptHash: 0x16,
wif: 0x9e,
dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
};
// https://github.com/monacoinproject/monacoin/blob/master-0.10/src/chainparams.cpp#L161
networks.monacoin = {
messagePrefix: '\x19Monacoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x32,
scriptHash: 0x05,
wif: 0xB2,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
};
// https://github.com/gamecredits-project/GameCredits/blob/master/src/chainparams.cpp#L136
networks.game = {
messagePrefix: '\x19GameCredits Signed Message:\n',
bip32: {
public: 0x043587cf,
private: 0x04358394,
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef,
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
};
// https://github.com/dashpay/dash/blob/master/src/chainparams.cpp#L171
networks.dash = {
messagePrefix: '\x19DarkCoin Signed Message:\n',
bip32: {
public: 0x02fe52f8,
private: 0x02fe52cc,
},
pubKeyHash: 0x4c,
scriptHash: 0x10,
wif: 0xcc,
dustThreshold: 5460, // https://github.com/dashpay/dash/blob/v0.12.0.x/src/primitives/transaction.h#L144-L155
};
// https://github.com/zcoinofficial/zcoin/blob/c93eccb39b07a6132cb3d787ac18be406b24c3fa/src/base58.h#L275
networks.zcoin = {
messagePrefix: '\x19ZCoin Signed Message:\n',
bip32: {
public: 0x0488b21e, // todo
private: 0x0488ade4, // todo
},
pubKeyHash: 0x52,
scriptHash: 0x07,
wif: 0x52 + 128,
dustThreshold: 1000, // https://github.com/zcoinofficial/zcoin/blob/f755f95a036eedfef7c96bcfb6769cb79278939f/src/main.h#L59
};
// https://raw.githubusercontent.com/jl777/komodo/beta/src/chainparams.cpp
networks.komodo = {
messagePrefix: '\x19Komodo Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x3c,
scriptHash: 0x55,
wif: 0xbc,
dustThreshold: 1000,
};
networks.viacoin = {
messagePrefix: '\x19Viacoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x47,
scriptHash: 0x21,
wif: 0xc7,
dustThreshold: 1000,
};
networks.vertcoin = {
messagePrefix: '\x19Vertcoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x47,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.namecoin = {
messagePrefix: '\x19Namecoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x34,
scriptHash: 0xd,
wif: 0xb4,
dustThreshold: 1000,
};
networks.faircoin = {
messagePrefix: '\x19Faircoin Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x5f,
scriptHash: 0x24,
wif: 0xdf,
dustThreshold: 1000,
};
networks.digibyte = {
messagePrefix: '\x19Digibyte Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x1e,
scriptHash: 0x5,
wif: 0x80,
dustThreshold: 1000,
};
networks.crown = {
messagePrefix: '\x19Crown Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x0,
scriptHash: 0x1c,
wif: 0x80,
dustThreshold: 1000,
};
networks.argentum = {
messagePrefix: '\x19Argentum Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x17,
scriptHash: 0x5,
wif: 0x97,
dustThreshold: 1000,
};
networks.chips = {
messagePrefix: '\x19Chips Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x3c,
scriptHash: 0x55,
wif: 0xbc,
dustThreshold: 1000,
};
/*networks.zcash = {
messagePrefix: '\x19Zcash Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};*/

110
routes/electrumjs/electrumjs.txdecoder.js

@ -0,0 +1,110 @@
/*
MIT License
Copyright (c) 2017 Yuki Akiyama, SuperNET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var bitcoin = require('bitcoinjs-lib');
var decodeFormat = function(tx) {
var result = {
txid: tx.getId(),
version: tx.version,
locktime: tx.locktime,
};
return result;
}
var decodeInput = function(tx) {
var result = [];
tx.ins.forEach(function(input, n) {
var vin = {
txid: input.hash.reverse().toString('hex'),
n: input.index,
script: bitcoin.script.toASM(input.script),
sequence: input.sequence,
};
result.push(vin);
});
return result;
}
var decodeOutput = function(tx, network) {
var format = function(out, n, network) {
var vout = {
satoshi: out.value,
value: (1e-8 * out.value).toFixed(8),
n: n,
scriptPubKey: {
asm: bitcoin.script.toASM(out.script),
hex: out.script.toString('hex'),
type: bitcoin.script.classifyOutput(out.script),
addresses: [],
},
};
switch(vout.scriptPubKey.type) {
case 'pubkeyhash':
case 'scripthash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
}
return vout;
}
var result = [];
tx.outs.forEach(function(out, n) {
result.push(format(out, n, network));
});
return result;
}
var TxDecoder = module.exports = function(rawtx, network) {
const _tx = bitcoin.Transaction.fromHex(rawtx);
return {
tx: _tx,
network: network,
format: decodeFormat(_tx),
inputs: decodeInput(_tx),
outputs: decodeOutput(_tx, network),
};
}
TxDecoder.prototype.decode = function() {
var result = {};
var self = this;
Object.keys(self.format).forEach(function(key) {
result[key] = self.format[key];
});
result.outputs = self.outputs;
return result;
}

916
routes/shepherd.js

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save