From 795715729c36dd9a5982617e1bb28f6278a61413 Mon Sep 17 00:00:00 2001 From: styppo Date: Mon, 29 Jul 2019 08:36:20 -0600 Subject: [PATCH] * Fix limit parameter in Client.getTransactionsByAddress() * Add missing message types to MessageFactory * Add more Client tests --- clients/nodejs/modules/JsonRpcServer.js | 2 +- src/main/generic/api/Client.js | 11 ++-- .../generic/consensus/BaseConsensusAgent.js | 1 - src/main/generic/consensus/full/FullChain.js | 4 +- .../generic/network/message/MessageFactory.js | 4 +- src/test/specs/generic/api/Client.spec.js | 54 ++++++++++++++++--- src/test/specs/spec.js | 2 +- 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/clients/nodejs/modules/JsonRpcServer.js b/clients/nodejs/modules/JsonRpcServer.js index 57b44de65..c4e12eb7f 100644 --- a/clients/nodejs/modules/JsonRpcServer.js +++ b/clients/nodejs/modules/JsonRpcServer.js @@ -664,7 +664,7 @@ class JsonRpcServer { obj.recipient = account.recipient.toHex(); obj.recipientAddress = account.recipient.toUserFriendlyAddress(); obj.hashRoot = account.hashRoot.toHex(); - obj.hashAlgorithm = account.hashRoot.algorithm + obj.hashAlgorithm = account.hashRoot.algorithm; obj.hashCount = account.hashCount; obj.timeout = account.timeout; obj.totalAmount = account.totalAmount; diff --git a/src/main/generic/api/Client.js b/src/main/generic/api/Client.js index 88fd9fa77..aa624a3ef 100644 --- a/src/main/generic/api/Client.js +++ b/src/main/generic/api/Client.js @@ -677,13 +677,14 @@ class Client { * not necessarily be considered a confirmation that a transaction was actually mined in a block. * * @param {Address|string} address Address of an account + * @param {number} [limit=Infinity] Maximum number of receipts to return, may be exceeded depending on your client configuration. * @returns {Promise.>} */ - async getTransactionReceiptsByAddress(address) { + async getTransactionReceiptsByAddress(address, limit = Infinity) { address = Address.fromAny(address); const consensus = await this._consensus; - return consensus.getTransactionReceiptsByAddress(address); + return consensus.getTransactionReceiptsByAddress(address, limit); } /** @@ -724,10 +725,10 @@ class Client { * @param {Address|string} address Address of an account * @param {number} [sinceBlockHeight=0] Minimum block height to consider for updates * @param {Array.} [knownTransactionDetails] List of transaction details on already known transactions since {@param sinceBlockHeight} - * @param {number} [limit=NumberUtils.UINT64_MAX] Maximum number of transactions to return, this number may be exceeded for large knownTransactionDetails sets. + * @param {number} [limit=Infinity] Maximum number of transactions to return, this number may be exceeded for large knownTransactionDetails sets. * @return {Promise.>} */ - async getTransactionsByAddress(address, sinceBlockHeight = 0, knownTransactionDetails, limit = NumberUtils.UINT64_MAX) { + async getTransactionsByAddress(address, sinceBlockHeight = 0, knownTransactionDetails, limit = Infinity) { address = Address.fromAny(address); const knownTxs = new HashMap(); if (knownTransactionDetails) { @@ -752,7 +753,7 @@ class Client { // Fetch transaction receipts. const receipts = new HashSet((receipt) => receipt.transactionHash); - if (txs.length < count) receipts.addAll(await consensus.getTransactionReceiptsByAddress(address, txs.length - limit)); + if (txs.length < limit) receipts.addAll(await consensus.getTransactionReceiptsByAddress(address, limit - txs.length)); /** @type {HashMap.>} */ const requestProofs = new HashMap(); diff --git a/src/main/generic/consensus/BaseConsensusAgent.js b/src/main/generic/consensus/BaseConsensusAgent.js index 809e251ac..2cec221b5 100644 --- a/src/main/generic/consensus/BaseConsensusAgent.js +++ b/src/main/generic/consensus/BaseConsensusAgent.js @@ -946,7 +946,6 @@ class BaseConsensusAgent extends Observable { async _onGetData(msg) { // Keep track of the objects the peer knows. for (const vector of msg.vectors) { - Log.v(BaseConsensusAgent, `Got request for ${vector.hash} from ${this.peer.peerAddress}`); this._knownObjects.add(vector); } diff --git a/src/main/generic/consensus/full/FullChain.js b/src/main/generic/consensus/full/FullChain.js index cdf4070c5..68ba751d2 100644 --- a/src/main/generic/consensus/full/FullChain.js +++ b/src/main/generic/consensus/full/FullChain.js @@ -592,8 +592,8 @@ class FullChain extends BaseChain { } const transactionReceipts = []; - const entriesBySender = await this._transactionStore.getBySender(address, (!limit || limit < 0) ? null : (limit / 2)); - const entriesByRecipient = await this._transactionStore.getByRecipient(address, (!limit || limit < 0) ? null : (limit / 2)); + const entriesBySender = await this._transactionStore.getBySender(address, (!limit || limit < 0 || !Number.isFinite(limit)) ? null : (limit / 2)); + const entriesByRecipient = await this._transactionStore.getByRecipient(address, (!limit || limit < 0 || !Number.isFinite(limit)) ? null : (limit / 2)); entriesBySender.forEach(entry => { transactionReceipts.push(new TransactionReceipt(entry.transactionHash, entry.blockHash, entry.blockHeight)); diff --git a/src/main/generic/network/message/MessageFactory.js b/src/main/generic/network/message/MessageFactory.js index 14bcbd868..4a60bfa21 100644 --- a/src/main/generic/network/message/MessageFactory.js +++ b/src/main/generic/network/message/MessageFactory.js @@ -51,8 +51,10 @@ MessageFactory.CLASSES[Message.Type.TRANSACTIONS_PROOF] = TransactionsProofMessa MessageFactory.CLASSES[Message.Type.GET_TRANSACTION_RECEIPTS_BY_ADDRESS] = GetTransactionReceiptsByAddressMessage; MessageFactory.CLASSES[Message.Type.TRANSACTION_RECEIPTS] = TransactionReceiptsMessage; MessageFactory.CLASSES[Message.Type.GET_BLOCK_PROOF] = GetBlockProofMessage; -MessageFactory.CLASSES[Message.Type.GET_BLOCK_PROOF_AT] = GetBlockProofAtMessage; MessageFactory.CLASSES[Message.Type.BLOCK_PROOF] = BlockProofMessage; +MessageFactory.CLASSES[Message.Type.GET_TRANSACTIONS_PROOF_BY_HASHES] = GetTransactionsProofByHashesMessage; +MessageFactory.CLASSES[Message.Type.GET_TRANSACTION_RECEIPTS_BY_HASHES] = GetTransactionReceiptsByHashesMessage; +MessageFactory.CLASSES[Message.Type.GET_BLOCK_PROOF_AT] = GetBlockProofAtMessage; MessageFactory.CLASSES[Message.Type.GET_HEAD] = GetHeadMessage; MessageFactory.CLASSES[Message.Type.HEAD] = HeadMessage; MessageFactory.CLASSES[Message.Type.VERACK] = VerAckMessage; diff --git a/src/test/specs/generic/api/Client.spec.js b/src/test/specs/generic/api/Client.spec.js index 12347684b..8ae9651b8 100644 --- a/src/test/specs/generic/api/Client.spec.js +++ b/src/test/specs/generic/api/Client.spec.js @@ -72,50 +72,88 @@ describe('Client', () => { done(); }); - established('can be used to fetch head height', async (done, client) => { + established('can fetch head height', async (done, client) => { expect(await client.getHeadHeight()).toBe(otherConsensus.blockchain.height); done(); }); - established('can be used to fetch head hash', async (done, client) => { + established('can fetch head hash', async (done, client) => { expect((await client.getHeadHash()).equals(otherConsensus.blockchain.headHash)).toBeTruthy(); done(); }); - established('can be used to fetch light head block', async (done, client) => { + established('can fetch light head block', async (done, client) => { expect((await client.getHeadBlock(false)).toLight().equals(otherConsensus.blockchain.head.toLight())).toBeTruthy(); done(); }); - established('can be used to fetch full head block', async (done, client) => { + established('can fetch full head block', async (done, client) => { expect((await client.getHeadBlock(true)).equals(otherConsensus.blockchain.head)).toBeTruthy(); done(); }); - established('can be used to fetch light block by hash', async (done, client) => { + established('can fetch light block by hash', async (done, client) => { const block = await otherConsensus.blockchain.getBlockAt(2); expect((await client.getBlock(block.hash(), false)).toLight().equals(block.toLight())).toBeTruthy(); done(); }); - established('can be used to fetch full block by hash', async (done, client) => { + established('can fetch full block by hash', async (done, client) => { const block = await otherConsensus.blockchain.getBlockAt(2); expect((await client.getBlock(block.hash(), true)).toLight().equals(block)).toBeTruthy(); done(); }); - established('can be used to fetch light block by height', async (done, client) => { + established('can fetch light block by height', async (done, client) => { const block = await otherConsensus.blockchain.getBlockAt(2); expect((await client.getBlockAt(block.height, false)).toLight().equals(block.toLight())).toBeTruthy(); done(); }); - established('can be used to fetch full block by height', async (done, client) => { + established('can fetch full block by height', async (done, client) => { const block = await otherConsensus.blockchain.getBlockAt(2); expect((await client.getBlockAt(block.height, true)).toLight().equals(block)).toBeTruthy(); done(); }); + established('can fetch an account', async (done, client) => { + const block = await otherConsensus.blockchain.getBlockAt(1, true); + const account = (await otherConsensus.getAccounts([block.minerAddr]))[0]; + expect((await client.getAccount(block.minerAddr)).equals(account)).toBeTruthy(); + done(); + }); + + established('can fetch multiple accounts', async (done, client) => { + const block = await otherConsensus.blockchain.getBlockAt(2, true); + const accounts = await otherConsensus.getAccounts([block.minerAddr, Address.NULL]); + expect((await client.getAccounts([block.minerAddr, Address.NULL])).every((account, i) => accounts[i].equals(account))).toBeTruthy(); + done(); + }); + + established('can fetch a transaction by hash', async (done, client) => { + const block = await otherConsensus.blockchain.getBlockAt(2, true); + const tx = block.transactions[0]; + expect((await client.getTransaction(tx.hash())).transaction.equals(tx)).toBeTruthy(); + done(); + }); + + established('can fetch transactions by address', async (done, client) => { + const block = await otherConsensus.blockchain.getBlockAt(1, true); + const receipts = await otherConsensus.getTransactionReceiptsByAddress(block.minerAddr, Infinity); + const details = (await client.getTransactionsByAddress(block.minerAddr)) + .filter(detail => detail.state === Client.TransactionState.MINED || detail.state === Client.TransactionState.CONFIRMED); + receipts.sort((a, b) => a.transactionHash.compare(b.transactionHash)); + details.sort((a, b) => a.transaction.hash().compare(b.transaction.hash())); + expect(details.length).toBe(receipts.length); + expect(details.every((detail, i) => { + const receipt = receipts[i]; + return detail.transaction.hash().equals(receipt.transactionHash) + && detail.blockHash.equals(receipt.blockHash) + && detail.blockHeight === receipt.blockHeight; + })).toBeTruthy(); + done(); + }); + allit('reports head changed', async (done, _, consensus) => { const client = startClient(consensus); let handle; diff --git a/src/test/specs/spec.js b/src/test/specs/spec.js index c3969e4f3..9f8b660bd 100644 --- a/src/test/specs/spec.js +++ b/src/test/specs/spec.js @@ -23,12 +23,12 @@ if (process.env.JASMINE_VERBOSE) { listStyle: 'indent', // "flat"|"indent" activity: true }); - Nimiq.Log.instance.level = process.env.NIMIQ_LOG ? 2 : 10; } // initialize and execute jasmine.getEnv().clearReporters(); jasmine.getEnv().addReporter(reporter); +Nimiq.Log.instance.level = process.env.NIMIQ_LOG ? 2 : 10; Nimiq.Log.instance._native._global_prefix = String.fromCharCode(27) + '[1K\r'; global.Class = {