UNPKG

espend-rest

Version:

A RESTful API for submitting payments and monitoring accounts on the eSpend network.

257 lines (219 loc) 9.24 kB
var _ = require('lodash'); var Promise = require('bluebird'); var async = require('async'); var ripple = require('espend-lib'); var transactions = require('./transactions.js'); var SubmitTransactionHooks = require('./lib/submit_transaction_hooks.js'); var remote = require('./lib/remote.js'); var respond = require('../server/response-handler.js'); var utils = require('./lib/utils'); var errors = require('./lib/errors.js'); var validator = require('./lib/schema-validator'); var TxToRestConverter = require('./lib/tx-to-rest-converter.js'); const TrustSetFlags = { SetAuth: { name: 'authorized', set: 'SetAuth' }, ClearNoRipple: { name: 'account_allows_rippling', set: 'ClearNoRipple', unset: 'NoRipple' }, SetFreeze: { name: 'account_trustline_frozen', set: 'SetFreeze', unset: 'ClearFreeze' } }; const DefaultPageLimit = 200; /** * Retrieves all trustlines for a given account * * Notes: * In order to use paging, you must provide at least ledger as a query parameter. * Additionally, any limit lower than 10 will be bumped up to 10. * * @url * @param {String} request.params.account - account to retrieve trustlines for * * @query * @param {String ISO 4217 Currency Code} [request.query.currency] - only request trustlines with given currency * @param {RippleAddress} [request.query.counterparty] - only request trustlines with given counterparty * @param {String} [request.query.marker] - start position in response paging * @param {Number String} [request.query.limit] - max results per response * @param {Number String} [request.query.ledger] - identifier * * @param {Express.js Response} response * @param {Express.js Next} next */ function getTrustLines(request, response, next) { var options = request.params; options.isAggregate = request.param('limit') === 'all'; Object.keys(request.query).forEach(function(param) { options[param] = request.query[param]; }); validateOptions(options) .then(getAccountLines) .then(respondWithTrustlines) .catch(next); var currencyRE = new RegExp(options.currency ? ('^' + options.currency.toUpperCase() + '$') : /./); function validateOptions(options) { if (!ripple.UInt160.is_valid(options.account)) { return Promise.reject(new errors.InvalidRequestError('Parameter is not a valid Ripple address: account')); } if (options.counterparty && !ripple.UInt160.is_valid(options.counterparty)) { return Promise.reject(new errors.InvalidRequestError('Parameter is not a valid Ripple address: counterparty')); } if (options.currency && !validator.isValid(options.currency, 'Currency')) { return Promise.reject(new errors.InvalidRequestError('Parameter is not a valid currency: currency')); } return Promise.resolve(options); }; function getAccountLines(options, prevResult) { if (prevResult && (!options.isAggregate || !prevResult.marker)) { return Promise.resolve(prevResult); } var promise = new Promise(function(resolve, reject) { var accountLinesRequest; var marker; var ledger; var limit; if (prevResult) { marker = prevResult.marker; limit = prevResult.limit; ledger = prevResult.ledger_index; } else { marker = request.query.marker; limit = validator.isValid(request.query.limit, 'UINT32') ? Number(request.query.limit) : DefaultPageLimit; ledger = utils.parseLedger(request.query.ledger); } accountLinesRequest = remote.requestAccountLines({ account: options.account, marker: marker, limit: limit, ledger: ledger }); if (options.counterparty) { accountLinesRequest.message.peer = options.counterparty; } accountLinesRequest.once('error', reject); accountLinesRequest.once('success', function(nextResult) { var lines = [ ]; nextResult.lines.forEach(function(line) { if (!currencyRE.test(line.currency)) return; lines.push({ account: options.account, counterparty: line.account, currency: line.currency, limit: line.limit, reciprocated_limit: line.limit_peer, account_allows_rippling: line.no_ripple ? !line.no_ripple : true, counterparty_allows_rippling: line.no_ripple_peer ? !line.no_ripple_peer : true, account_trustline_frozen: line.freeze ? line.freeze : false, counterparty_trustline_frozen: line.freeze_peer ? line.freeze_peer : false }); }); nextResult.lines = prevResult ? prevResult.lines.concat(lines) : lines; resolve([options, nextResult]); }); accountLinesRequest.request(); }); return promise.spread(getAccountLines); } function respondWithTrustlines(result) { var promise = new Promise(function (resolve, reject) { var trustlines = {}; if (result.marker) { trustlines.marker = result.marker; } trustlines.limit = result.limit; trustlines.ledger = result.ledger_index; trustlines.validated = result.validated; trustlines.trustlines = result.lines; resolve(respond.success(response, trustlines)); }); return promise; } }; /** * Grant a trustline to a counterparty * * @body * @param {Trustline} request.body.trustline * @param {String} request.body.secret * * @query * @param {String "true"|"false"} request.query.validated Used to force request to wait until rippled has finished validating the submitted transaction * * @param {Express.js Response} response * @param {Express.js Next} next */ function addTrustLine(request, response, next) { var params = request.params; Object.keys(request.body).forEach(function(param) { params[param] = request.body[param]; }); var options = { secret: params.secret, validated: request.query.validated === 'true' }; var hooks = { validateParams: validateParams, formatTransactionResponse: TxToRestConverter.parseTrustResponseFromTx, setTransactionParameters: setTransactionParameters }; transactions.submit(options, new SubmitTransactionHooks(hooks), function(err, trustline) { if (err) { return next(err); } respond.created(response, trustline); }); function validateParams(callback) { if (!ripple.UInt160.is_valid(params.account)) { return callback(new errors.InvalidRequestError('Parameter is not a valid Ripple address: account')); } if (typeof params.trustline !== 'object') { return callback(new errors.InvalidRequestError('Parameter missing: trustline')); } if (_.isUndefined(params.trustline.limit)) { return callback(new errors.InvalidRequestError('Parameter missing: trustline.limit')); } if (isNaN(params.trustline.limit = String(params.trustline.limit))) { return callback(new errors.InvalidRequestError('Parameter is not a number: trustline.limit')); } if (!params.trustline.currency) { return callback(new errors.InvalidRequestError('Parameter missing: trustline.currency')); } if (!validator.isValid(params.trustline.currency, 'Currency')) { return callback(new errors.InvalidRequestError('Parameter is not a valid currency: trustline.currency')); } if (!params.trustline.counterparty) { return callback(new errors.InvalidRequestError('Parameter missing: trustline.counterparty')); } if (!ripple.UInt160.is_valid(params.trustline.counterparty)) { return callback(new errors.InvalidRequestError('Parameter is not a Ripple address: trustline.counterparty')); } if (!/^(undefined|number)$/.test(typeof params.trustline.quality_in)) { return callback(new errors.InvalidRequestError('Parameter must be a number: trustline.quality_in')); } if (!/^(undefined|number)$/.test(typeof params.trustline.quality_out)) { return callback(new errors.InvalidRequestError('Parameter must be a number: trustline.quality_out')); } if (!/^(undefined|boolean)$/.test(typeof params.trustline.account_allows_rippling)) { return callback(new errors.InvalidRequestError('Parameter must be a boolean: trustline.allow_rippling')); } callback(); }; function setTransactionParameters(transaction) { var limit = [ params.trustline.limit, params.trustline.currency, params.trustline.counterparty ].join('/'); transaction.trustSet(params.account, limit); transaction.secret(params.secret); if (typeof params.trustline.quality_in === 'number') { transaction.tx_json.QualityIn = params.trustline.quality_in; } if (typeof params.trustline.quality_out === 'number') { transaction.tx_json.QualityOut = params.trustline.quality_out; } transactions.setTransactionBitFlags(transaction, { input: params.trustline, flags: TrustSetFlags, clear_setting: '' }); }; }; module.exports.get = getTrustLines; module.exports.add = addTrustLine;