UNPKG

@kazeblockchain/kazejs

Version:

Javascript libraries for Kaze wallet

377 lines (344 loc) 10.3 kB
import Query from './query' import { isAddress } from '../wallet' import semver from 'semver' import { RPC_VERSION, DEFAULT_RPC, KAZE_NETWORK } from '../consts' import logger from '../logging' import { timeout } from '../settings' const log = logger('rpc') const versionRegex = /KAZE:(\d+\.\d+\.\d+)/ /** * @class RPCClient * @classdesc * RPC Client model to query a KAZE node. Contains built-in methods to query using RPC calls. * @param {string} net - 'MainNet' or 'TestNet' will query the default RPC address found in consts. You may provide a custom URL. * @param {string} version - Version of KAZE node. Used to check if RPC methods have been implemented. it will default to DEFAULT_RPC found in CONST */ class RPCClient { constructor (net, version = RPC_VERSION) { if (net === KAZE_NETWORK.MAIN) { this.net = DEFAULT_RPC.MAIN } else if (net === KAZE_NETWORK.TEST) { this.net = DEFAULT_RPC.TEST } else { /** * The URL of the node that this client queries. * @type string */ this.net = net } /** * History of queries made with this client. * @type Query[] */ this.history = [] this.lastSeenHeight = 0 this._latencies = [] /** * Version of this client. Used to check if RPC call is implemented. * @type string */ this.version = '' if (semver.valid(version)) { this.version = semver.clean(version) } else { throw new Error(`Invalid Version: ${version}`) } } get [Symbol.toStringTag] () { return 'RPC Client' } get latency () { if (this._latencies.length === 0) return 99999 return Math.floor(this._latencies.reduce((p, c) => p + c, 0) / this._latencies.length) } set latency (lat) { if (this._latencies.length > 4) this._latencies.shift() this._latencies.push(lat) } /** * Measures the latency using getBlockCount call. Returns the current latency. For average, call this.latency * @returns {number} */ ping () { const timeStart = Date.now() var query = Query.getBlockCount() return this.execute(query, { timeout: timeout.ping }) .then(res => { this.lastSeenHeight = res.result const newPing = Date.now() - timeStart this.latency = newPing return newPing }) .catch(_ => { this.latency = timeout.ping return timeout.ping }) } /** * Takes an Query object and executes it. Adds the Query object to history. * @param {Query} query * @param {AxiosRequestConfig} config * @return {Promise<any>} */ execute (query, config) { this.history.push(query) log.info(`RPC: ${this.net} executing Query[${query.req.method}]`) return query.execute(this.net, config) } /** * Creates a query with the given req and immediately executes it. * @param {object} req * @param {AxiosRequestConfig} config * @return {Promise<any>} */ query (req, config) { const query = new Query(req) return this.execute(query, config) } /** * Gets the state of an account given an address. * @param {string} addr * @return {Promise<object>} */ getAccountState (addr) { if (!isAddress(addr)) throw new Error(`Invalid address given: ${addr}`) return this.execute(Query.getAccountState(addr)) .then((res) => { return res.result }) } /** * Gets the state of an asset given an id. * @param {string} assetId * @return {Promise<object>} */ getAssetState (assetId) { return this.execute(Query.getAssetState(assetId)) .then((res) => { return res.result }) } /** * Gets the block at a given height or hash. * @param {string|number} indexOrHash * @return {Promise<object|string>} */ getBlock (indexOrHash, verbose = 1) { return this.execute(Query.getBlock(indexOrHash, verbose)) .then((res) => { return res.result }) } /** * Gets the block hash at a given height. * @param {number} index * @return {Promise<string>} */ getBlockHash (index) { return this.execute(Query.getBlockHash(index)) .then((res) => { return res.result }) } /** * Get the latest block hash. * @return {Promise<string>} */ getBestBlockHash () { return this.execute(Query.getBestBlockHash()) .then((res) => { return res.result }) } /** * Get the current block height. * @return {Promise<number>} */ getBlockCount () { return this.execute(Query.getBlockCount()) .then((res) => { return res.result }) } /** * Get the system fees of a block. * @param {number} index * @return {Promise<string>} - System fees as a string. */ getBlockSysFee (index) { return this.execute(Query.getBlockSysFee(index)) .then((res) => { return res.result }) } /** * Gets the number of peers this node is connected to. * @return {Promise<number>} */ getConnectionCount () { return this.execute(Query.getConnectionCount()) .then((res) => { return res.result }) } /** * Gets the state of the contract at the given scriptHash. * @param {string} scriptHash * @return {Promise<object>} */ getContractState (scriptHash) { return this.execute(Query.getContractState(scriptHash)) .then((res) => { return res.result }) } /** * Gets a list of all peers that this node has discovered. * @return {Promise<object>} */ getPeers () { return this.execute(Query.getPeers()) .then((res) => { return res.result }) } /** * Gets a list of all transaction hashes waiting to be processed. * @return {Promise<string[]>} */ getRawMemPool () { return this.execute(Query.getRawMemPool()) .then((res) => { return res.result }) } /** * Gets a transaction based on its hash. * @param {string} txid * @param {number} verbose * @param {Promise<string|object>} */ getRawTransaction (txid, verbose = 1) { return this.execute(Query.getRawTransaction(txid, verbose)) .then((res) => { return res.result }) } /** * Gets the corresponding value of a key in the storage of a contract address. * @param {string} scriptHash * @param {string} key * @return {Promise<string>} value */ getStorage (scriptHash, key) { return this.execute(Query.getStorage(scriptHash, key)) .then((res) => { return res.result }) } /** * Gets the transaction output given a transaction id and index * @param {string} txid * @param {number} index * @return {Promise<object>} */ getTxOut (txid, index) { return this.execute(Query.getTxOut(txid, index)) .then((res) => { return res.result }) } /** * Gets the version of the KAZE node. This method will never be blocked by version. This method will also update the current Client's version to the one received. * @return {Promise<string>} */ getVersion () { return this.execute(Query.getVersion()) .then((res) => { const version = res.result.useragent.match(versionRegex)[1] this.version = semver.clean(version) return this.version }) .catch((err) => { if (err.message.includes('Method not found')) { this.version = RPC_VERSION return this.version } else { throw err } }) } /** * Calls a smart contract with the given parameters. This method is a local invoke, results are not reflected on the blockchain. * @param {string} scriptHash * @param {Array} params * @return {Promise<object>} */ invoke (scriptHash, params) { if (semver.lt(this.version, '2.3.3')) return Promise.reject(new Error('This method is not implemented for this version')) return this.execute(Query.invoke(scriptHash, params)) .then((res) => { return res.result }) } /** * Submits a contract method call with parameters for the node to run. This method is a local invoke, results are not reflected on the blockchain. * @param {string} scriptHash * @param {string} operation * @param {Array} params * @return {Promise<object>} */ invokeFunction (scriptHash, operation, params) { if (semver.lt(this.version, '2.3.3')) return Promise.reject(new Error('This method is not implemented for this version')) return this.execute(Query.invokeFunction(scriptHash, operation, params)) .then((res) => { return res.result }) } /** * Submits a script for the node to run. This method is a local invoke, results are not reflected on the blockchain. * @param {string} script * @return {Promise<object>} */ invokeScript (script) { if (semver.lt(this.version, '2.3.3')) return Promise.reject(new Error('This method is not implemented for this version')) return this.execute(Query.invokeScript(script)) .then((res) => { return res.result }) } /** * Sends a serialized transaction to the network. * @param {Transaction|string} transaction * @return {Promise<boolean>} */ sendRawTransaction (transaction) { return this.execute(Query.sendRawTransaction(transaction)) .then((res) => { return res.result }) } /** * Submits a serialized block to the network. * @param {string} block * @return {Promise<any>} */ submitBlock (block) { return this.execute(Query.submitBlock(block)) .then((res) => { return res.result }) } /** * Checks if the provided address is a valid KAZE address. * @param {string} addr * @return {Promise<boolean>} */ validateAddress (addr) { return this.execute(Query.validateAddress(addr)) .then((res) => { return res.result.isvalid }) } } export default RPCClient