Merge pull request #1370 from mempool/simon/merge-rpc-api
Merge node-bitcoin into the project
This commit is contained in:
		
						commit
						a292745ea7
					
				
							
								
								
									
										14
									
								
								backend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								backend/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -9,7 +9,6 @@ | |||||||
|       "version": "2.4.0-dev", |       "version": "2.4.0-dev", | ||||||
|       "license": "GNU Affero General Public License v3.0", |       "license": "GNU Affero General Public License v3.0", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@mempool/bitcoin": "^3.0.3", |  | ||||||
|         "@mempool/electrum-client": "^1.1.7", |         "@mempool/electrum-client": "^1.1.7", | ||||||
|         "@types/ws": "8.2.2", |         "@types/ws": "8.2.2", | ||||||
|         "axios": "0.24.0", |         "axios": "0.24.0", | ||||||
| @ -56,14 +55,6 @@ | |||||||
|         "js-tokens": "^4.0.0" |         "js-tokens": "^4.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@mempool/bitcoin": { |  | ||||||
|       "version": "3.0.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@mempool/bitcoin/-/bitcoin-3.0.3.tgz", |  | ||||||
|       "integrity": "sha512-10UdbwchnevlebDTN+Xhv75AEhDmTMy9UgWHlqx5MG2mheFG6+eqmtHsdxeYnv3IAtTtlRfA6fY0RbV/x4TNFQ==", |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">= 0.10.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@mempool/electrum-client": { |     "node_modules/@mempool/electrum-client": { | ||||||
|       "version": "1.1.8", |       "version": "1.1.8", | ||||||
|       "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.8.tgz", |       "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.8.tgz", | ||||||
| @ -1515,11 +1506,6 @@ | |||||||
|         "js-tokens": "^4.0.0" |         "js-tokens": "^4.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "@mempool/bitcoin": { |  | ||||||
|       "version": "3.0.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@mempool/bitcoin/-/bitcoin-3.0.3.tgz", |  | ||||||
|       "integrity": "sha512-10UdbwchnevlebDTN+Xhv75AEhDmTMy9UgWHlqx5MG2mheFG6+eqmtHsdxeYnv3IAtTtlRfA6fY0RbV/x4TNFQ==" |  | ||||||
|     }, |  | ||||||
|     "@mempool/electrum-client": { |     "@mempool/electrum-client": { | ||||||
|       "version": "1.1.8", |       "version": "1.1.8", | ||||||
|       "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.8.tgz", |       "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.8.tgz", | ||||||
|  | |||||||
| @ -28,7 +28,6 @@ | |||||||
|     "test": "echo \"Error: no test specified\" && exit 1" |     "test": "echo \"Error: no test specified\" && exit 1" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@mempool/bitcoin": "^3.0.3", |  | ||||||
|     "@mempool/electrum-client": "^1.1.7", |     "@mempool/electrum-client": "^1.1.7", | ||||||
|     "@types/ws": "8.2.2", |     "@types/ws": "8.2.2", | ||||||
|     "axios": "0.24.0", |     "axios": "0.24.0", | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import config from '../../config'; | import config from '../../config'; | ||||||
| import * as bitcoin from '@mempool/bitcoin'; | const bitcoin = require('../../rpc-api/index'); | ||||||
| import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | ||||||
| 
 | 
 | ||||||
| const nodeRpcCredentials: BitcoinRpcCredentials = { | const nodeRpcCredentials: BitcoinRpcCredentials = { | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import config from '../../config'; | import config from '../../config'; | ||||||
| import * as bitcoin from '@mempool/bitcoin'; | const bitcoin = require('../../rpc-api/index'); | ||||||
| import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | ||||||
| 
 | 
 | ||||||
| const nodeRpcCredentials: BitcoinRpcCredentials = { | const nodeRpcCredentials: BitcoinRpcCredentials = { | ||||||
|  | |||||||
							
								
								
									
										92
									
								
								backend/src/rpc-api/commands.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								backend/src/rpc-api/commands.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | module.exports = { | ||||||
|  |   addMultiSigAddress: 'addmultisigaddress', | ||||||
|  |   addNode: 'addnode', // bitcoind v0.8.0+
 | ||||||
|  |   backupWallet: 'backupwallet', | ||||||
|  |   createMultiSig: 'createmultisig', | ||||||
|  |   createRawTransaction: 'createrawtransaction', // bitcoind v0.7.0+
 | ||||||
|  |   decodeRawTransaction: 'decoderawtransaction', // bitcoind v0.7.0+
 | ||||||
|  |   decodeScript: 'decodescript', | ||||||
|  |   dumpPrivKey: 'dumpprivkey', | ||||||
|  |   dumpWallet: 'dumpwallet', // bitcoind v0.9.0+
 | ||||||
|  |   encryptWallet: 'encryptwallet', | ||||||
|  |   estimateFee: 'estimatefee', // bitcoind v0.10.0x
 | ||||||
|  |   estimatePriority: 'estimatepriority', // bitcoind v0.10.0+
 | ||||||
|  |   generate: 'generate', // bitcoind v0.11.0+
 | ||||||
|  |   getAccount: 'getaccount', | ||||||
|  |   getAccountAddress: 'getaccountaddress', | ||||||
|  |   getAddedNodeInfo: 'getaddednodeinfo', // bitcoind v0.8.0+
 | ||||||
|  |   getAddressesByAccount: 'getaddressesbyaccount', | ||||||
|  |   getBalance: 'getbalance', | ||||||
|  |   getBestBlockHash: 'getbestblockhash', // bitcoind v0.9.0+
 | ||||||
|  |   getBlock: 'getblock', | ||||||
|  |   getBlockStats: 'getblockstats', | ||||||
|  |   getBlockFilter: 'getblockfilter', | ||||||
|  |   getBlockchainInfo: 'getblockchaininfo', // bitcoind v0.9.2+
 | ||||||
|  |   getBlockCount: 'getblockcount', | ||||||
|  |   getBlockHash: 'getblockhash', | ||||||
|  |   getBlockHeader: 'getblockheader', | ||||||
|  |   getBlockTemplate: 'getblocktemplate', // bitcoind v0.7.0+
 | ||||||
|  |   getChainTips: 'getchaintips', // bitcoind v0.10.0+
 | ||||||
|  |   getChainTxStats: 'getchaintxstats', | ||||||
|  |   getConnectionCount: 'getconnectioncount', | ||||||
|  |   getDifficulty: 'getdifficulty', | ||||||
|  |   getGenerate: 'getgenerate', | ||||||
|  |   getInfo: 'getinfo', | ||||||
|  |   getMempoolAncestors: 'getmempoolancestors', | ||||||
|  |   getMempoolDescendants: 'getmempooldescendants', | ||||||
|  |   getMempoolEntry: 'getmempoolentry', | ||||||
|  |   getMempoolInfo: 'getmempoolinfo', // bitcoind v0.10+
 | ||||||
|  |   getMiningInfo: 'getmininginfo', | ||||||
|  |   getNetTotals: 'getnettotals', | ||||||
|  |   getNetworkInfo: 'getnetworkinfo', // bitcoind v0.9.2+
 | ||||||
|  |   getNetworkHashPs: 'getnetworkhashps', // bitcoind v0.9.0+
 | ||||||
|  |   getNewAddress: 'getnewaddress', | ||||||
|  |   getPeerInfo: 'getpeerinfo', // bitcoind v0.7.0+
 | ||||||
|  |   getRawChangeAddress: 'getrawchangeaddress', // bitcoin v0.9+
 | ||||||
|  |   getRawMemPool: 'getrawmempool', // bitcoind v0.7.0+
 | ||||||
|  |   getRawTransaction: 'getrawtransaction', // bitcoind v0.7.0+
 | ||||||
|  |   getReceivedByAccount: 'getreceivedbyaccount', | ||||||
|  |   getReceivedByAddress: 'getreceivedbyaddress', | ||||||
|  |   getTransaction: 'gettransaction', | ||||||
|  |   getTxOut: 'gettxout', // bitcoind v0.7.0+
 | ||||||
|  |   getTxOutProof: 'gettxoutproof', // bitcoind v0.11.0+
 | ||||||
|  |   getTxOutSetInfo: 'gettxoutsetinfo', // bitcoind v0.7.0+
 | ||||||
|  |   getUnconfirmedBalance: 'getunconfirmedbalance', // bitcoind v0.9.0+
 | ||||||
|  |   getWalletInfo: 'getwalletinfo', // bitcoind v0.9.2+
 | ||||||
|  |   help: 'help', | ||||||
|  |   importAddress: 'importaddress', // bitcoind v0.10.0+
 | ||||||
|  |   importPrivKey: 'importprivkey', | ||||||
|  |   importWallet: 'importwallet', // bitcoind v0.9.0+
 | ||||||
|  |   keypoolRefill: 'keypoolrefill', | ||||||
|  |   keyPoolRefill: 'keypoolrefill', | ||||||
|  |   listAccounts: 'listaccounts', | ||||||
|  |   listAddressGroupings: 'listaddressgroupings', // bitcoind v0.7.0+
 | ||||||
|  |   listLockUnspent: 'listlockunspent', // bitcoind v0.8.0+
 | ||||||
|  |   listReceivedByAccount: 'listreceivedbyaccount', | ||||||
|  |   listReceivedByAddress: 'listreceivedbyaddress', | ||||||
|  |   listSinceBlock: 'listsinceblock', | ||||||
|  |   listTransactions: 'listtransactions', | ||||||
|  |   listUnspent: 'listunspent', // bitcoind v0.7.0+
 | ||||||
|  |   lockUnspent: 'lockunspent', // bitcoind v0.8.0+
 | ||||||
|  |   move: 'move', | ||||||
|  |   ping: 'ping', // bitcoind v0.9.0+
 | ||||||
|  |   prioritiseTransaction: 'prioritisetransaction', // bitcoind v0.10.0+
 | ||||||
|  |   sendFrom: 'sendfrom', | ||||||
|  |   sendMany: 'sendmany', | ||||||
|  |   sendRawTransaction: 'sendrawtransaction', // bitcoind v0.7.0+
 | ||||||
|  |   sendToAddress: 'sendtoaddress', | ||||||
|  |   setAccount: 'setaccount', | ||||||
|  |   setGenerate: 'setgenerate', | ||||||
|  |   setTxFee: 'settxfee', | ||||||
|  |   signMessage: 'signmessage', | ||||||
|  |   signRawTransaction: 'signrawtransaction', // bitcoind v0.7.0+
 | ||||||
|  |   stop: 'stop', | ||||||
|  |   submitBlock: 'submitblock', // bitcoind v0.7.0+
 | ||||||
|  |   validateAddress: 'validateaddress', | ||||||
|  |   verifyChain: 'verifychain', // bitcoind v0.9.0+
 | ||||||
|  |   verifyMessage: 'verifymessage', | ||||||
|  |   verifyTxOutProof: 'verifytxoutproof', // bitcoind v0.11.0+
 | ||||||
|  |   walletLock: 'walletlock', | ||||||
|  |   walletPassphrase: 'walletpassphrase', | ||||||
|  |   walletPassphraseChange: 'walletpassphrasechange' | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								backend/src/rpc-api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								backend/src/rpc-api/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | var commands = require('./commands') | ||||||
|  | var rpc = require('./jsonrpc') | ||||||
|  | 
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | // JsonRPC
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | function Client (opts) { | ||||||
|  |   // @ts-ignore
 | ||||||
|  |   this.rpc = new rpc.JsonRPC(opts) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | // cmd
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | Client.prototype.cmd = function () { | ||||||
|  |   var args = [].slice.call(arguments) | ||||||
|  |   var cmd = args.shift() | ||||||
|  | 
 | ||||||
|  |   callRpc(cmd, args, this.rpc) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | // callRpc
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | function callRpc (cmd, args, rpc) { | ||||||
|  |   var fn = args[args.length - 1] | ||||||
|  | 
 | ||||||
|  |   // If the last argument is a callback, pop it from the args list
 | ||||||
|  |   if (typeof fn === 'function') { | ||||||
|  |     args.pop() | ||||||
|  |   } else { | ||||||
|  |     fn = function () {} | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return rpc.call(cmd, args, function () { | ||||||
|  |     var args = [].slice.call(arguments) | ||||||
|  |       // @ts-ignore
 | ||||||
|  |     args.unshift(null) | ||||||
|  |       // @ts-ignore
 | ||||||
|  |     fn.apply(this, args) | ||||||
|  |   }, function (err) { | ||||||
|  |     fn(err) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | // Initialize wrappers
 | ||||||
|  | // ===----------------------------------------------------------------------===//
 | ||||||
|  | (function () { | ||||||
|  |   for (var protoFn in commands) { | ||||||
|  |     (function (protoFn) { | ||||||
|  |       Client.prototype[protoFn] = function () { | ||||||
|  |         var args = [].slice.call(arguments) | ||||||
|  |         return callRpc(commands[protoFn], args, this.rpc) | ||||||
|  |       } | ||||||
|  |     })(protoFn) | ||||||
|  |   } | ||||||
|  | })() | ||||||
|  | 
 | ||||||
|  | // Export!
 | ||||||
|  | module.exports.Client = Client; | ||||||
							
								
								
									
										162
									
								
								backend/src/rpc-api/jsonrpc.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								backend/src/rpc-api/jsonrpc.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | |||||||
|  | var http = require('http') | ||||||
|  | var https = require('https') | ||||||
|  | 
 | ||||||
|  | var JsonRPC = function (opts) { | ||||||
|  |   // @ts-ignore
 | ||||||
|  |   this.opts = opts || {} | ||||||
|  |   // @ts-ignore
 | ||||||
|  |   this.http = this.opts.ssl ? https : http | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | JsonRPC.prototype.call = function (method, params) { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     var time = Date.now() | ||||||
|  |     var requestJSON | ||||||
|  | 
 | ||||||
|  |     if (Array.isArray(method)) { | ||||||
|  |       // multiple rpc batch call
 | ||||||
|  |       requestJSON = [] | ||||||
|  |       method.forEach(function (batchCall, i) { | ||||||
|  |         requestJSON.push({ | ||||||
|  |           id: time + '-' + i, | ||||||
|  |           method: batchCall.method, | ||||||
|  |           params: batchCall.params | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     } else { | ||||||
|  |       // single rpc call
 | ||||||
|  |       requestJSON = { | ||||||
|  |         id: time, | ||||||
|  |         method: method, | ||||||
|  |         params: params | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // First we encode the request into JSON
 | ||||||
|  |     requestJSON = JSON.stringify(requestJSON) | ||||||
|  | 
 | ||||||
|  |     // prepare request options
 | ||||||
|  |     var requestOptions = { | ||||||
|  |       host: this.opts.host || 'localhost', | ||||||
|  |       port: this.opts.port || 8332, | ||||||
|  |       method: 'POST', | ||||||
|  |       path: '/', | ||||||
|  |       headers: { | ||||||
|  |         'Host': this.opts.host || 'localhost', | ||||||
|  |         'Content-Length': requestJSON.length | ||||||
|  |       }, | ||||||
|  |       agent: false, | ||||||
|  |       rejectUnauthorized: this.opts.ssl && this.opts.sslStrict !== false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (this.opts.ssl && this.opts.sslCa) { | ||||||
|  |     // @ts-ignore 
 | ||||||
|  |       requestOptions.ca = this.opts.sslCa | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // use HTTP auth if user and password set
 | ||||||
|  |     if (this.opts.user && this.opts.pass) { | ||||||
|  |       // @ts-ignore
 | ||||||
|  |       requestOptions.auth = this.opts.user + ':' + this.opts.pass | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Now we'll make a request to the server
 | ||||||
|  |     var cbCalled = false | ||||||
|  |     var request = this.http.request(requestOptions) | ||||||
|  | 
 | ||||||
|  |     // start request timeout timer
 | ||||||
|  |     var reqTimeout = setTimeout(function () { | ||||||
|  |       if (cbCalled) return | ||||||
|  |       cbCalled = true | ||||||
|  |       request.abort() | ||||||
|  |       var err = new Error('ETIMEDOUT') | ||||||
|  |       // @ts-ignore
 | ||||||
|  |       err.code = 'ETIMEDOUT' | ||||||
|  |       reject(err) | ||||||
|  |     }, this.opts.timeout || 30000) | ||||||
|  | 
 | ||||||
|  |     // set additional timeout on socket in case of remote freeze after sending headers
 | ||||||
|  |     request.setTimeout(this.opts.timeout || 30000, function () { | ||||||
|  |       if (cbCalled) return | ||||||
|  |       cbCalled = true | ||||||
|  |       request.abort() | ||||||
|  |       var err = new Error('ESOCKETTIMEDOUT') | ||||||
|  |       // @ts-ignore
 | ||||||
|  |       err.code = 'ESOCKETTIMEDOUT' | ||||||
|  |       reject(err) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     request.on('error', function (err) { | ||||||
|  |       if (cbCalled) return | ||||||
|  |       cbCalled = true | ||||||
|  |       clearTimeout(reqTimeout) | ||||||
|  |       reject(err) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     request.on('response', function (response) { | ||||||
|  |       clearTimeout(reqTimeout) | ||||||
|  | 
 | ||||||
|  |       // We need to buffer the response chunks in a nonblocking way.
 | ||||||
|  |       var buffer = '' | ||||||
|  |       response.on('data', function (chunk) { | ||||||
|  |         buffer = buffer + chunk | ||||||
|  |       }) | ||||||
|  |       // When all the responses are finished, we decode the JSON and
 | ||||||
|  |       // depending on whether it's got a result or an error, we call
 | ||||||
|  |       // emitSuccess or emitError on the promise.
 | ||||||
|  |       response.on('end', function () { | ||||||
|  |         var err | ||||||
|  | 
 | ||||||
|  |         if (cbCalled) return | ||||||
|  |         cbCalled = true | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |           var decoded = JSON.parse(buffer) | ||||||
|  |         } catch (e) { | ||||||
|  |           if (response.statusCode !== 200) { | ||||||
|  |             err = new Error('Invalid params, response status code: ' + response.statusCode) | ||||||
|  |             err.code = -32602 | ||||||
|  |             reject(err) | ||||||
|  |           } else { | ||||||
|  |             err = new Error('Problem parsing JSON response from server') | ||||||
|  |             err.code = -32603 | ||||||
|  |             reject(err) | ||||||
|  |           } | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!Array.isArray(decoded)) { | ||||||
|  |           decoded = [decoded] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // iterate over each response, normally there will be just one
 | ||||||
|  |         // unless a batch rpc call response is being processed
 | ||||||
|  |         decoded.forEach(function (decodedResponse, i) { | ||||||
|  |           if (decodedResponse.hasOwnProperty('error') && decodedResponse.error != null) { | ||||||
|  |             if (reject) { | ||||||
|  |               err = new Error(decodedResponse.error.message || '') | ||||||
|  |               if (decodedResponse.error.code) { | ||||||
|  |                 err.code = decodedResponse.error.code | ||||||
|  |               } | ||||||
|  |               reject(err) | ||||||
|  |             } | ||||||
|  |           } else if (decodedResponse.hasOwnProperty('result')) { | ||||||
|  |             // @ts-ignore
 | ||||||
|  |             resolve(decodedResponse.result, response.headers) | ||||||
|  |           } else { | ||||||
|  |             if (reject) { | ||||||
|  |               err = new Error(decodedResponse.error.message || '') | ||||||
|  |               if (decodedResponse.error.code) { | ||||||
|  |                 err.code = decodedResponse.error.code | ||||||
|  |               } | ||||||
|  |               reject(err) | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  |     request.end(requestJSON); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports.JsonRPC = JsonRPC | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user