diff --git a/src/api/api.js b/src/api/api.js deleted file mode 100644 index 474048a8e4..0000000000 --- a/src/api/api.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; -const ripple = require('./common').core; -const generateWallet = require('./generate/wallet'); -const server = require('./server/server'); -const balances = require('./ledger/balances'); -const settings = require('./ledger/settings'); -const transactions = require('./ledger/transactions'); -const trustlines = require('./ledger/trustlines'); -const payments = require('./ledger/payments'); -const orders = require('./ledger/orders'); -const preparePayment = require('./transaction/payment'); -const prepareOrder = require('./transaction/order'); -const prepareOrderCancellation = require('./transaction/ordercancellation'); -const prepareTrustline = require('./transaction/trustline'); -const prepareSettings = require('./transaction/settings'); -const sign = require('./transaction/sign'); -const submit = require('./transaction/submit'); -const errors = require('./common').errors; - -function RippleAPI(options) { - this.remote = new ripple.Remote(options); -} - -RippleAPI.prototype = { - generateWallet: generateWallet, - - connect: server.connect, - getServerStatus: server.getServerStatus, - getFee: server.getFee, - isConnected: server.isConnected, - - getBalances: balances.getBalances, - getPayment: payments.getPayment, - getAccountPayments: payments.getAccountPayments, - getPathFind: payments.getPathFind, - getTrustlines: trustlines.getTrustlines, - getOrder: orders.getOrder, - getOrders: orders.getOrders, - getOrderBook: orders.getOrderBook, - getSettings: settings.getSettings, - getTransaction: transactions.getTransaction, - getAccountTransactions: transactions.getAccountTransactions, - - preparePayment: preparePayment, - prepareTrustline: prepareTrustline, - prepareOrder: prepareOrder, - prepareOrderCancellation: prepareOrderCancellation, - prepareSettings: prepareSettings, - sign: sign, - submit: submit, - - errors: errors -}; - -module.exports = RippleAPI; diff --git a/src/api/generate/wallet.js b/src/api/generate/wallet.js index c83431f1c7..a832481797 100644 --- a/src/api/generate/wallet.js +++ b/src/api/generate/wallet.js @@ -1,13 +1,12 @@ 'use strict'; const common = require('../common'); -function generateWallet(callback) { +function generateWallet() { const wallet = common.core.Wallet.generate(); - if (wallet) { - callback(null, {wallet: wallet}); - } else { - callback(new common.errors.ApiError('Could not generate wallet')); + if (!wallet) { + throw new common.errors.ApiError('Could not generate wallet'); } + return wallet; } module.exports = generateWallet; diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000000..b6bb8e5b77 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,57 @@ +'use strict'; +const ripple = require('./common').core; +const server = require('./server/server'); +const connect = server.connect; +// const getServerStatus = server.getServerStatus; +// const getFee = server.getFee; +// const isConnected = server.isConnected; +const getTransaction = require('./ledger/transaction'); +const getAccountTransactions = require('./ledger/transactions'); +const getTrustlines = require('./ledger/trustlines'); +const getBalances = require('./ledger/balances'); +// const getPathFind = require('./ledger/pathfind'); +// const getOrders = require('./ledger/orders'); +// const getOrderBook = require('./ledger/orderbook'); +const getSettings = require('./ledger/settings'); +const preparePayment = require('./transaction/payment'); +const prepareTrustline = require('./transaction/trustline'); +const prepareOrder = require('./transaction/order'); +const prepareOrderCancellation = require('./transaction/ordercancellation'); +const prepareSettings = require('./transaction/settings'); +const sign = require('./transaction/sign'); +const submit = require('./transaction/submit'); +const generateWallet = require('./generate/wallet'); +const errors = require('./common').errors; + +function RippleAPI(options) { + this.remote = new ripple.Remote(options); +} + +RippleAPI.prototype = { + connect, + // getServerStatus, + // getFee, + // isConnected, + + getTransaction, + getAccountTransactions, + getTrustlines, + getBalances, + // getPathFind, + // getOrders, + // getOrderBook, + getSettings, + + preparePayment, + prepareTrustline, + prepareOrder, + prepareOrderCancellation, + prepareSettings, + sign, + submit, + + generateWallet, + errors +}; + +module.exports = RippleAPI; diff --git a/src/api/ledger/balances.js b/src/api/ledger/balances.js index f3bd9bf680..a741a40a29 100644 --- a/src/api/ledger/balances.js +++ b/src/api/ledger/balances.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const async = require('async'); const utils = require('./utils'); -const getTrustlines = require('./trustlines').getTrustlines; +const getTrustlines = require('./trustlines'); const validate = utils.common.validate; const composeAsync = utils.common.composeAsync; const dropsToXrp = utils.common.dropsToXrp; @@ -12,7 +12,7 @@ function getXRPBalance(remote, address, ledgerVersion, callback) { composeAsync((data) => dropsToXrp(data.account_data.Balance), callback)); } -function parseBalanceAmount(trustline) { +function getTrustlineBalanceAmount(trustline) { return { currency: trustline.specification.currency, counterparty: trustline.specification.counterparty, @@ -25,7 +25,7 @@ function formatBalances(balances) { currency: 'XRP', value: balances[0] }; - return [xrpBalance].concat(balances[1].map(parseBalanceAmount)); + return [xrpBalance].concat(balances[1].map(getTrustlineBalanceAmount)); } function getBalances(account, options, callback) { @@ -40,4 +40,4 @@ function getBalances(account, options, callback) { ], composeAsync(formatBalances, callback)); } -module.exports.getBalances = getBalances; +module.exports = getBalances; diff --git a/src/api/ledger/orderbook.js b/src/api/ledger/orderbook.js new file mode 100644 index 0000000000..b489ccebec --- /dev/null +++ b/src/api/ledger/orderbook.js @@ -0,0 +1,171 @@ +'use strict'; +const _ = require('lodash'); +const utils = require('./utils'); +const ripple = utils.common.core; +const validate = utils.common.validate; +const bignum = require('bignumber.js'); + +/** + * Get the most recent spapshot of the order book for a currency pair + * + * @url + * @param {RippleAddress} request.params.account + * - The ripple address to use as point-of-view + * (returns unfunded orders for this account) + * @param {String ISO 4217 Currency Code + RippleAddress} request.params.base + * - Base currency as currency+issuer + * @param {String ISO 4217 Currency Code + RippleAddress} + * request.params.counter - Counter currency as currency+issuer + * + * @query + * @param {String} [request.query.limit] + * - Set a limit to the number of results returned + * + * @param {Express.js Request} request + */ +function getOrderBook(account, orderbook, options, callback) { + const self = this; + validate.address(account); + validate.orderbook(orderbook); + validate.options(options); + + const params = _.assign({}, orderbook, options, { + validated: true, + order_book: orderbook.base + '/' + orderbook.counter + }); + + function getLastValidatedLedger(parameters) { + const promise = new Promise(function(resolve, reject) { + const ledgerRequest = self.remote.requestLedger('validated'); + + ledgerRequest.once('success', function(res) { + parameters.ledger = res.ledger.ledger_index; + resolve(parameters); + }); + + ledgerRequest.once('error', reject); + ledgerRequest.request(); + }); + + return promise; + } + + function getBookOffers(taker_gets, taker_pays, parameters) { + const promise = new Promise(function(resolve, reject) { + const bookOffersRequest = self.remote.requestBookOffers({ + taker_gets: {currency: taker_gets.currency, + issuer: taker_gets.counterparty}, + taker_pays: {currency: taker_pays.currency, + issuer: taker_pays.counterparty}, + ledger: parameters.ledger, + limit: parameters.limit, + taker: account + }); + + bookOffersRequest.once('success', resolve); + bookOffersRequest.once('error', reject); + bookOffersRequest.request(); + }); + + return promise; + } + + function getBids(parameters) { + const taker_gets = parameters.counter; + const taker_pays = parameters.base; + + return getBookOffers(taker_gets, taker_pays, parameters); + } + + function getAsks(parameters) { + const taker_gets = parameters.base; + const taker_pays = parameters.counter; + + return getBookOffers(taker_gets, taker_pays, parameters); + } + + function getBidsAndAsks(parameters) { + return Promise.join( + getBids(parameters), + getAsks(parameters), + function(bids, asks) { + return [bids, asks, parameters]; + } + ); + } + + function getParsedBookOffers(offers, isAsk) { + return offers.reduce(function(orderBook, off) { + let price; + const order_maker = off.Account; + const sequence = off.Sequence; + + // Transaction Flags + const passive = (off.Flags & ripple.Remote.flags.offer.Passive) !== 0; + const sell = (off.Flags & ripple.Remote.flags.offer.Sell) !== 0; + + const taker_gets_total = utils.parseCurrencyAmount(off.TakerGets); + const taker_gets_funded = off.taker_gets_funded ? + utils.parseCurrencyAmount(off.taker_gets_funded) : taker_gets_total; + + const taker_pays_total = utils.parseCurrencyAmount(off.TakerPays); + const taker_pays_funded = off.taker_pays_funded ? + utils.parseCurrencyAmount(off.taker_pays_funded) : taker_pays_total; + + if (isAsk) { + price = { + currency: taker_pays_total.currency, + counterparty: taker_pays_total.counterparty, + value: bignum(taker_pays_total.value).div( + bignum(taker_gets_total.value)) + }; + } else { + price = { + currency: taker_gets_total.currency, + counterparty: taker_gets_total.counterparty, + value: bignum(taker_gets_total.value).div( + bignum(taker_pays_total.value)) + }; + } + + price.value = price.value.toString(); + + orderBook.push({ + price: price, + taker_gets_funded: taker_gets_funded, + taker_gets_total: taker_gets_total, + taker_pays_funded: taker_pays_funded, + taker_pays_total: taker_pays_total, + order_maker: order_maker, + sequence: sequence, + passive: passive, + sell: sell + }); + + return orderBook; + }, []); + } + + function respondWithOrderBook(bids, asks, parameters) { + const promise = new Promise(function(resolve) { + const orderBook = { + order_book: parameters.order_book, + ledger: parameters.ledger, + validated: parameters.validated, + bids: getParsedBookOffers(bids.offers), + asks: getParsedBookOffers(asks.offers, true) + }; + + resolve(callback(null, orderBook)); + }); + + return promise; + } + + getLastValidatedLedger(params) + .then(getBidsAndAsks) + .spread(respondWithOrderBook) + .catch(callback); +} + +module.exports = getOrderBook; diff --git a/src/api/ledger/orders.js b/src/api/ledger/orders.js index 3043d80009..7a2c749ee9 100644 --- a/src/api/ledger/orders.js +++ b/src/api/ledger/orders.js @@ -1,14 +1,9 @@ -/* globals Promise: true */ /* eslint-disable valid-jsdoc */ 'use strict'; const _ = require('lodash'); -const bignum = require('bignumber.js'); -const asyncify = require('simple-asyncify'); const utils = require('./utils'); const ripple = utils.common.core; -const errors = utils.common.errors; const validate = utils.common.validate; -const parseTransaction = require('./parse/transaction'); const DefaultPageLimit = 200; @@ -121,202 +116,4 @@ function getOrders(account, options, callback) { .catch(callback); } -/** - * Get the most recent spapshot of the order book for a currency pair - * - * @url - * @param {RippleAddress} request.params.account - * - The ripple address to use as point-of-view - * (returns unfunded orders for this account) - * @param {String ISO 4217 Currency Code + RippleAddress} request.params.base - * - Base currency as currency+issuer - * @param {String ISO 4217 Currency Code + RippleAddress} - * request.params.counter - Counter currency as currency+issuer - * - * @query - * @param {String} [request.query.limit] - * - Set a limit to the number of results returned - * - * @param {Express.js Request} request - */ -function getOrderBook(account, orderbook, options, callback) { - const self = this; - validate.address(account); - validate.orderbook(orderbook); - validate.options(options); - - const params = _.assign({}, orderbook, options, { - validated: true, - order_book: orderbook.base + '/' + orderbook.counter - }); - - function getLastValidatedLedger(parameters) { - const promise = new Promise(function(resolve, reject) { - const ledgerRequest = self.remote.requestLedger('validated'); - - ledgerRequest.once('success', function(res) { - parameters.ledger = res.ledger.ledger_index; - resolve(parameters); - }); - - ledgerRequest.once('error', reject); - ledgerRequest.request(); - }); - - return promise; - } - - function getBookOffers(taker_gets, taker_pays, parameters) { - const promise = new Promise(function(resolve, reject) { - const bookOffersRequest = self.remote.requestBookOffers({ - taker_gets: {currency: taker_gets.currency, - issuer: taker_gets.counterparty}, - taker_pays: {currency: taker_pays.currency, - issuer: taker_pays.counterparty}, - ledger: parameters.ledger, - limit: parameters.limit, - taker: account - }); - - bookOffersRequest.once('success', resolve); - bookOffersRequest.once('error', reject); - bookOffersRequest.request(); - }); - - return promise; - } - - function getBids(parameters) { - const taker_gets = parameters.counter; - const taker_pays = parameters.base; - - return getBookOffers(taker_gets, taker_pays, parameters); - } - - function getAsks(parameters) { - const taker_gets = parameters.base; - const taker_pays = parameters.counter; - - return getBookOffers(taker_gets, taker_pays, parameters); - } - - function getBidsAndAsks(parameters) { - return Promise.join( - getBids(parameters), - getAsks(parameters), - function(bids, asks) { - return [bids, asks, parameters]; - } - ); - } - - function getParsedBookOffers(offers, isAsk) { - return offers.reduce(function(orderBook, off) { - let price; - const order_maker = off.Account; - const sequence = off.Sequence; - - // Transaction Flags - const passive = (off.Flags & ripple.Remote.flags.offer.Passive) !== 0; - const sell = (off.Flags & ripple.Remote.flags.offer.Sell) !== 0; - - const taker_gets_total = utils.parseCurrencyAmount(off.TakerGets); - const taker_gets_funded = off.taker_gets_funded ? - utils.parseCurrencyAmount(off.taker_gets_funded) : taker_gets_total; - - const taker_pays_total = utils.parseCurrencyAmount(off.TakerPays); - const taker_pays_funded = off.taker_pays_funded ? - utils.parseCurrencyAmount(off.taker_pays_funded) : taker_pays_total; - - if (isAsk) { - price = { - currency: taker_pays_total.currency, - counterparty: taker_pays_total.counterparty, - value: bignum(taker_pays_total.value).div( - bignum(taker_gets_total.value)) - }; - } else { - price = { - currency: taker_gets_total.currency, - counterparty: taker_gets_total.counterparty, - value: bignum(taker_gets_total.value).div( - bignum(taker_pays_total.value)) - }; - } - - price.value = price.value.toString(); - - orderBook.push({ - price: price, - taker_gets_funded: taker_gets_funded, - taker_gets_total: taker_gets_total, - taker_pays_funded: taker_pays_funded, - taker_pays_total: taker_pays_total, - order_maker: order_maker, - sequence: sequence, - passive: passive, - sell: sell - }); - - return orderBook; - }, []); - } - - function respondWithOrderBook(bids, asks, parameters) { - const promise = new Promise(function(resolve) { - const orderBook = { - order_book: parameters.order_book, - ledger: parameters.ledger, - validated: parameters.validated, - bids: getParsedBookOffers(bids.offers), - asks: getParsedBookOffers(asks.offers, true) - }; - - resolve(callback(null, orderBook)); - }); - - return promise; - } - - getLastValidatedLedger(params) - .then(getBidsAndAsks) - .spread(respondWithOrderBook) - .catch(callback); -} - -/** - * Get an Order transaction (`OfferCreate` or `OfferCancel`) - * - * @url - * @param {RippleAddress} request.params.account - * @param {String} request.params.identifier - * - * @param {Express.js Request} request - */ -function getOrder(account, identifier, callback) { - validate.address(account); - validate.identifier(identifier); - - const txRequest = this.remote.requestTx({ - hash: identifier - }); - - txRequest.once('error', callback); - txRequest.once('transaction', function(tx) { - if (tx.TransactionType !== 'OfferCreate' - && tx.TransactionType !== 'OfferCancel') { - callback(new errors.InvalidRequestError('Invalid parameter: identifier. ' - + 'The transaction corresponding to the given identifier ' - + 'is not an order')); - } else { - asyncify(parseTransaction)(tx, callback); - } - }); - txRequest.request(); -} - -module.exports = { - getOrders: getOrders, - getOrderBook: getOrderBook, - getOrder: getOrder -}; +module.exports = getOrders; diff --git a/src/api/ledger/payments.js b/src/api/ledger/pathfind.js similarity index 61% rename from src/api/ledger/payments.js rename to src/api/ledger/pathfind.js index 377f9440b2..540ad4aa99 100644 --- a/src/api/ledger/payments.js +++ b/src/api/ledger/pathfind.js @@ -1,128 +1,14 @@ /* eslint-disable valid-jsdoc */ 'use strict'; - -const _ = require('lodash'); const async = require('async'); const asyncify = require('simple-asyncify'); const bignum = require('bignumber.js'); -const transactions = require('./transactions'); const utils = require('./utils'); const validate = utils.common.validate; -const parseTransaction = require('./parse/transaction'); const parsePathfind = require('./parse/pathfind'); - -const ValidationError = utils.common.errors.ValidationError; const NotFoundError = utils.common.errors.NotFoundError; const TimeOutError = utils.common.errors.TimeOutError; -const DEFAULT_RESULTS_PER_PAGE = 10; - -/** - * Formats a transaction into ripple-rest Payment format - * - * @param {RippleAddress} account - * @param {Transaction} transaction - * @param {Function} callback - * - * @callback - * @param {Error} error - * @param {RippleRestTransaction} transaction - */ -function formatPaymentHelper(account, txJSON) { - if (!(txJSON && /^payment$/i.test(txJSON.TransactionType))) { - throw new ValidationError('Not a payment. The transaction ' - + 'corresponding to the given identifier is not a payment.'); - } - return parseTransaction(txJSON); -} - -/** - * Retrieve the details of a particular payment from the Remote - * and return it in the ripple-rest Payment format. - * - * @param {Remote} remote - * @param {RippleAddress} req.params.account - * @param {Hex-encoded String|ASCII printable character String} - * req.params.identifier - */ -function getPayment(account, identifier, callback) { - const self = this; - - validate.address(account); - validate.identifier(identifier); - - function getTransaction(_callback) { - transactions.getTransaction(self, account, identifier, {}, _callback); - } - - const steps = [ - getTransaction, - asyncify(_.partial(formatPaymentHelper, account)) - ]; - - async.waterfall(steps, callback); -} - -/** - * Retrieve the details of multiple payments from the Remote - * - * This function calls transactions.getAccountTransactions - * recursively to retrieve results_per_page number of transactions - * and filters the results by type "payment", along with the other - * client-specified parameters. - * - * @param {Remote} remote - * @param {RippleAddress} req.params.account - * @param {RippleAddress} req.query.source_account - * @param {RippleAddress} req.query.destination_account - * @param {String "incoming"|"outgoing"} req.query.direction - * @param {Number} [-1] req.query.start_ledger - * @param {Number} [-1] req.query.end_ledger - * @param {Boolean} [false] req.query.earliest_first - * @param {Boolean} [false] req.query.exclude_failed - * @param {Number} [20] req.query.results_per_page - * @param {Number} [1] req.query.page - */ -function getAccountPayments(account, source_account, destination_account, - direction, options, callback) { - const self = this; - - function getTransactions(_callback) { - const args = { - account: account, - source_account: source_account, - destination_account: destination_account, - direction: direction, - min: options.results_per_page, - max: options.results_per_page, - offset: (options.results_per_page || DEFAULT_RESULTS_PER_PAGE) - * ((options.page || 1) - 1), - types: ['payment'], - earliestFirst: options.earliest_first - }; - - transactions.getAccountTransactions(self, - _.merge(options, args), _callback); - } - - function formatTransactions(_transactions) { - return _transactions.map(_.partial(formatPaymentHelper, account)); - } - - function formatResponse(_transactions) { - return {payments: _transactions}; - } - - const steps = [ - getTransactions, - _.partial(utils.attachDate, self), - asyncify(formatTransactions), - asyncify(formatResponse) - ]; - - async.waterfall(steps, callback); -} - /** * Get a ripple path find, a.k.a. payment options, * for a given set of parameters and respond to the @@ -258,8 +144,4 @@ function getPathFind(pathfind, callback) { async.waterfall(steps, callback); } -module.exports = { - getPayment: getPayment, - getAccountPayments: getAccountPayments, - getPathFind: getPathFind -}; +module.exports = getPathFind; diff --git a/src/api/ledger/settings.js b/src/api/ledger/settings.js index 71f5934b14..4efc888a69 100644 --- a/src/api/ledger/settings.js +++ b/src/api/ledger/settings.js @@ -41,6 +41,4 @@ function getSettings(account, callback) { }); } -module.exports = { - getSettings: getSettings -}; +module.exports = getSettings; diff --git a/src/api/ledger/transaction.js b/src/api/ledger/transaction.js new file mode 100644 index 0000000000..ce85606533 --- /dev/null +++ b/src/api/ledger/transaction.js @@ -0,0 +1,74 @@ +'use strict'; +const _ = require('lodash'); +const async = require('async'); +const utils = require('./utils'); +const parseTransaction = require('./parse/transaction'); +const validate = utils.common.validate; +const errors = utils.common.errors; +const MIN_LEDGER_VERSION = 32570; // earlier versions have been completely lost + +function hasCompleteLedgerRange(remote, options) { + const minLedgerVersion = options.minLedgerVersion || MIN_LEDGER_VERSION; + const maxLedgerVersion = options.maxLedgerVersion + || remote.getLedgerSequence(); + for (let i = minLedgerVersion; i <= maxLedgerVersion; i++) { + if (!remote.getServer().hasLedger(i)) { // TODO: optimize this + return false; + } + } + return true; +} + +function attachTransactionDate(remote, tx, callback) { + if (tx.date) { + callback(null, tx); + return; + } + if (!tx.ledger_index) { + callback(new errors.ApiError('ledger_index not found in tx')); + return; + } + + remote.requestLedger(tx.ledger_index, (error, data) => { + if (error) { + callback(new errors.NotFoundError('Transaction ledger not found')); + } else if (typeof data.ledger.close_time === 'number') { + callback(null, _.assign({date: data.ledger.close_time, tx})); + } else { + callback(new errors.ApiError('Ledger missing close_time')); + } + }); +} + +function isTransactionInRange(tx, options) { + return (!options.minLedgerVersion + || tx.ledger_index >= options.minLedgerVersion) + && (!options.maxLedgerVersion + || tx.ledger_index <= options.maxLedgerVersion); +} + +function getTransaction(identifier, options, callback) { + validate.identifier(identifier); + validate.options(options); + + const remote = this.remote; + + function callbackWrapper(error, tx) { + if (error instanceof errors.NotFoundError + && !hasCompleteLedgerRange(remote, options)) { + callback(new errors.MissingLedgerHistoryError('Transaction not found,' + + ' but the server\'s ledger history is incomplete')); + } else if (!error && !isTransactionInRange(tx, options)) { + callback(new errors.NotFoundError('Transaction not found')); + } else { + callback(error, parseTransaction(tx)); + } + } + + async.waterfall([ + _.partial(remote.requestTx.bind(remote), {hash: identifier}), + _.partial(attachTransactionDate, remote) + ], callbackWrapper); +} + +module.exports = utils.wrapCatch(getTransaction); diff --git a/src/api/ledger/transactions.js b/src/api/ledger/transactions.js index 07f05a1ed1..c972a6ce4e 100644 --- a/src/api/ledger/transactions.js +++ b/src/api/ledger/transactions.js @@ -1,79 +1,11 @@ -/* eslint-disable valid-jsdoc */ 'use strict'; const _ = require('lodash'); -const async = require('async'); const utils = require('./utils'); const parseTransaction = require('./parse/transaction'); +const getTransaction = require('./transaction'); const validate = utils.common.validate; -const errors = utils.common.errors; const composeAsync = utils.common.composeAsync; - const DEFAULT_LIMIT = 100; -const MIN_LEDGER_VERSION = 32570; // earlier versions have been completely lost - -function hasCompleteLedgerRange(remote, options) { - const minLedgerVersion = options.minLedgerVersion || MIN_LEDGER_VERSION; - const maxLedgerVersion = options.maxLedgerVersion - || remote.getLedgerSequence(); - for (let i = minLedgerVersion; i <= maxLedgerVersion; i++) { - if (!remote.getServer().hasLedger(i)) { // TODO: optimize this - return false; - } - } - return true; -} - -function attachTransactionDate(remote, tx, callback) { - if (tx.date) { - callback(null, tx); - return; - } - if (!tx.ledger_index) { - callback(new errors.ApiError('ledger_index not found in tx')); - return; - } - - remote.requestLedger(tx.ledger_index, (error, data) => { - if (error) { - callback(new errors.NotFoundError('Transaction ledger not found')); - } else if (typeof data.ledger.close_time === 'number') { - callback(null, _.assign({date: data.ledger.close_time, tx})); - } else { - callback(new errors.ApiError('Ledger missing close_time')); - } - }); -} - -function isTransactionInRange(tx, options) { - return (!options.minLedgerVersion - || tx.ledger_index >= options.minLedgerVersion) - && (!options.maxLedgerVersion - || tx.ledger_index <= options.maxLedgerVersion); -} - -function getTransaction(identifier, options, callback) { - validate.identifier(identifier); - validate.options(options); - - const remote = this.remote; - - function callbackWrapper(error, tx) { - if (error instanceof errors.NotFoundError - && !hasCompleteLedgerRange(remote, options)) { - callback(new errors.MissingLedgerHistoryError('Transaction not found,' - + ' but the server\'s ledger history is incomplete')); - } else if (!error && !isTransactionInRange(tx, options)) { - callback(new errors.NotFoundError('Transaction not found')); - } else { - callback(error, parseTransaction(tx)); - } - } - - async.waterfall([ - _.partial(remote.requestTx.bind(remote), {hash: identifier}), - _.partial(attachTransactionDate, remote) - ], callbackWrapper); -} function parseAccountTxTransaction(tx) { // rippled uses a different response format for 'account_tx' than 'tx' @@ -158,7 +90,4 @@ function getAccountTransactions(address, options, callback) { } } -module.exports = { - getTransaction: utils.wrapCatch(getTransaction), - getAccountTransactions: utils.wrapCatch(getAccountTransactions) -}; +module.exports = utils.wrapCatch(getAccountTransactions); diff --git a/src/api/ledger/trustlines.js b/src/api/ledger/trustlines.js index 9e68d02536..97e4595678 100644 --- a/src/api/ledger/trustlines.js +++ b/src/api/ledger/trustlines.js @@ -45,4 +45,4 @@ function getTrustlines(account: string, options: Options, utils.getRecursive(getter, limit, callback); } -module.exports.getTrustlines = getTrustlines; +module.exports = getTrustlines; diff --git a/src/index.js b/src/index.js index 5af22e5470..cb12f30189 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ 'use strict'; const _ = require('lodash'); const core = require('./core'); -const RippleAPI = require('./api/api'); +const RippleAPI = require('./api'); module.exports = _.assign({}, core, {RippleAPI: RippleAPI}); diff --git a/test/api-test.js b/test/api-test.js index 0888c335db..528164c18a 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -25,6 +25,8 @@ const transactionResponse = require('./fixtures/transaction-response'); const accountTransactionsResponse = require('./fixtures/account-transactions-response'); const trustlinesResponse = require('./fixtures/trustlines-response'); +const walletResponse = require('./fixtures/wallet.json'); +const getSettingsResponse = require('./fixtures/get-settings-response'); function checkResult(expected, done, error, response) { if (error) { @@ -117,4 +119,16 @@ describe('RippleAPI', function() { this.api.getTrustlines(address, options, _.partial(checkResult, trustlinesResponse, done)); }); + + it('generateWallet', function() { + withDeterministicPRNG(() => { + assert.deepEqual(this.api.generateWallet(), walletResponse); + }); + }); + + it('getSettings', function(done) { + this.api.getSettings(address, + _.partial(checkResult, getSettingsResponse, done)); + }); + }); diff --git a/test/fixtures/get-settings-response.json b/test/fixtures/get-settings-response.json new file mode 100644 index 0000000000..dad280e5f8 --- /dev/null +++ b/test/fixtures/get-settings-response.json @@ -0,0 +1,9 @@ +{ + "requireDestinationTag": true, + "disallowIncomingXRP": true, + "sequence": 23, + "emailHash": "23463B99B62A72F26ED677CC556C44E8", + "walletLocator": "00000000000000000000000000000000000000000000000000000000DEADBEEF", + "domain": "example.com", + "transferRate": 1002000000 +} diff --git a/test/fixtures/wallet.json b/test/fixtures/wallet.json new file mode 100644 index 0000000000..ba97f559ba --- /dev/null +++ b/test/fixtures/wallet.json @@ -0,0 +1,4 @@ +{ + "address": "ra2d1wnhDFyZGFz62wT3GSRhAEjpLuqfnj", + "secret": "ss4JedFMSQJa4mHQz3VZ58ZG4TYWg" +} diff --git a/test/mock-prng.js b/test/mock-prng.js index 2f9cf36213..2d09cab496 100644 --- a/test/mock-prng.js +++ b/test/mock-prng.js @@ -12,6 +12,10 @@ function MockPRNG(seed) { this.seed = seed || SEED; } +/* eslint-disable no-unused-vars */ +MockPRNG.prototype.addEntropy = function(data, estimatedEntropy, source) {}; +/* eslint-enable no-unused-vars */ + MockPRNG.prototype.randomWord = function() { const i = this.position; this.position = (i + 8) % this.seed.length;