const MoneroError = require("../common/MoneroError");
/**
* Copyright (c) woodser
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Monero daemon interface and default implementations.
*
* @interface
*/
class MoneroDaemon {
/**
* Register a listener to receive daemon notifications.
*
* @param {MoneroDaemonListener} listener - listener to receive daemon notifications
*/
async addListener(listener) {
throw new MoneroError("Subclass must implement");
}
/**
* Unregister a listener to receive daemon notifications.
*
* @param {MoneroDaemonListener} listener - listener to unregister
*/
async removeListener(listener) {
throw new MoneroError("Subclass must implement");
}
/**
* Get the listeners registered with the daemon.
*
* @return {MoneroDaemonListener[]} the registered listeners
*/
getListeners() {
throw new MoneroError("Subclass must implement");
}
/**
* Indicates if the client is connected to the daemon via RPC.
*
* @return {boolean} true if the client is connected to the daemon, false otherwise
*/
async isConnected() {
throw new MoneroError("Subclass must implement");
}
/**
* Gets the version of the daemon.
*
* @return {MoneroVersion} the version of the daemon
*/
async getVersion() {
throw new MoneroError("Subclass must implement");
}
/**
* Indicates if the daemon is trusted xor untrusted.
*
* @return {boolean} true if the daemon is trusted, false otherwise
*/
async isTrusted() {
throw new MoneroError("Subclass must implement");
}
/**
* Get the number of blocks in the longest chain known to the node.
*
* @return {int} the number of blocks
*/
async getHeight() {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block's hash by its height.
*
* @param {int} height - height of the block hash to get
* @return {string} the block's hash at the given height
*/
async getBlockHash(height) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block template for mining a new block.
*
* @param {string} walletAddress - address of the wallet to receive miner transactions if block is successfully mined
* @param {int} reserveSize - reserve size (optional)
* @return {MoneroBlockTemplate} is a block template for mining a new block
*/
async getBlockTemplate(walletAddress, reserveSize) {
throw new MoneroError("Subclass must implement");
}
/**
* Get the last block's header.
*
* @return {MoneroBlockHeader} last block's header
*/
async getLastBlockHeader() {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block header by its hash.
*
* @param {string} blockHash - hash of the block to get the header of
* @return {MoneroBlockHeader} block's header
*/
async getBlockHeaderByHash(blockHash) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block header by its height.
*
* @param {int} height - height of the block to get the header of
* @return {MoneroBlockHeader} block's header
*/
async getBlockHeaderByHeight(height) {
throw new MoneroError("Subclass must implement");
}
/**
* Get block headers for the given range.
*
* @param {int} startHeight - start height lower bound inclusive (optional)
* @param {int} endHeight - end height upper bound inclusive (optional)
* @return {MoneroBlockHeader[]} for the given range
*/
async getBlockHeadersByRange(startHeight, endHeight) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block by hash.
*
* @param {string} blockHash - hash of the block to get
* @return {MoneroBlock} with the given hash
*/
async getBlockByHash(blockHash) {
throw new MoneroError("Subclass must implement");
}
/**
* Get blocks by hash.
*
* @param {string[]} blockHashes - array of hashes; first 10 blocks hashes goes sequential,
* next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on,
* and the last one is always genesis block
* @param {int} startHeight - start height to get blocks by hash
* @param {boolean} prune - specifies if returned blocks should be pruned (defaults to false) // TODO: test default
* @return {MoneroBlock[]} retrieved blocks
*/
async getBlocksByHash(blockHashes, startHeight, prune) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a block by height.
*
* @param {int} height - height of the block to get
* @return {MoneroBlock} with the given height
*/
async getBlockByHeight(height) {
throw new MoneroError("Subclass must implement");
}
/**
* Get blocks at the given heights.
*
* @param {int[]} heights - heights of the blocks to get
* @return {MoneroBlock[]} are blocks at the given heights
*/
async getBlocksByHeight(heights) {
throw new MoneroError("Subclass must implement");
}
/**
* Get blocks in the given height range.
*
* @param {int} startHeight - start height lower bound inclusive (optional)
* @param {int} endHeight - end height upper bound inclusive (optional)
* @return {MoneroBlock[]} are blocks in the given height range
*/
async getBlocksByRange(startHeight, endHeight) {
throw new MoneroError("Subclass must implement");
}
/**
* Get blocks in the given height range as chunked requests so that each request is
* not too big.
*
* @param {int} startHeight - start height lower bound inclusive (optional)
* @param {int} endHeight - end height upper bound inclusive (optional)
* @param {int} maxChunkSize - maximum chunk size in any one request (default 3,000,000 bytes)
* @return {MoneroBlock[]} blocks in the given height range
*/
async getBlocksByRangeChunked(startHeight, endHeight, maxChunkSize) {
throw new MoneroError("Subclass must implement");
}
/**
* Get block hashes as a binary request to the daemon.
*
* @param {string[]} blockHashes - specify block hashes to fetch; first 10 blocks hash goes
* sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64
* and so on, and the last one is always genesis block
* @param {int} startHeight - starting height of block hashes to return
* @return {string[]} requested block hashes
*/
async getBlockHashes(blockHashes, startHeight) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a transaction by hash.
*
* @param {string} txHash - hash of the transaction to get
* @param {boolean} prune - specifies if the returned tx should be pruned (defaults to false)
* @return {MoneroTx} transaction with the given hash
*/
async getTx(txHash, prune = false) {
return (await this.getTxs([txHash], prune))[0];
}
/**
* Get transactions by hashes.
*
* @param {string[]} txHashes - hashes of transactions to get
* @param {boolean} prune - specifies if the returned txs should be pruned (defaults to false)
* @return {MoneroTx[]} transactions with the given hashes
*/
async getTxs(txHashes, prune = false) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a transaction hex by hash.
*
* @param {string} txHash - hash of the transaction to get hex from
* @param {boolean} prune - specifies if the returned tx hex should be pruned (defaults to false)
* @return {string} tx hex with the given hash
*/
async getTxHex(txHash, prune = false) {
return (await this.getTxHexes([txHash], prune))[0];
}
/**
* Get transaction hexes by hashes.
*
* @param {string[]} txHashes - hashes of transactions to get hexes from
* @param {boolean} prune - specifies if the returned tx hexes should be pruned (defaults to false)
* @return {string[]} tx hexes
*/
async getTxHexes(txHashes, prune = false) {
throw new MoneroError("Subclass must implement");
}
/**
* Gets the total emissions and fees from the genesis block to the current height.
*
* @param {int} height - height to start computing the miner sum
* @param {int} numBlocks - number of blocks to include in the sum
* @return {MoneroMinerTxSum} encapsulates the total emissions and fees since the genesis block
*/
async getMinerTxSum(height, numBlocks) {
throw new MoneroError("Subclass must implement");
}
/**
* Get mining fee estimates per kB.
*
* @param {number} graceBlocks TODO
* @return {MoneroFeeEstimate} mining fee estimates per kB
*/
async getFeeEstimate(graceBlocks) {
throw new MoneroError("Subclass must implement");
}
/**
* Submits a transaction to the daemon's pool.
*
* @param {string} txHex - raw transaction hex to submit
* @param {boolean} doNotRelay specifies if the tx should be relayed (optional)
* @return {MoneroSubmitTxResult} contains submission results
*/
async submitTxHex(txHex, doNotRelay) {
throw new MoneroError("Subclass must implement");
}
/**
* Relays a transaction by hash.
*
* @param {string} txHash - hash of the transaction to relay
*/
async relayTxByHash(txHash) {
const assert = require("assert");
assert.equal(typeof txHash, "string", "Must provide a transaction hash");
await this.relayTxsByHash([txHash]);
}
/**
* Relays transactions by hash.
*
* @param {string[]} txHashes - hashes of the transactinos to relay
*/
async relayTxsByHash(txHashes) {
throw new MoneroError("Subclass must implement");
}
/**
* Get valid transactions seen by the node but not yet mined into a block, as well
* as spent key image information for the tx pool.
*
* @return {MoneroTx[]} are transactions in the transaction pool
*/
async getTxPool() {
throw new MoneroError("Subclass must implement");
}
/**
* Get hashes of transactions in the transaction pool.
*
* @return {string[]} are hashes of transactions in the transaction pool
*/
async getTxPoolHashes() {
throw new MoneroError("Subclass must implement");
}
/**
* Get all transaction pool backlog.
*
* @return {MoneroTxBacklogEntry[]} backlog entries
*/
async getTxPoolBacklog() {
throw new MoneroError("Subclass must implement");
}
/**
* Get transaction pool statistics.
*
* @return {MoneroTxPoolStats} contains statistics about the transaction pool
*/
async getTxPoolStats() {
throw new MoneroError("Subclass must implement");
}
/**
* Flush transactions from the tx pool.
*
* @param {(string|string[])} hashes - specific transactions to flush (defaults to all)
*/
async flushTxPool(hashes) {
throw new MoneroError("Subclass must implement");
}
/**
* Get the spent status of the given key image.
*
* @param {string} keyImage - key image hex to get the status of
* @return {MoneroKeyImageSpentStatus} status of the key image
*/
async getKeyImageSpentStatus(keyImage) {
return (await this.getKeyImageSpentStatuses([keyImage]))[0];
}
/**
* Get the spent status of each given key image.
*
* @param {string[]} keyImages are hex key images to get the statuses of
* @return {MoneroKeyImageSpentStatus[]} status for each key image
*/
async getKeyImageSpentStatuses(keyImages) {
throw new MoneroError("Subclass must implement");
}
/**
* Get outputs identified by a list of output amounts and indices as a binary
* request.
*
* @param {MoneroOutput[]} outputs - identify each output by amount and index
* @return {MoneroOutput[]} identified outputs
*/
async getOutputs(outputs) {
throw new MoneroError("Subclass must implement");
}
/**
* Get a histogram of output amounts. For all amounts (possibly filtered by
* parameters), gives the number of outputs on the chain for that amount.
* RingCT outputs counts as 0 amount.
*
* @param {BigInteger[]} amounts - amounts of outputs to make the histogram with
* @param {int} minCount - TODO
* @param {int} maxCount - TODO
* @param {boolean} isUnlocked - makes a histogram with outputs with the specified lock state
* @param {int} recentCutoff - TODO
* @return {MoneroOutputHistogramEntry[]} are entries meeting the parameters
*/
async getOutputHistogram(amounts, minCount, maxCount, isUnlocked, recentCutoff) {
throw new MoneroError("Subclass must implement");
}
/**
* Creates an output distribution.
*
* @param {BigInteger[]} amounts - amounts of outputs to make the distribution with
* @param {boolean} cumulative - specifies if the results should be cumulative (defaults to TODO)
* @param {int} startHeight - start height lower bound inclusive (optional)
* @param {int} endHeight - end height upper bound inclusive (optional)
* @return {MoneroOutputDistributionEntry[]} are entries meeting the parameters
*/
async getOutputDistribution(amounts, cumulative, startHeight, endHeight) {
throw new MoneroError("Subclass must implement");
}
/**
* Get general information about the state of the node and the network.
*
* @return {MoneroDaemonInfo} is general information about the node and network
*/
async getInfo() {
throw new MoneroError("Subclass must implement");
}
/**
* Get synchronization information.
*
* @return {MoneroDaemonSyncInfo} contains sync information
*/
async getSyncInfo() {
throw new MoneroError("Subclass must implement");
}
/**
* Look up information regarding hard fork voting and readiness.
*
* @return {MoneroHardForkInfo} contains hard fork information
*/
async getHardForkInfo() {
throw new MoneroError("Subclass must implement");
}
/**
* Get alternative chains seen by the node.
*
* @return {MoneroAltChain[]} alternative chains
*/
async getAltChains() {
throw new MoneroError("Subclass must implement");
}
/**
* Get known block hashes which are not on the main chain.
*
* @return {string[]} known block hashes which are not on the main chain
*/
async getAltBlockHashes() {
throw new MoneroError("Subclass must implement");
}
/**
* Get the download bandwidth limit.
*
* @return {int} download bandwidth limit
*/
async getDownloadLimit() {
throw new MoneroError("Subclass must implement");
}
/**
* Set the download bandwidth limit.
*
* @param {int} limit - download limit to set (-1 to reset to default)
* @return {int} new download limit after setting
*/
async setDownloadLimit(limit) {
throw new MoneroError("Subclass must implement");
}
/**
* Reset the download bandwidth limit.
*
* @return {int} download bandwidth limit after resetting
*/
async resetDownloadLimit() {
throw new MoneroError("Subclass must implement");
}
/**
* Get the upload bandwidth limit.
*
* @return {int} upload bandwidth limit
*/
async getUploadLimit() {
throw new MoneroError("Subclass must implement");
}
/**
* Set the upload bandwidth limit.
*
* @param limit - upload limit to set (-1 to reset to default)
* @return {int} new upload limit after setting
*/
async setUploadLimit(limit) {
throw new MoneroError("Subclass must implement");
}
/**
* Reset the upload bandwidth limit.
*
* @return {int} upload bandwidth limit after resetting
*/
async resetUploadLimit() {
throw new MoneroError("Subclass must implement");
}
/**
* Get peers with active incoming or outgoing connections to the node.
*
* @return {MoneroPeer[]} the daemon's peers
*/
async getPeers() {
throw new MoneroError("Subclass must implement");
}
/**
* Get known peers including their last known online status.
*
* @return {MoneroPeer[]} the daemon's known peers
*/
async getKnownPeers() {
throw new MoneroError("Subclass must implement");
}
/**
* Limit number of outgoing peers.
*
* @param {int} limit - maximum number of outgoing peers
*/
async setOutgoingPeerLimit(limit) {
throw new MoneroError("Subclass must implement");
}
/**
* Limit number of incoming peers.
*
* @param {int} limit - maximum number of incoming peers
*/
async setIncomingPeerLimit(limit) {
throw new MoneroError("Subclass must implement");
}
/**
* Get peer bans.
*
* @return {MoneroBan[]} entries about banned peers
*/
async getPeerBans() {
throw new MoneroError("Subclass must implement");
}
/**
* Ban a peer node.
*
* @param {MoneroBan} ban - contains information about a node to ban
*/
async setPeerBan(ban) {
return await this.setPeerBans([ban]);
}
/**
* Ban peers nodes.
*
* @param {MoneroBan[]} bans - specify which peers to ban
*/
async setPeerBans(bans) {
throw new MoneroError("Subclass must implement");
}
/**
* Start mining.
*
* @param {string} address - address given miner rewards if the daemon mines a block
* @param {integer} numThreads - number of mining threads to run
* @param {boolean} isBackground - specifies if the miner should run in the background or not
* @param {boolean} ignoreBattery - specifies if the battery state (e.g. on laptop) should be ignored or not
*/
async startMining(address, numThreads, isBackground, ignoreBattery) {
throw new MoneroError("Subclass must implement");
}
/**
* Stop mining.
*/
async stopMining() {
throw new MoneroError("Subclass must implement");
}
/**
* Get the daemon's mining status.
*
* @return {MoneroMiningStatus} daemon's mining status
*/
async getMiningStatus() {
throw new MoneroError("Subclass must implement");
}
/**
* Submit a mined block to the network.
*
* @param {string} blockBlob - mined block to submit
*/
async submitBlock(blockBlob) {
await this.submitBlocks([blockBlob]);
}
/**
* Submit mined blocks to the network.
*
* @param {string[]} blockBlobs - mined blocks to submit
*/
async submitBlocks(blockBlobs) {
throw new MoneroError("Subclass must implement");
}
/**
* Check for update.
*
* @return {MoneroDaemonUpdateCheckResult} the result
*/
async checkForUpdate() {
throw new MoneroError("Subclass must implement");
}
/**
* Download an update.
*
* @param {string} path - path to download the update (optional)
* @return {MoneroDaemonUpdateDownloadResult} the result
*/
async downloadUpdate(path) {
throw new MoneroError("Subclass must implement");
}
/**
* Safely disconnect and shut down the daemon.
*/
async stop() {
throw new MoneroError("Subclass must implement");
}
/**
* Get the header of the next block added to the chain.
*
* @return {MoneroBlockHeader} header of the next block added to the chain
*/
async waitForNextBlockHeader() {
throw new MoneroError("Subclass must implement");
}
// ----------------------------- STATIC UTILITIES ---------------------------
/**
* Parses a network string to an enumerated type.
*
* @param {string} network - network string to parse
* @return {MoneroNetworkType} enumerated network type
*/
static parseNetworkType(network) {
const MoneroNetworkType = require("./model/MoneroNetworkType");
if (network === "mainnet") return MoneroNetworkType.MAINNET;
if (network === "testnet") return MoneroNetworkType.TESTNET;
if (network === "stagenet") return MoneroNetworkType.STAGENET;
throw new MoneroError("Invalid network type to parse: " + network);
}
}
module.exports = MoneroDaemon;