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