Yemel Jardi
10 years ago
2 changed files with 251 additions and 0 deletions
@ -0,0 +1,223 @@ |
|||||
|
// RpcClient.js
|
||||
|
// MIT/X11-like license. See LICENSE.txt.
|
||||
|
// Copyright 2013 BitPay, Inc.
|
||||
|
//
|
||||
|
var http = require('http'); |
||||
|
var https = require('https'); |
||||
|
var log = require('../util/log'); |
||||
|
|
||||
|
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; |
||||
|
this.rejectUnauthorized = opts.rejectUnauthorized || false; |
||||
|
} |
||||
|
|
||||
|
RpcClient.prototype.batch = function(batchCallback, resultCallback) { |
||||
|
this.batchedCalls = []; |
||||
|
batchCallback(); |
||||
|
rpc.call(this, this.batchedCalls, resultCallback); |
||||
|
this.batchedCalls = null; |
||||
|
} |
||||
|
|
||||
|
var callspec = { |
||||
|
addMultiSigAddress: '', |
||||
|
addNode: '', |
||||
|
backupWallet: '', |
||||
|
createMultiSig: '', |
||||
|
createRawTransaction: '', |
||||
|
decodeRawTransaction: '', |
||||
|
dumpPrivKey: '', |
||||
|
encryptWallet: '', |
||||
|
getAccount: '', |
||||
|
getAccountAddress: 'str', |
||||
|
getAddedNodeInfo: '', |
||||
|
getAddressesByAccount: '', |
||||
|
getBalance: 'str int', |
||||
|
getBestBlockHash: '', |
||||
|
getBlock: '', |
||||
|
getBlockCount: '', |
||||
|
getBlockHash: 'int', |
||||
|
getBlockNumber: '', |
||||
|
getBlockTemplate: '', |
||||
|
getConnectionCount: '', |
||||
|
getDifficulty: '', |
||||
|
getGenerate: '', |
||||
|
getHashesPerSec: '', |
||||
|
getInfo: '', |
||||
|
getMemoryPool: '', |
||||
|
getMiningInfo: '', |
||||
|
getNewAddress: '', |
||||
|
getPeerInfo: '', |
||||
|
getRawMemPool: '', |
||||
|
getRawTransaction: 'str int', |
||||
|
getReceivedByAccount: 'str int', |
||||
|
getReceivedByAddress: 'str int', |
||||
|
getTransaction: '', |
||||
|
getTxOut: 'str int bool', |
||||
|
getTxOutSetInfo: '', |
||||
|
getWork: '', |
||||
|
help: '', |
||||
|
importAddress: 'str str bool', |
||||
|
importPrivKey: 'str str bool', |
||||
|
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', |
||||
|
sendFrom: 'str str float int str str', |
||||
|
sendMany: 'str str int str', //not sure this is will work
|
||||
|
sendRawTransaction: '', |
||||
|
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) var 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) |
||||
|
}); |
||||
|
} else { |
||||
|
rpc.call(this, { |
||||
|
method: methodName, |
||||
|
params: slice(arguments, 0, arguments.length - 1) |
||||
|
}, 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'); |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
for (var k in apiCalls) { |
||||
|
if (apiCalls.hasOwnProperty(k)) { |
||||
|
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.string; |
||||
|
} |
||||
|
} |
||||
|
var methodName = k.toLowerCase(); |
||||
|
constructor.prototype[k] = createRPCMethod(methodName, spec); |
||||
|
constructor.prototype[methodName] = constructor.prototype[k]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function rpc(request, callback) { |
||||
|
var self = this; |
||||
|
var request; |
||||
|
request = JSON.stringify(request); |
||||
|
var auth = 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 err = null; |
||||
|
var req = this.protocol.request(options, function(res) { |
||||
|
|
||||
|
var buf = ''; |
||||
|
res.on('data', function(data) { |
||||
|
buf += data; |
||||
|
}); |
||||
|
res.on('end', function() { |
||||
|
if (res.statusCode == 401) { |
||||
|
callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized')); |
||||
|
return; |
||||
|
} |
||||
|
if (res.statusCode == 403) { |
||||
|
callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden')); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (err) { |
||||
|
callback(err); |
||||
|
return; |
||||
|
} |
||||
|
try { |
||||
|
var parsedBuf = JSON.parse(buf); |
||||
|
} catch (e) { |
||||
|
log.err(e.stack); |
||||
|
log.err(buf); |
||||
|
log.err('HTTP Status code:' + res.statusCode); |
||||
|
callback(e); |
||||
|
return; |
||||
|
} |
||||
|
callback(parsedBuf.error, parsedBuf); |
||||
|
}); |
||||
|
}); |
||||
|
req.on('error', function(e) { |
||||
|
var err = new Error('Could not connect to bitcoin via RPC at host: ' + self.host + ' port: ' + self.port + ' Error: ' + e.message); |
||||
|
log.err(err); |
||||
|
callback(err); |
||||
|
}); |
||||
|
|
||||
|
req.setHeader('Content-Length', request.length); |
||||
|
req.setHeader('Content-Type', 'application/json'); |
||||
|
req.setHeader('Authorization', 'Basic ' + auth); |
||||
|
req.write(request); |
||||
|
req.end(); |
||||
|
}; |
||||
|
|
||||
|
generateRPCMethods(RpcClient, callspec, rpc); |
||||
|
|
||||
|
module.exports = RpcClient; |
@ -0,0 +1,28 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
var chai = chai || require('chai'); |
||||
|
var bitcore = bitcore || require('../bitcore'); |
||||
|
|
||||
|
var should = chai.should(); |
||||
|
|
||||
|
var RpcClientModule = bitcore.RpcClient; |
||||
|
var RpcClient; |
||||
|
RpcClient = RpcClientModule; |
||||
|
|
||||
|
describe('RpcClient', function() { |
||||
|
it('should initialze the main object', function() { |
||||
|
should.exist(RpcClientModule); |
||||
|
}); |
||||
|
it('should be able to create class', function() { |
||||
|
should.exist(RpcClient); |
||||
|
}); |
||||
|
it('should be able to create instance', function() { |
||||
|
var s = new RpcClient(); |
||||
|
should.exist(s); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
Loading…
Reference in new issue