diff --git a/src/js/ripple/request.js b/src/js/ripple/request.js index 48857e58dc..5ec5c61dcb 100644 --- a/src/js/ripple/request.js +++ b/src/js/ripple/request.js @@ -199,7 +199,7 @@ Request.prototype.ledgerSelect = function(ledger) { switch (ledger) { case 'current': case 'closed': - case 'verified': + case 'validated': this.message.ledger_index = ledger; break; @@ -327,7 +327,7 @@ Request.prototype.books = function(books, snapshot) { return this; }; -Request.prototype.addBook = function (book, snapshot) { +Request.prototype.addBook = function(book, snapshot) { if (!Array.isArray(this.message.books)) { this.message.books = [ ]; } diff --git a/src/js/ripple/transactionqueue.js b/src/js/ripple/transactionqueue.js index 6fa8ac0d11..d71e741860 100644 --- a/src/js/ripple/transactionqueue.js +++ b/src/js/ripple/transactionqueue.js @@ -4,6 +4,7 @@ */ var LRU = require('lru-cache'); +var Transaction = require('./transaction').Transaction; function TransactionQueue() { this._queue = [ ]; @@ -19,6 +20,15 @@ TransactionQueue.prototype.addReceivedSequence = function(sequence) { this._sequenceCache.set(String(sequence), true); }; +/** + * Check that sequence number has been consumed by a validated + * transaction + */ + +TransactionQueue.prototype.hasSequence = function(sequence) { + return this._sequenceCache.has(String(sequence)); +}; + /** * Store received (validated) ID transaction */ @@ -35,22 +45,13 @@ TransactionQueue.prototype.getReceived = function(id) { return this._idCache.get(id); }; -/** - * Check that sequence number has been consumed by a validated - * transaction - */ - -TransactionQueue.prototype.hasSequence = function(sequence) { - return this._sequenceCache.has(String(sequence)); -}; - /** * Get a submitted transaction by ID. Transactions * may have multiple associated IDs. */ TransactionQueue.prototype.getSubmission = function(id) { - var result = false; + var result = void(0); for (var i=0, tx; (tx=this._queue[i]); i++) { if (~tx.submittedIDs.indexOf(id)) { @@ -70,6 +71,14 @@ TransactionQueue.prototype.remove = function(tx) { // ND: We are just removing the Transaction by identity var i = this._queue.length; + if (typeof tx === 'string') { + tx = this.getSubmission(tx); + } + + if (!(tx instanceof Transaction)) { + return; + } + while (i--) { if (this._queue[i] === tx) { this._queue.splice(i, 1); diff --git a/test/request-test.js b/test/request-test.js new file mode 100644 index 0000000000..3dbeb17206 --- /dev/null +++ b/test/request-test.js @@ -0,0 +1,494 @@ +var assert = require('assert'); +var utils = require('./testutils'); +var Request = utils.load_module('request').Request; +var Remote = utils.load_module('remote').Remote; +var Server = utils.load_module('server').Server; + +function makeServer(url) { + var server = new Server(new process.EventEmitter(), url); + server._connected = true; + return server; +}; + +const SERVER_INFO = { + "info": { + "build_version": "0.25.2-rc1", + "complete_ledgers": "32570-7016339", + "hostid": "LIED", + "io_latency_ms": 1, + "last_close": { + "converge_time_s": 2.013, + "proposers": 5 + }, + "load_factor": 1, + "peers": 42, + "pubkey_node": "n9LpxYuMx4Epz4Wz8Kg2kH3eBTx1mUtHnYwtCdLoj3HC85L2pvBm", + "server_state": "full", + "validated_ledger": { + "age": 0, + "base_fee_xrp": 0.00001, + "hash": "E43FD49087B18031721D9C3C4743FE1692C326AFF7084A2C01B355CE65A4C699", + "reserve_base_xrp": 20, + "reserve_inc_xrp": 5, + "seq": 7016339 + }, + "validation_quorum": 3 + } +}; + +describe('Request', function() { + it('Send request', function(done) { + var remote = { + request: function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + done(); + } + }; + + var request = new Request(remote, 'server_info'); + + request.request(); + }); + + it('Broadcast request', function(done) { + var servers = [ + makeServer('wss://localhost:5006'), + makeServer('wss://localhost:5007') + ]; + + var requests = 0; + + servers.forEach(function(server, index, arr) { + server._request = function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + if (++requests === arr.length) { + done(); + } + }; + }); + + var remote = new Remote(); + remote._connected = true; + remote._servers = servers; + + var request = new Request(remote, 'server_info'); + + request.broadcast(); + }); + + it('Events API', function(done) { + var server = makeServer('wss://localhost:5006'); + + server._request = function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + req.emit('success', SERVER_INFO); + }; + + var remote = new Remote(); + remote._connected = true; + remote._servers = [ server ]; + + var request = new Request(remote, 'server_info'); + + request.once('success', function(res) { + assert.deepEqual(res, SERVER_INFO); + done(); + }); + + request.request(); + }); + + it('Callback API', function(done) { + var server = makeServer('wss://localhost:5006'); + + server._request = function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + req.emit('success', SERVER_INFO); + }; + + var remote = new Remote(); + remote._connected = true; + remote._servers = [ server ]; + + var request = new Request(remote, 'server_info'); + + request.callback(function(err, res) { + assert.ifError(err); + assert.deepEqual(res, SERVER_INFO); + done(); + }); + }); + + it('Timeout', function(done) { + var server = makeServer('wss://localhost:5006'); + var successEmited = false; + + server._request = function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + setTimeout(function() { + successEmitted = true; + req.emit('success', SERVER_INFO); + }, 200); + }; + + var remote = new Remote(); + remote._connected = true; + remote._servers = [ server ]; + + var request = new Request(remote, 'server_info'); + + request.timeout(10, function() { + setTimeout(function() { + assert(successEmitted); + done(); + }, 200); + }); + + request.callback(function(err, res) { + assert(false, 'Callback should not be called'); + }); + }); + + it('Set server', function(done) { + var servers = [ + makeServer('wss://localhost:5006'), + makeServer('wss://localhost:5007') + ]; + + servers[1]._request = function(req) { + assert(req instanceof Request); + assert.strictEqual(typeof req.message, 'object'); + assert.strictEqual(req.message.command, 'server_info'); + done(); + }; + + var remote = new Remote(); + remote._connected = true; + remote._servers = servers; + + remote.getServer = function() { + return servers[0]; + }; + + var request = new Request(remote, 'server_info'); + request.setServer(servers[1]); + + assert.strictEqual(request.server, servers[1]); + + request.request(); + }); + + it('Set build path', function() { + var remote = new Remote(); + remote._connected = true; + remote.local_signing = false; + + var request = new Request(remote, 'server_info'); + request.buildPath(true); + assert.strictEqual(request.message.build_path, true); + }); + + it('Remove build path', function() { + var remote = new Remote(); + remote._connected = true; + remote.local_signing = false; + + var request = new Request(remote, 'server_info'); + request.buildPath(false); + assert(!request.message.hasOwnProperty('build_path')); + }); + + it('Set build path with local signing', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + + assert.throws(function() { + request.buildPath(true); + }, Error); + }); + + it('Set ledger hash', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerHash('B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + }); + + it('Set ledger index', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerIndex(7016915); + assert.strictEqual(request.message.ledger_index, 7016915); + }); + + it('Select ledger (identifier)', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerSelect('validated'); + assert.strictEqual(request.message.ledger_index, 'validated'); + }); + + it('Select ledger (index)', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerSelect(7016915); + assert.strictEqual(request.message.ledger_index, 7016915); + }); + + it('Select ledger (hash)', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerSelect('B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + }); + + it('Select ledger (hash)', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.ledgerSelect('B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE'); + }); + + it('Set offer ID', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.offerId('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', 1337); + assert.deepEqual(request.message.offer, { + account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', + seq: 1337 + }); + }); + + it('Set offer index', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.offerIndex(1337); + assert.strictEqual(request.message.offer, 1337); + }); + + it('Set secret', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.secret('mySecret'); + assert.strictEqual(request.message.secret, 'mySecret'); + }); + + it('Set transaction hash', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.txHash('E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7'); + assert.strictEqual(request.message.tx_hash, 'E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7'); + }); + + it('Set transaction JSON', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + var txJson = { hash: 'E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7' }; + request.txJson(txJson); + assert.deepEqual(request.message.tx_json, txJson); + }); + + it('Set transaction blob', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.txBlob('asdf'); + assert.strictEqual(request.message.tx_blob, 'asdf'); + }); + + it('Set ripple state', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.rippleState('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', 'USD'); + assert.deepEqual(request.message.ripple_state, { + currency: 'USD', + accounts: [ 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59' ] + }); + }); + + it('Set accounts', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.accounts([ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + + assert.deepEqual(request.message.accounts, [ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + }); + + it('Set accounts proposed', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + request.accountsProposed([ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + + assert.deepEqual(request.message.accounts_proposed, [ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + }); + + it('Add account', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + + request.accounts([ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + ]); + + request.addAccount('rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'); + + assert.deepEqual(request.message.accounts, [ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + }); + + it('Add account proposed', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + + request.accountsProposed([ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + ]); + + request.addAccountProposed('rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'); + + assert.deepEqual(request.message.accounts_proposed, [ + 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun', + 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' + ]); + }); + + it('Set books', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + + var books = [ + { + "taker_gets": { + "currency": "EUR", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "taker_pays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + } + ]; + + request.books(books); + + assert.deepEqual(request.message.books, books); + }); + + it('Add book', function() { + var remote = new Remote(); + remote._connected = true; + + var request = new Request(remote, 'server_info'); + + var books = [ + { + "taker_gets": { + "currency": "EUR", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "taker_pays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + } + ]; + + request.books(books); + + request.addBook({ + "taker_gets": { + "currency": "CNY", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "taker_pays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + }); + + assert.deepEqual(request.message.books, [ + { + "taker_gets": { + "currency": "EUR", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "taker_pays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + }, + + { + "taker_gets": { + "currency": "CNY", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "taker_pays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + } + ]); + }); +}); diff --git a/test/transaction-queue-test.js b/test/transaction-queue-test.js new file mode 100644 index 0000000000..8288ea18d7 --- /dev/null +++ b/test/transaction-queue-test.js @@ -0,0 +1,92 @@ +var assert = require('assert'); +var utils = require('./testutils'); +var Transaction = utils.load_module('transaction').Transaction; +var TransactionQueue = utils.load_module('transactionqueue').TransactionQueue; + +describe('Transaction queue', function() { + it('Push transaction', function() { + var queue = new TransactionQueue(); + var tx = new Transaction(); + + queue.push(tx); + + assert.strictEqual(queue.length(), 1); + }); + + it('Remove transaction', function() { + var queue = new TransactionQueue(); + var tx = new Transaction(); + + queue.push(tx); + queue.remove(tx); + + assert.strictEqual(queue.length(), 0); + }); + + it('Remove transaction by ID', function() { + var queue = new TransactionQueue(); + var tx = new Transaction(); + + queue.push(tx); + + tx.submittedIDs = [ + '1A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B', + '2A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B' + ]; + + queue.remove('3A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'); + + assert.strictEqual(queue.length(), 1); + + queue.remove('2A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'); + + assert.strictEqual(queue.length(), 0); + }); + + it('Add sequence', function() { + var queue = new TransactionQueue(); + queue.addReceivedSequence(1); + assert(queue.hasSequence(1)); + }); + + it('Add ID', function() { + var queue = new TransactionQueue(); + var tx = new Transaction(); + queue.addReceivedId('1A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B', tx); + assert.strictEqual(queue.getReceived('2A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'), void(0)); + assert.strictEqual(queue.getReceived('1A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'), tx); + }); + + it('Get submission', function() { + var queue = new TransactionQueue(); + var tx = new Transaction(); + + queue.push(tx); + + tx.submittedIDs = [ + '1A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B', + '2A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B' + ]; + + assert.strictEqual(queue.getSubmission('1A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'), tx); + assert.strictEqual(queue.getSubmission('2A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'), tx); + assert.strictEqual(queue.getSubmission('3A4DEBF37496464145AA301F0AA77712E3A2BFE3480D24C3584663F800B85B5B'), void(0)); + }); + + it('Iterate over queue', function() { + var queue = new TransactionQueue(); + var count = 10; + + for (var i=0; i