Cleanup RippleAPI

This commit is contained in:
Chris Clark
2015-07-15 11:02:46 -07:00
parent aa646a3acf
commit 1fb1bc7404
19 changed files with 85 additions and 155 deletions

View File

@@ -10,5 +10,6 @@ module.exports = {
xrpToDrops: utils.xrpToDrops, xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount, toRippledAmount: utils.toRippledAmount,
wrapCatch: utils.wrapCatch, wrapCatch: utils.wrapCatch,
composeAsync: utils.composeAsync composeAsync: utils.composeAsync,
convertExceptions: utils.convertExceptions
}; };

View File

@@ -3,17 +3,17 @@ const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const validator = require('is-my-json-valid'); const validator = require('is-my-json-valid');
const ripple = require('./utils').core; const utils = require('./utils');
const ValidationError = require('./errors').ValidationError; const ValidationError = require('./errors').ValidationError;
let SCHEMAS = {}; let SCHEMAS = {};
function isValidAddress(address) { function isValidAddress(address) {
return ripple.UInt160.is_valid(address); return utils.core.UInt160.is_valid(address);
} }
function isValidLedgerHash(ledgerHash) { function isValidLedgerHash(ledgerHash) {
return ripple.UInt256.is_valid(ledgerHash); return utils.core.UInt256.is_valid(ledgerHash);
} }
function loadSchema(filepath) { function loadSchema(filepath) {

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "hash256",
"description": "The hex representation of a 128-bit hash",
"type": "string",
"pattern": "^[A-F0-9]{32}$"
}

View File

@@ -3,5 +3,5 @@
"title": "hash256", "title": "hash256",
"description": "The hex representation of a 256-bit hash", "description": "The hex representation of a 256-bit hash",
"type": "string", "type": "string",
"pattern": "^[A-Fa-f0-9]{64}$" "pattern": "^[A-F0-9]{64}$"
} }

View File

@@ -5,6 +5,7 @@
"properties": { "properties": {
"source": {"$ref": "adjustment"}, "source": {"$ref": "adjustment"},
"destination": {"$ref": "adjustment"}, "destination": {"$ref": "adjustment"},
"paths": {"type": "string"},
"slippage": { "slippage": {
"description": "An optional cushion for the source_amount to increase the likelihood that the payment will succeed. The source_account will never be charged more than source_amount.value + source_slippage", "description": "An optional cushion for the source_amount to increase the likelihood that the payment will succeed. The source_account will never be charged more than source_amount.value + source_slippage",
"$ref": "value" "$ref": "value"

View File

@@ -12,7 +12,7 @@
"noFreeze": {"type": "boolean"}, "noFreeze": {"type": "boolean"},
"globalFreeze": {"type": "boolean"}, "globalFreeze": {"type": "boolean"},
"defaultRipple": {"type": "boolean"}, "defaultRipple": {"type": "boolean"},
"emailHash": {"type": "string"}, "emailHash": {"$ref": "hash128"},
"walletLocator": {"$ref": "hash256"}, "walletLocator": {"$ref": "hash256"},
"walletSize": {"type": "integer"}, "walletSize": {"type": "integer"},
"messageKey": {"type": "string"}, "messageKey": {"type": "string"},

View File

@@ -1,7 +1,8 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"title": "uint32", "title": "uint32",
"description": "A string representation of an unsigned 32-bit integer (0-4294967295)", "description": "A 32-bit unsigned integer",
"type": "string", "type": "integer",
"pattern": "^(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{8}|[1-9][0-9]{7}|[1-9][0-9]{6}|[1-9][0-9]{5}|[1-9][0-9]{4}|[1-9][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])$" "minimum": 0,
"maximum": 4294967295
} }

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const core = require('../../core'); const core = require('../../core');
const errors = require('./errors');
function dropsToXrp(drops) { function dropsToXrp(drops) {
return (new BigNumber(drops)).dividedBy(1000000.0).toString(); return (new BigNumber(drops)).dividedBy(1000000.0).toString();
@@ -49,11 +50,22 @@ function composeAsync(wrapper, callback) {
}; };
} }
function convertExceptions(f) {
return function() {
try {
return f.apply(this, arguments);
} catch (error) {
throw new errors.ApiError(error.message);
}
};
}
module.exports = { module.exports = {
core, core,
dropsToXrp, dropsToXrp,
xrpToDrops, xrpToDrops,
toRippledAmount, toRippledAmount,
wrapCatch, wrapCatch,
composeAsync composeAsync,
convertExceptions
}; };

View File

@@ -1,8 +1,8 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils');
const ValidationError = require('./errors').ValidationError; const ValidationError = require('./errors').ValidationError;
const schemaValidate = require('./schema-validator'); const schemaValidate = require('./schema-validator');
const ripple = require('./utils').core;
function error(text) { function error(text) {
return new ValidationError(text); return new ValidationError(text);
@@ -16,7 +16,7 @@ function validateAddressAndSecret(obj) {
throw error('Parameter missing: secret'); throw error('Parameter missing: secret');
} }
try { try {
if (!ripple.Seed.from_json(secret).get_key(address)) { if (!utils.core.Seed.from_json(secret).get_key(address)) {
throw error('secret does not match address'); throw error('secret does not match address');
} }
} catch (exception) { } catch (exception) {

View File

@@ -1,12 +0,0 @@
'use strict';
const common = require('../common');
function generateWallet() {
const wallet = common.core.Wallet.generate();
if (!wallet) {
throw new common.errors.ApiError('Could not generate wallet');
}
return wallet;
}
module.exports = generateWallet;

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const ripple = require('./common').core; const core = require('./common').core;
const server = require('./server/server'); const server = require('./server/server');
const connect = server.connect; const connect = server.connect;
const disconnect = server.disconnect; const disconnect = server.disconnect;
@@ -23,12 +23,13 @@ const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSettings = require('./transaction/settings'); const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign'); const sign = require('./transaction/sign');
const submit = require('./transaction/submit'); const submit = require('./transaction/submit');
const generateWallet = require('./generate/wallet');
const errors = require('./common').errors; const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions;
const generateWallet = convertExceptions(core.Wallet.generate);
function RippleAPI(options) { function RippleAPI(options) {
const _options = _.assign({}, options, {automatic_resubmission: false}); const _options = _.assign({}, options, {automatic_resubmission: false});
this.remote = new ripple.Remote(_options); this.remote = new core.Remote(_options);
} }
RippleAPI.prototype = { RippleAPI.prototype = {

View File

@@ -1,5 +1,4 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
@@ -20,23 +19,19 @@ function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
peer: options.counterparty peer: options.counterparty
}; };
const currency = options.currency ? options.currency.toUpperCase() : null;
remote.requestAccountLines(requestOptions, (error, data) => { remote.requestAccountLines(requestOptions, (error, data) => {
return error ? callback(error) : return error ? callback(error) :
callback(null, { callback(null, {
marker: data.marker, marker: data.marker,
results: data.lines.map(parseAccountTrustline) results: data.lines.map(parseAccountTrustline)
.filter(_.partial(currencyFilter, currency)) .filter(_.partial(currencyFilter, options.currency || null))
}); });
}); });
} }
function getTrustlines( function getTrustlines(account: string, options: {currency: string,
account: string, counterparty: string, limit: number, ledgerVersion: number},
options: {currency: string, counterparty: string, callback: () => void): void {
limit: number, ledgerVersion: number},
callback: () => void
): void {
validate.address(account); validate.address(account);
validate.getTrustlinesOptions(options); validate.getTrustlinesOptions(options);

View File

@@ -1,20 +1,7 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
const xrpToDrops = utils.common.xrpToDrops;
function renameCounterpartyToIssuer(amount) {
if (amount === undefined || amount.counterparty === undefined) {
return amount;
}
const issuer = amount.counterparty === undefined ?
amount.issuer : amount.counterparty;
const withIssuer = _.assign({}, amount, {issuer: issuer});
return _.omit(withIssuer, 'counterparty');
}
const OfferCreateFlags = { const OfferCreateFlags = {
passive: {set: 'Passive'}, passive: {set: 'Passive'},
@@ -22,19 +9,14 @@ const OfferCreateFlags = {
fillOrKill: {set: 'FillOrKill'} fillOrKill: {set: 'FillOrKill'}
}; };
function toRippledAmount(amount) {
return amount.currency === 'XRP' ?
xrpToDrops(amount.value) : renameCounterpartyToIssuer(amount);
}
function createOrderTransaction(account, order) { function createOrderTransaction(account, order) {
validate.address(account); validate.address(account);
validate.order(order); validate.order(order);
const transaction = new ripple.Transaction(); const transaction = new utils.common.core.Transaction();
const takerPays = toRippledAmount(order.direction === 'buy' ? const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ?
order.quantity : order.totalPrice); order.quantity : order.totalPrice);
const takerGets = toRippledAmount(order.direction === 'buy' ? const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ?
order.totalPrice : order.quantity); order.totalPrice : order.quantity);
transaction.offerCreate(account, takerPays, takerGets); transaction.offerCreate(account, takerPays, takerGets);

View File

@@ -2,13 +2,12 @@
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const validate = utils.common.validate;
const ripple = utils.common.core;
function createOrderCancellationTransaction(account, sequence) { function createOrderCancellationTransaction(account, sequence) {
validate.address(account); validate.address(account);
validate.sequence(sequence); validate.sequence(sequence);
const transaction = new ripple.Transaction(); const transaction = new utils.common.core.Transaction();
transaction.offerCancel(account, sequence); transaction.offerCancel(account, sequence);
return transaction; return transaction;
} }

View File

@@ -1,9 +1,8 @@
/* @flow */ /* @flow */
/* eslint-disable valid-jsdoc */
'use strict'; 'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount; const toRippledAmount = utils.common.toRippledAmount;
@@ -37,80 +36,48 @@ function applyAnyCounterpartyEncoding(payment) {
} }
} }
function uppercaseCurrencyCodes(payment) {
if (payment.source.amount) {
payment.source.amount.currency =
payment.source.amount.currency.toUpperCase();
}
if (payment.destination.amount) {
payment.destination.amount.currency =
payment.destination.amount.currency.toUpperCase();
}
}
function createPaymentTransaction(account, payment) { function createPaymentTransaction(account, payment) {
applyAnyCounterpartyEncoding(payment); applyAnyCounterpartyEncoding(payment);
uppercaseCurrencyCodes(payment);
validate.address(account); validate.address(account);
validate.payment(payment); validate.payment(payment);
const transaction = new ripple.Transaction(); const transaction = new utils.common.core.Transaction();
const transactionData = { transaction.payment({
from: payment.source.address, from: payment.source.address,
to: payment.destination.address, to: payment.destination.address,
amount: toRippledAmount(payment.destination.amount) amount: toRippledAmount(payment.destination.amount)
}; });
if (payment.invoiceID) { if (payment.invoiceID) {
transaction.invoiceID(payment.invoiceID); transaction.invoiceID(payment.invoiceID);
} }
transaction.payment(transactionData);
if (payment.source.tag) { if (payment.source.tag) {
transaction.sourceTag(parseInt(payment.source.tag, 10)); transaction.sourceTag(payment.source.tag);
} }
if (payment.destination.tag) { if (payment.destination.tag) {
transaction.destinationTag(parseInt(payment.destination.tag, 10)); transaction.destinationTag(payment.destination.tag);
}
if (payment.paths) {
transaction.paths(JSON.parse(payment.paths));
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
if (payment.allowPartialPayment) {
transaction.setFlags(['PartialPayment']);
}
if (payment.noDirectRipple) {
transaction.setFlags(['NoRippleDirect']);
} }
if (isSendMaxAllowed(payment)) { if (isSendMaxAllowed(payment)) {
const maxValue = new BigNumber(payment.source.amount.value) const maxValue = new BigNumber(payment.source.amount.value)
.plus(payment.source.slippage || 0).toString(); .plus(payment.source.slippage || 0).toString();
const maxAmount = _.assign({}, payment.source.amount, {value: maxValue});
if (payment.source.amount.currency === 'XRP') { transaction.sendMax(toRippledAmount(maxAmount));
transaction.sendMax(utils.common.xrpToDrops(maxValue));
} else {
transaction.sendMax({
value: maxValue,
currency: payment.source.amount.currency,
issuer: payment.source.amount.counterparty
});
}
} }
if (typeof payment.paths === 'string') {
transaction.paths(JSON.parse(payment.paths));
} else if (typeof payment.paths === 'object') {
transaction.paths(payment.paths);
}
if (payment.memos && Array.isArray(payment.memos)) {
for (let m = 0; m < payment.memos.length; m++) {
const memo = payment.memos[m];
transaction.addMemo(memo.type, memo.format, memo.data);
}
}
const flags = [];
if (payment.allowPartialPayment) {
flags.push('PartialPayment');
}
if (payment.noDirectRipple) {
flags.push('NoRippleDirect');
}
if (flags.length > 0) {
transaction.setFlags(flags);
}
return transaction; return transaction;
} }

View File

@@ -3,11 +3,9 @@
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
const AccountFlagIndices = utils.common.constants.AccountFlagIndices; const AccountFlagIndices = utils.common.constants.AccountFlagIndices;
const AccountFields = utils.common.constants.AccountFields; const AccountFields = utils.common.constants.AccountFields;
const ValidationError = utils.common.errors.ValidationError;
// Emptry string passed to setting will clear it // Emptry string passed to setting will clear it
const CLEAR_SETTING = ''; const CLEAR_SETTING = '';
@@ -33,7 +31,7 @@ function setTransactionFields(transaction, input) {
const field = fieldSchema[fieldName]; const field = fieldSchema[fieldName];
let value = input[field.name]; let value = input[field.name];
if (typeof value === 'undefined') { if (value === undefined) {
continue; continue;
} }
@@ -42,24 +40,9 @@ function setTransactionFields(transaction, input) {
value = field.defaults; value = field.defaults;
} }
if (field.encoding === 'hex') { if (field.encoding === 'hex' && !field.length) {
// If the field is supposed to be hex, why don't we do a
// toString('hex') on it?
if (field.length) {
// Field is fixed length, why are we checking here though?
// We could move this to validateInputs
if (value.length > field.length) {
throw new ValidationError('Parameter length exceeded: ' + fieldName);
} else if (value.length < field.length) {
value = _.padLeft(value, field.length);
}
} else {
// Field is variable length. Expecting an ascii string as input.
// This is currently only used for Domain field // This is currently only used for Domain field
value = new Buffer(value, 'ascii').toString('hex'); value = new Buffer(value, 'ascii').toString('hex').toUpperCase();
}
value = value.toUpperCase();
} }
transaction.tx_json[fieldName] = value; transaction.tx_json[fieldName] = value;
@@ -80,18 +63,14 @@ function setTransactionFields(transaction, input) {
*/ */
function convertTransferRate(transferRate) { function convertTransferRate(transferRate) {
if (_.isNumber(transferRate)) { return _.isNumber(transferRate) ? transferRate * 1e9 : transferRate;
return transferRate * Math.pow(10, 9);
}
return transferRate;
} }
function createSettingsTransaction(account, settings) { function createSettingsTransaction(account, settings) {
validate.address(account); validate.address(account);
validate.settings(settings); validate.settings(settings);
const transaction = new ripple.Transaction(); const transaction = new utils.common.core.Transaction();
if (settings.regularKey) { if (settings.regularKey) {
return transaction.setRegularKey({ return transaction.setRegularKey({
account: account, account: account,

View File

@@ -1,7 +1,7 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core; const core = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
/** /**
@@ -19,7 +19,7 @@ const HASH_TX_SIGN = 0x53545800; // 'STX'
const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx' const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
function getKeyPair(address, secret) { function getKeyPair(address, secret) {
return ripple.Seed.from_json(secret).get_key(address); return core.Seed.from_json(secret).get_key(address);
} }
function getPublicKeyHex(keypair) { function getPublicKeyHex(keypair) {
@@ -27,7 +27,7 @@ function getPublicKeyHex(keypair) {
} }
function serialize(txJSON) { function serialize(txJSON) {
return ripple.SerializedObject.from_json(txJSON); return core.SerializedObject.from_json(txJSON);
} }
function hashSerialization(serialized, prefix) { function hashSerialization(serialized, prefix) {
@@ -44,13 +44,12 @@ function signingHash(txJSON, isTestNet=false) {
function computeSignature(txJSON, keypair) { function computeSignature(txJSON, keypair) {
const signature = keypair.sign(signingHash(txJSON)); const signature = keypair.sign(signingHash(txJSON));
return ripple.sjcl.codec.hex.fromBits(signature).toUpperCase(); return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
} }
/*:: type TxJSON = {Account: string; SigningPubKey: string, function sign(txJSON: {Account: string; SigningPubKey: string,
TxnSignature: string}; TxnSignature: string}, secret: string):
type Signed = {signedTransaction: string; id: string}; */ {signedTransaction: string; id: string} {
function sign(txJSON: TxJSON, secret: string): Signed {
validate.txJSON(txJSON); validate.txJSON(txJSON);
validate.addressAndSecret({address: txJSON.Account, secret: secret}); validate.addressAndSecret({address: txJSON.Account, secret: secret});

View File

@@ -1,13 +1,12 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
/*:: type Callback = (err: any, data: any) => void */ function submit(tx_blob: string,
function submit(tx_blob: string, callback: Callback): void { callback: (err: any, data: any) => void): void {
validate.blob(tx_blob); validate.blob(tx_blob);
const request = new ripple.Request(this.remote, 'submit'); const request = new utils.common.core.Request(this.remote, 'submit');
request.message.tx_blob = tx_blob; request.message.tx_blob = tx_blob;
request.request(null, callback); request.request(null, callback);
} }

View File

@@ -1,7 +1,6 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const ripple = utils.common.core;
const validate = utils.common.validate; const validate = utils.common.validate;
const TrustSetFlags = { const TrustSetFlags = {
@@ -20,7 +19,7 @@ function createTrustlineTransaction(account, trustline) {
value: trustline.limit value: trustline.limit
}; };
const transaction = new ripple.Transaction(); const transaction = new utils.common.core.Transaction();
transaction.trustSet(account, limit, transaction.trustSet(account, limit,
trustline.qualityIn, trustline.qualityOut); trustline.qualityIn, trustline.qualityOut);
utils.setTransactionBitFlags(transaction, trustline, TrustSetFlags); utils.setTransactionBitFlags(transaction, trustline, TrustSetFlags);