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", | ||||
|       "license": "GNU Affero General Public License v3.0", | ||||
|       "dependencies": { | ||||
|         "@mempool/bitcoin": "^3.0.3", | ||||
|         "@mempool/electrum-client": "^1.1.7", | ||||
|         "@types/ws": "8.2.2", | ||||
|         "axios": "0.24.0", | ||||
| @ -56,14 +55,6 @@ | ||||
|         "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": { | ||||
|       "version": "1.1.8", | ||||
|       "resolved": "https://registry.npmjs.org/@mempool/electrum-client/-/electrum-client-1.1.8.tgz", | ||||
| @ -1515,11 +1506,6 @@ | ||||
|         "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": { | ||||
|       "version": "1.1.8", | ||||
|       "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" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@mempool/bitcoin": "^3.0.3", | ||||
|     "@mempool/electrum-client": "^1.1.7", | ||||
|     "@types/ws": "8.2.2", | ||||
|     "axios": "0.24.0", | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import config from '../../config'; | ||||
| import * as bitcoin from '@mempool/bitcoin'; | ||||
| const bitcoin = require('../../rpc-api/index'); | ||||
| import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | ||||
| 
 | ||||
| const nodeRpcCredentials: BitcoinRpcCredentials = { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import config from '../../config'; | ||||
| import * as bitcoin from '@mempool/bitcoin'; | ||||
| const bitcoin = require('../../rpc-api/index'); | ||||
| import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; | ||||
| 
 | ||||
| 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