Skip to content
This repository has been archived by the owner on Sep 2, 2023. It is now read-only.

Commit

Permalink
Add prepare and sign functionality to existing REST endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Clark committed Apr 20, 2015
1 parent cc16a1a commit 37508bb
Show file tree
Hide file tree
Showing 28 changed files with 320 additions and 182 deletions.
4 changes: 1 addition & 3 deletions api/balances.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ function getBalances(account, options, callback) {
validate.address(account);
validate.currency(options.currency, true);
validate.counterparty(options.counterparty, true);
validate.ledger(options.ledger, true);
validate.limit(options.limit, true);
validate.paging(options, true);
validate.options(options);

var self = this;

Expand Down
78 changes: 67 additions & 11 deletions api/lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ function validateAddressAndSecret(obj) {
}
}

function validateAddressAndMaybeSecret(obj) {
if (obj.secret === undefined) {
validateAddress(obj.address);
} else {
validateAddressAndSecret(obj);
}
}

function validateCurrency(currency) {
if (!validator.isValid(currency, 'Currency')) {
throw error('Parameter is not a valid currency: currency');
Expand Down Expand Up @@ -451,12 +459,6 @@ function validateTrustline(trustline) {
// TODO: validateSchema(trustline, 'Trustline');
}

function validateValidated(validated) {
if (!isBoolean(validated)) {
throw error('validated must be boolean, not: ' + validated);
}
}

function validateTxJSON(txJSON) {
if (typeof txJSON !== 'object') {
throw error('tx_json must be an object, not: ' + typeof txJSON);
Expand All @@ -479,6 +481,62 @@ function validateBlob(blob) {
}
}

function isNumeric(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
}

function validateNonNegativeStringFloat(value, name) {
if (typeof value !== 'string') {
throw error(name + ' must be a string, not: ' + typeof value);
}
if (!isNumeric(value)) {
throw error(name + ' must be a numeric string, not: ' + value);
}
if (parseFloat(value) < 0) {
throw error(name + ' must be non-negative, got: ' + parseFloat(value));
}
}

function validateNonNegativeStringInteger(value, name) {
validateNonNegativeStringFloat(value, name);
if (value.indexOf('.') !== -1) {
throw error(name + ' must be an integer, got: ' + value);
}
}

function validateOptions(options) {
if (options.max_fee !== undefined) {
validateNonNegativeStringFloat(options.max_fee, 'max_fee');
}
if (options.fixed_fee !== undefined) {
validateNonNegativeStringFloat(options.fixed_fee, 'fixed_fee');
}
if (options.last_ledger_sequence !== undefined) {
validateNonNegativeStringInteger(options.last_ledger_sequence,
'last_ledger_sequence');
}
if (options.last_ledger_offset !== undefined) {
validateNonNegativeStringInteger(options.last_ledger_offset,
'last_ledger_offset');
}
if (options.sequence !== undefined) {
validateNonNegativeStringInteger(options.sequence, 'sequence');
}
if (options.limit !== undefined) {
validateLimit(options.limit);
}
if (options.ledger !== undefined) {
validateLedger(options.ledger);
}
if (options.validated !== undefined && !isBoolean(options.validated)) {
throw error('validated must be boolean, not: ' + options.validated);
}
if (options.submit !== undefined && !isBoolean(options.submit)) {
throw error('submit must be boolean, not: ' + options.submit);
}
validatePaging(options);
}

function createValidators(validatorMap) {
var result = {};
_.forEach(validatorMap, function(validateFunction, key) {
Expand All @@ -498,12 +556,10 @@ function createValidators(validatorMap) {
module.exports = createValidators({
address: validateAddress,
addressAndSecret: validateAddressAndSecret,
addressAndMaybeSecret: validateAddressAndMaybeSecret,
currency: validateCurrency,
counterparty: validateCounterparty,
issue: validateIssue,
ledger: validateLedger,
limit: validateLimit,
paging: validatePaging,
identifier: validateIdentifier,
paymentIdentifier: validatePaymentIdentifier,
sequence: validateSequence,
Expand All @@ -515,7 +571,7 @@ module.exports = createValidators({
pathfind: validatePathFind,
settings: validateSettings,
trustline: validateTrustline,
validated: validateValidated,
txJSON: validateTxJSON,
blob: validateBlob
blob: validateBlob,
options: validateOptions
});
28 changes: 7 additions & 21 deletions api/orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
/* eslint-disable valid-jsdoc */
'use strict';
var _ = require('lodash');
var async = require('async');
var Promise = require('bluebird');
var ripple = require('ripple-lib');
var transactions = require('./transactions');
var utils = require('./lib/utils');
var errors = require('./lib/errors.js');
var TxToRestConverter = require('./lib/tx-to-rest-converter.js');
Expand All @@ -15,6 +13,7 @@ var validate = require('./lib/validate');
var createOrderTransaction = require('./transaction').createOrderTransaction;
var createOrderCancellationTransaction =
require('./transaction').createOrderCancellationTransaction;
var transact = require('./transact');

var InvalidRequestError = errors.InvalidRequestError;

Expand All @@ -41,9 +40,7 @@ function getOrders(account, options, callback) {
var self = this;

validate.address(account);
validate.ledger(options.ledger, true);
validate.limit(options.limit, true);
validate.paging(options, true);
validate.options(options);

function getAccountOrders(prevResult) {
var isAggregate = options.limit === 'all';
Expand Down Expand Up @@ -162,15 +159,9 @@ function getOrders(account, options, callback) {
* - validating the submitted transaction
*/
function placeOrder(account, order, secret, options, callback) {
validate.addressAndSecret({address: account, secret: secret});
validate.order(order);
validate.validated(options.validated, true);

var transaction = createOrderTransaction(account, order);
async.waterfall([
_.partial(transactions.submit, this, transaction, secret, options),
TxToRestConverter.parseSubmitOrderFromTx
], callback);
var converter = TxToRestConverter.parseSubmitOrderFromTx;
transact(transaction, this, secret, options, converter, callback);
}

/**
Expand All @@ -186,14 +177,9 @@ function placeOrder(account, order, secret, options, callback) {
* validating the submitted transaction
*/
function cancelOrder(account, sequence, secret, options, callback) {
validate.sequence(sequence);
validate.address(account);

var transaction = createOrderCancellationTransaction(account, sequence);
async.waterfall([
_.partial(transactions.submit, this, transaction, secret, options),
TxToRestConverter.parseCancelOrderFromTx
], callback);
var converter = TxToRestConverter.parseCancelOrderFromTx;
transact(transaction, this, secret, options, converter, callback);
}

/**
Expand Down Expand Up @@ -225,7 +211,7 @@ function getOrderBook(account, base, counter, options, callback) {
});
validate.address(account);
validate.orderbook(params);
validate.validated(options.validated, true);
validate.options(options);

function getLastValidatedLedger(parameters) {
var promise = new Promise(function(resolve, reject) {
Expand Down
35 changes: 23 additions & 12 deletions api/payments.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var createPaymentTransaction =
var renameCounterpartyToIssuer =
require('./transaction/utils').renameCounterpartyToIssuer;
var xrpToDrops = require('./transaction/utils').xrpToDrops;
var transact = require('./transact');

var errors = require('./lib/errors');
var InvalidRequestError = errors.InvalidRequestError;
Expand Down Expand Up @@ -122,19 +123,19 @@ function formatPaymentHelper(account, transaction, callback) {
* submitted transaction
*/
function submitPayment(account, payment, clientResourceID, secret,
lastLedgerSequence, urlBase, options, callback) {
urlBase, options, callback) {
var self = this;
var max_fee = Number(options.max_fee) > 0 ?
xrpToDrops(options.max_fee) : undefined;
var fixed_fee = Number(options.fixed_fee) > 0 ?
xrpToDrops(options.fixed_fee) : undefined;

validate.client_resource_id(clientResourceID);
// TODO: validate.addressAndSecret({address: account, secret: secret});
validate.address(account);
// these checks are done at the top of the function because we cannot
// raise exceptions inside callbacks
validate.addressAndMaybeSecret({address: account, secret: secret});
validate.payment(payment);
validate.last_ledger_sequence(lastLedgerSequence, true);
validate.validated(options.validated, true);
validate.client_resource_id(clientResourceID);
validate.options(options);

function formatTransactionResponse(message, meta, _callback) {
if (meta.state === 'validated') {
Expand All @@ -155,14 +156,20 @@ function submitPayment(account, payment, clientResourceID, secret,
}

function _createPaymentTransaction(remote, _callback) {
var transaction = createPaymentTransaction(payment);
var transaction;
try {
transaction = createPaymentTransaction(account, payment);
} catch (err) {
_callback(err);
return;
}

var ledgerIndex;
var maxFee = Number(max_fee);
var fixedFee = Number(fixed_fee);

if (Number(lastLedgerSequence) > 0) {
ledgerIndex = Number(lastLedgerSequence);
if (Number(options.last_ledger_sequence) > 0) {
ledgerIndex = Number(options.last_ledger_sequence);
} else {
ledgerIndex = Number(remote._ledger_current_index)
+ transactions.DEFAULT_LEDGER_BUFFER;
Expand All @@ -189,12 +196,16 @@ function submitPayment(account, payment, clientResourceID, secret,
saveTransaction: true
};

var converter = formatTransactionResponse;
async.waterfall([
_.partial(_createPaymentTransaction, self.remote),
function(transaction, _callback) {
transactions.submit(self, transaction, secret, _options, _callback);
},
formatTransactionResponse
try { // for case of missing secret and submit=false not set
transact(transaction, self, secret, _options, converter, _callback);
} catch (err) {
_callback(err);
}
}
], callback);
}

Expand Down
13 changes: 4 additions & 9 deletions api/settings.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/* eslint-disable valid-jsdoc */
'use strict';
var _ = require('lodash');
var async = require('async');
var utils = require('./lib/utils');
var transactions = require('./transactions.js');
var TxToRestConverter = require('./lib/tx-to-rest-converter.js');
var validate = require('./lib/validate');
var createSettingsTransaction =
require('./transaction').createSettingsTransaction;
var constants = require('./lib/constants');
var transact = require('./transact');

function parseFieldsFromResponse(responseBody, fields) {
var parsedBody = {};
Expand Down Expand Up @@ -73,14 +72,10 @@ function getSettings(account, callback) {
*
*/
function changeSettings(account, settings, secret, options, callback) {
validate.address(account);
validate.settings(settings);

var transaction = createSettingsTransaction(account, settings);
async.waterfall([
_.partial(transactions.submit, this, transaction, secret, options),
_.partial(TxToRestConverter.parseSettingsResponseFromTx, settings)
], callback);
var converter = _.partial(TxToRestConverter.parseSettingsResponseFromTx,
settings);
transact(transaction, this, secret, options, converter, callback);
}

module.exports = {
Expand Down
56 changes: 56 additions & 0 deletions api/transact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';
var _ = require('lodash');
var async = require('async');
var sign = require('./transaction/sign');
var createTxJSON = require('./transaction/utils').createTxJSON;
var transactions = require('./transactions');
var validate = require('./lib/validate');

function signIfSecretExists(secret, prepared, callback) {
var signResponse = secret ? sign(prepared.tx_json, secret) : {};
callback(null, _.assign(prepared, signResponse));
}

function formatResponse(converter, prepared, callback) {
async.waterfall([
_.partial(converter, prepared, {}),
function(formatted, _callback) {
_callback(null, _.assign(formatted, prepared));
}
], callback);
}

function prepareAndOptionallySign(transaction, api, secret, options,
converter, callback) {
var address = transaction.tx_json.Account;
validate.addressAndMaybeSecret({address: address, secret: secret});
validate.options(options);
async.waterfall([
_.partial(createTxJSON, transaction, api.remote, options),
_.partial(signIfSecretExists, secret),
_.partial(formatResponse, converter)
], callback);
}

function prepareAndSignAndSubmit(transaction, api, secret, options, converter,
callback) {
var address = transaction.tx_json.Account;
validate.addressAndSecret({address: address, secret: secret});
validate.options(options);
async.waterfall([
_.partial(transactions.submit, api, transaction, secret, options),
converter
], callback);
}

/* eslint-disable no-unused-vars */
function transact(transaction, api, secret, options, converter, callback) {
if (options.submit === false) {
prepareAndOptionallySign.apply(this, arguments);
} else {
prepareAndSignAndSubmit.apply(this, arguments);
}
}
/* eslint-enable no-unused-vars */

module.exports = transact;
13 changes: 5 additions & 8 deletions api/transaction/order.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ var OfferCreateFlags = {
};

function createOrderTransaction(account, order) {
if (order) {
utils.renameCounterpartyToIssuer(order.taker_gets);
utils.renameCounterpartyToIssuer(order.taker_pays);
}
validate.address(account);
validate.order(order);

utils.renameCounterpartyToIssuer(order.taker_gets);
utils.renameCounterpartyToIssuer(order.taker_pays);
var transaction = new ripple.Transaction();
var takerPays = order.taker_pays.currency !== 'XRP'
? order.taker_pays : utils.xrpToDrops(order.taker_pays.value);
Expand All @@ -36,10 +37,6 @@ function createOrderTransaction(account, order) {
}

function prepareOrder(account, order, instructions, callback) {
instructions = instructions || {};
validate.address(account);
validate.order(order);

var transaction = createOrderTransaction(account, order);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
Expand Down
Loading

0 comments on commit 37508bb

Please sign in to comment.