bitcoind-rpc-veggie
Version:
Bitcoin Client Library to connect to Veggie Core via RPC
305 lines (263 loc) • 7.49 kB
JavaScript
;
var http = require('http');
var https = require('https');
function RpcClient(opts) {
opts = opts || {};
this.host = opts.host || '127.0.0.1';
this.port = opts.port || 8332;
this.user = opts.user || 'user';
this.pass = opts.pass || 'pass';
this.protocol = opts.protocol === 'http' ? http : https;
this.batchedCalls = null;
this.disableAgent = opts.disableAgent || false;
var isRejectUnauthorized = typeof opts.rejectUnauthorized !== 'undefined';
this.rejectUnauthorized = isRejectUnauthorized ? opts.rejectUnauthorized : true;
if(RpcClient.config.log) {
this.log = RpcClient.config.log;
} else {
this.log = RpcClient.loggers[RpcClient.config.logger || 'normal'];
}
}
var cl = console.log.bind(console);
var noop = function() {};
RpcClient.loggers = {
none: {info: noop, warn: noop, err: noop, debug: noop},
normal: {info: cl, warn: cl, err: cl, debug: noop},
debug: {info: cl, warn: cl, err: cl, debug: cl}
};
RpcClient.config = {
logger: 'normal' // none, normal, debug
};
function rpc(request, callback) {
var self = this;
request = JSON.stringify(request);
var auth = new Buffer(self.user + ':' + self.pass).toString('base64');
var options = {
host: self.host,
path: '/',
method: 'POST',
port: self.port,
rejectUnauthorized: self.rejectUnauthorized,
agent: self.disableAgent ? false : undefined
};
if (self.httpOptions) {
for (var k in self.httpOptions) {
options[k] = self.httpOptions[k];
}
}
var called = false;
var errorMessage = 'Bitcoin JSON-RPC: ';
var req = this.protocol.request(options, function(res) {
var buf = '';
res.on('data', function(data) {
buf += data;
});
res.on('end', function() {
if (called) {
return;
}
called = true;
if (res.statusCode === 401) {
callback(new Error(errorMessage + 'Connection Rejected: 401 Unnauthorized'));
return;
}
if (res.statusCode === 403) {
callback(new Error(errorMessage + 'Connection Rejected: 403 Forbidden'));
return;
}
if (res.statusCode === 500 && buf.toString('utf8') === 'Work queue depth exceeded') {
var exceededError = new Error('Bitcoin JSON-RPC: ' + buf.toString('utf8'));
exceededError.code = 429; // Too many requests
callback(exceededError);
return;
}
var parsedBuf;
try {
parsedBuf = JSON.parse(buf);
} catch(e) {
self.log.err(e.stack);
self.log.err(buf);
self.log.err('HTTP Status code:' + res.statusCode);
var err = new Error(errorMessage + 'Error Parsing JSON: ' + e.message);
callback(err);
return;
}
callback(parsedBuf.error, parsedBuf);
});
});
req.on('error', function(e) {
var err = new Error(errorMessage + 'Request Error: ' + e.message);
if (!called) {
called = true;
callback(err);
}
});
req.setHeader('Content-Length', request.length);
req.setHeader('Content-Type', 'application/json');
req.setHeader('Authorization', 'Basic ' + auth);
req.write(request);
req.end();
}
RpcClient.prototype.batch = function(batchCallback, resultCallback) {
this.batchedCalls = [];
batchCallback();
rpc.call(this, this.batchedCalls, resultCallback);
this.batchedCalls = null;
};
RpcClient.callspec = {
abandonTransaction: 'str',
addMultiSigAddress: '',
addNode: '',
backupWallet: '',
createMultiSig: '',
createRawTransaction: '',
decodeRawTransaction: '',
dumpPrivKey: '',
encryptWallet: '',
estimateFee: 'int',
estimatePriority: 'int',
generate: 'int',
getAccount: '',
getAccountAddress: 'str',
getAddedNodeInfo: '',
getAddressMempool: 'obj',
getAddressUtxos: 'obj',
getAddressBalance: 'obj',
getAddressDeltas: 'obj',
getAddressTxids: 'obj',
getAddressesByAccount: '',
getBalance: 'str int',
getBestBlockHash: '',
getBlockDeltas: 'str',
getBlock: 'str bool',
getBlockchainInfo: '',
getBlockCount: '',
getBlockHashes: 'int int obj',
getBlockHash: 'int',
getBlockHeader: 'str',
getBlockNumber: '',
getBlockTemplate: '',
getConnectionCount: '',
getChainTips: '',
getDifficulty: '',
getGenerate: '',
getHashesPerSec: '',
getInfo: '',
getMemoryPool: '',
getMemPoolInfo: '',
getMiningInfo: '',
getNewAddress: '',
getPeerInfo: '',
getRawMemPool: '',
getRawTransaction: 'str int',
getReceivedByAccount: 'str int',
getReceivedByAddress: 'str int',
getSpentInfo: 'obj',
getTransaction: '',
getTxOut: 'str int bool',
getTxOutSetInfo: '',
getWork: '',
gobject: 'str str',
help: '',
importAddress: 'str str bool',
importPrivKey: 'str str bool',
invalidateBlock: 'str',
keyPoolRefill: '',
listAccounts: 'int',
listAddressGroupings: '',
listReceivedByAccount: 'int bool',
listReceivedByAddress: 'int bool',
listSinceBlock: 'str int',
listTransactions: 'str int int',
listUnspent: 'int int',
listLockUnspent: 'bool',
lockUnspent: '',
move: 'str str float int str',
prioritiseTransaction: 'str float int',
sendFrom: 'str str float int str str',
sendMany: 'str obj int str', //not sure this is will work
sendRawTransaction: 'str',
sendToAddress: 'str float str str',
setAccount: '',
setGenerate: 'bool int',
setTxFee: 'float',
signMessage: '',
signRawTransaction: '',
stop: '',
submitBlock: '',
validateAddress: '',
verifyMessage: '',
walletLock: '',
walletPassPhrase: 'string int',
walletPassphraseChange: '',
};
var slice = function(arr, start, end) {
return Array.prototype.slice.call(arr, start, end);
};
function generateRPCMethods(constructor, apiCalls, rpc) {
function createRPCMethod(methodName, argMap) {
return function() {
var limit = arguments.length - 1;
if (this.batchedCalls) {
limit = arguments.length;
}
for (var i = 0; i < limit; i++) {
if(argMap[i]) {
arguments[i] = argMap[i](arguments[i]);
}
}
if (this.batchedCalls) {
this.batchedCalls.push({
jsonrpc: '2.0',
method: methodName,
params: slice(arguments),
id: getRandomId()
});
} else {
rpc.call(this, {
method: methodName,
params: slice(arguments, 0, arguments.length - 1),
id: getRandomId()
}, arguments[arguments.length - 1]);
}
};
};
var types = {
str: function(arg) {
return arg.toString();
},
int: function(arg) {
return parseFloat(arg);
},
float: function(arg) {
return parseFloat(arg);
},
bool: function(arg) {
return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true');
},
obj: function(arg) {
if(typeof arg === 'string') {
return JSON.parse(arg);
}
return arg;
}
};
for(var k in apiCalls) {
var spec = apiCalls[k].split(' ');
for (var i = 0; i < spec.length; i++) {
if(types[spec[i]]) {
spec[i] = types[spec[i]];
} else {
spec[i] = types.str;
}
}
var methodName = k.toLowerCase();
constructor.prototype[k] = createRPCMethod(methodName, spec);
constructor.prototype[methodName] = constructor.prototype[k];
}
}
function getRandomId() {
return parseInt(Math.random() * 100000);
}
generateRPCMethods(RpcClient, RpcClient.callspec, rpc);
module.exports = RpcClient;