Convert getAccountTransactions

This commit is contained in:
Chris Clark
2015-06-16 18:47:43 -07:00
parent ff6ac0333c
commit 8f37438a08
21 changed files with 514 additions and 229 deletions

View File

@@ -1,9 +1,11 @@
[ignore]
.*/src/.*
.*/src/api/.*
.*/src/core/.*
.*/dist/.*
.*/test/fixtures/.*
[include]
./node_modules/
[libs]

View File

@@ -40,6 +40,7 @@ RippleAPI.prototype = {
getOrderBook: orders.getOrderBook,
getSettings: settings.getSettings,
getTransaction: transactions.getTransaction,
getAccountTransactions: transactions.getAccountTransactions,
getNotification: notifications.getNotification,
getNotifications: notifications.getNotifications,

View File

@@ -1,7 +1,12 @@
/* @flow */
'use strict';
const utils = require('./utils');
function parseAmount(amount) {
/*:: type Amount = string | {currency: string, issuer: string, value: string} */
/*:: type XRPAmount = {currency: string, value: string} */
/*:: type IOUAmount = {currency: string, value: string, counterparty: string} */
/*:: type Output = XRPAmount | IOUAmount */
function parseAmount(amount: Amount): Output {
if (typeof amount === 'string') {
return {
currency: 'XRP',
@@ -11,7 +16,7 @@ function parseAmount(amount) {
return {
currency: amount.currency,
value: amount.value,
counterparty: amount.issuer || amount.counterparty
counterparty: amount.issuer
};
}

View File

@@ -1,7 +1,8 @@
/* @flow */
'use strict';
const assert = require('assert');
function parseOrderCancellation(tx) {
function parseOrderCancellation(tx: Object): Object {
assert(tx.TransactionType === 'OfferCancel');
return {
orderSequence: tx.OfferSequence

View File

@@ -1,3 +1,4 @@
/* @flow */
'use strict';
const AccountFields = require('./utils').constants.AccountFields;
@@ -8,7 +9,7 @@ function parseField(info, value) {
return value;
}
function parseFields(data) {
function parseFields(data: Object): Object {
const settings = {};
for (const fieldName in AccountFields) {
const fieldValue = data[fieldName];

View File

@@ -1,3 +1,4 @@
/* @flow */
/* eslint-disable valid-jsdoc */
'use strict';
const ripple = require('../utils').common.core;

View File

@@ -1,10 +1,11 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const flags = utils.core.Transaction.flags.OfferCreate;
function parseOrder(tx) {
function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate');
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell';

View File

@@ -1,7 +1,9 @@
/* @flow */
'use strict';
const parseAmount = require('./amount');
function parsePathfind(sourceAddress, destinationAmount, pathfindResult) {
function parsePathfind(sourceAddress: string,
destinationAmount: Object, pathfindResult: Object): Object {
return pathfindResult.alternatives.map(function(alternative) {
return {
source: {

View File

@@ -1,3 +1,4 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
@@ -19,7 +20,7 @@ function parsePaymentMemos(tx) {
return tx.Memos.map((m) => m.Memo);
}
function parsePayment(tx) {
function parsePayment(tx: Object): Object {
assert(tx.TransactionType === 'Payment');
const source = {

View File

@@ -1,3 +1,4 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
@@ -8,7 +9,7 @@ function getName(flagNumber) {
return _.findKey(AccountSetFlags, (v) => v === flagNumber);
}
function parseSettings(tx) {
function parseSettings(tx: Object) {
const txType = tx.TransactionType;
assert(txType === 'AccountSet' || txType === 'SetRegularKey');
const settings = {};

View File

@@ -1,3 +1,4 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
@@ -19,7 +20,7 @@ function parseTransactionType(type) {
return mapping[type] || null;
}
function parseTransaction(tx) {
function parseTransaction(tx: Object): ?Object {
const type = parseTransactionType(tx.TransactionType);
const mapping = {
'payment': parsePayment,

View File

@@ -1,9 +1,10 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const flags = utils.core.Transaction.flags.TrustSet;
function parseTrustline(tx) {
function parseTrustline(tx: Object): Object {
assert(tx.TransactionType === 'TrustSet');
return {
@@ -12,9 +13,9 @@ function parseTrustline(tx) {
counterparty: tx.LimitAmount.issuer,
qualityIn: tx.QualityIn,
qualityOut: tx.QualityOut,
allowRippling: tx.Flags & flags.NoRipple === 0,
frozen: tx.Flags & flags.SetFreeze !== 0,
authorized: tx.Flags & flags.SetAuth !== 0
allowRippling: (tx.Flags & flags.NoRipple) === 0,
frozen: (tx.Flags & flags.SetFreeze) !== 0,
authorized: (tx.Flags & flags.SetAuth) !== 0
};
}

View File

@@ -1,18 +1,19 @@
/* @flow */
'use strict';
const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const toTimestamp = require('../../../core/utils').toTimestamp;
const utils = require('../utils');
function parseTimestamp(tx) {
function parseTimestamp(tx: {date: string}): string | void {
return tx.date ? (new Date(toTimestamp(tx.date))).toISOString() : undefined;
}
function removeUndefined(obj) {
function removeUndefined(obj: ?Object): ?Object {
return obj ? _.omit(obj, _.isUndefined) : obj;
}
function parseOutcome(tx) {
function parseOutcome(tx: Object): ?Object {
if (!tx.validated) {
return undefined;
}
@@ -27,6 +28,7 @@ function parseOutcome(tx) {
balanceChanges: balanceChanges,
orderbookChanges: orderbookChanges,
ledgerVersion: tx.ledger_index,
indexInLedger: tx.meta.TransactionIndex,
sequence: tx.Sequence
};
}

View File

@@ -7,7 +7,7 @@ const parseTransaction = require('./parse/transaction');
const validate = utils.common.validate;
const errors = utils.common.errors;
const DEFAULT_RESULTS_PER_PAGE = 10;
const DEFAULT_LIMIT = 100;
const MIN_LEDGER_VERSION = 32570; // earlier versions have been completely lost
function hasCompleteLedgerRange(remote, options) {
@@ -74,228 +74,86 @@ function getTransaction(identifier, options, callback) {
], callbackWrapper);
}
/**
* Wrapper around the standard ripple-lib requestAccountTx function
*
* @param {Remote} remote
* @param {RippleAddress} options.account
* @param {Number} [-1] options.ledger_index_min
* @param {Number} [-1] options.ledger_index_max
* @param {Boolean} [false] options.earliestFirst
* @param {Boolean} [false] options.binary
* @param {opaque value} options.marker
* @param {Function} callback
*
* @callback
* @param {Error} error
* @param {Array of transactions in JSON format} response.transactions
* @param {opaque value} response.marker
*/
function getAccountTx(api, options, callback) {
function parseAccountTxTransaction(tx) {
// rippled uses a different response format for 'account_tx' than 'tx'
tx.tx.meta = tx.meta;
tx.tx.validated = tx.validated;
return parseTransaction(tx.tx);
}
function getAccountTx(remote, address, limit, marker, options, callback) {
const params = {
account: options.account,
ledger_index_min: options.ledger_index_min || options.ledger_index || -1,
ledger_index_max: options.ledger_index_max || options.ledger_index || -1,
limit: options.limit || DEFAULT_RESULTS_PER_PAGE,
account: address,
ledger_index_min: options.ledgerVersion || options.minLedgerVersion || -1,
ledger_index_max: options.ledgerVersion || options.maxLedgerVersion || -1,
forward: options.earliestFirst,
marker: options.marker
binary: options.binary,
limit: Math.min(limit || DEFAULT_LIMIT, 10),
marker: marker
};
if (options.binary) {
params.binary = true;
remote.requestAccountTx(params, (error, data) => {
return error ? callback(error) : callback(null, {
transactions: data.transactions.filter((tx) => tx.validated)
.map(parseAccountTxTransaction),
marker: data.marker
});
});
}
function transactionFilter(address, filters, tx) {
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
return false;
}
api.remote.requestAccountTx(params, function(error, account_tx_results) {
if (error) {
return callback(error);
}
const transactions = [];
account_tx_results.transactions.forEach(function(tx_entry) {
if (!tx_entry.validated) {
return;
}
const tx = tx_entry.tx;
tx.meta = tx_entry.meta;
tx.validated = tx_entry.validated;
transactions.push(tx);
});
callback(null, {
transactions: transactions,
marker: account_tx_results.marker
});
});
if (filters.types && !_.includes(filters.types, tx.type)) {
return false;
}
if (filters.outgoing && tx.address !== address) {
return false;
}
if (filters.incoming && tx.address === address) {
return false;
}
return true;
}
/**
* Filter transactions based on the given set of options.
*
* @param {Array of transactions in JSON format} transactions
* @param {Boolean} [false] options.exclude_failed
* @param {Array of Strings} options.types Possible values are "payment",
* "offercreate", "offercancel", "trustset", "accountset"
* @param {RippleAddress} options.source_account
* @param {RippleAddress} options.destination_account
* @param {String} options.direction Possible values are "incoming", "outgoing"
*
* @returns {Array of transactions in JSON format} filtered_transactions
*/
function transactionFilter(transactions, options) {
const filtered_transactions = transactions.filter(function(transaction) {
if (options.exclude_failed) {
if (transaction.state === 'failed' || (transaction.meta
&& transaction.meta.TransactionResult !== 'tesSUCCESS')) {
return false;
}
}
if (options.types && options.types.length > 0) {
if (options.types.indexOf(
transaction.TransactionType.toLowerCase()) === -1) {
return false;
}
}
if (options.source_account) {
if (transaction.Account !== options.source_account) {
return false;
}
}
if (options.destination_account) {
if (transaction.Destination !== options.destination_account) {
return false;
}
}
if (options.direction) {
if (options.direction === 'outgoing'
&& transaction.Account !== options.account) {
return false;
}
if (options.direction === 'incoming' && transaction.Destination
&& transaction.Destination !== options.account) {
return false;
}
}
return true;
});
return filtered_transactions;
}
function getTransactionsHelper(api, options, callback) {
getAccountTx(api, options, function(error, results) {
function getAccountTransactionsRecursive(
remote, address, limit, marker, options, callback) {
getAccountTx(remote, address, limit, marker, options, (error, data) => {
if (error) {
callback(error);
return;
}
const filter = _.partial(transactionFilter, address, options);
const unfilteredTransactions = data.transactions;
const filteredTransactions = unfilteredTransactions.filter(filter);
const isExhausted = unfilteredTransactions.length === 0;
if (!isExhausted && filteredTransactions.length < limit) {
const remaining = limit - filteredTransactions.length;
getAccountTransactionsRecursive(
remote, address, remaining, data.marker, options, (_err, txs) => {
return error ? callback(_err) :
callback(null, filteredTransactions.concat(txs));
});
} else {
// Set marker so that when this function is called again
// recursively it starts from the last place it left off
options.marker = results.marker;
callback(null, results.transactions);
callback(null, filteredTransactions.slice(0, limit));
}
});
}
/**
* Recursively get transactions for the specified account from
* the Remote. If options.min is set, this will
* recurse until it has retrieved that number of transactions or
* it has reached the end of the account's transaction history.
*
* @param {Remote} remote
* @param {RippleAddress} options.account
* @param {Number} [-1] options.ledger_index_min
* @param {Number} [-1] options.ledger_index_max
* @param {Boolean} [false] options.earliestFirst
* @param {Boolean} [false] options.binary
* @param {Boolean} [false] options.exclude_failed
* @param {Number} [DEFAULT_RESULTS_PER_PAGE] options.min
* @param {Number} [DEFAULT_RESULTS_PER_PAGE] options.max
* @param {Array of Strings} options.types Possible values are "payment",
* "offercreate", "offercancel", "trustset", "accountset"
* @param {opaque value} options.marker
* @param {Array of Transactions} options.previous_transactions
* Included automatically when this function is called recursively
* @param {Express.js Response} res
* @param {Function} callback
*
* @callback
* @param {Error} error
* @param {Array of transactions in JSON format} transactions
*/
function getAccountTransactions(api, options, callback) {
try {
validate.address(options.account);
} catch(err) {
return callback(err);
}
function getAccountTransactions(address, options, callback) {
validate.address(address);
if (!options.min) {
options.min = module.exports.DEFAULT_RESULTS_PER_PAGE;
}
if (!options.max) {
options.max = Math.max(options.min,
module.exports.DEFAULT_RESULTS_PER_PAGE);
}
if (!options.limit) {
options.limit = module.exports.DEFAULT_LIMIT;
}
function queryTransactions(async_callback) {
getTransactionsHelper(api, options, async_callback);
}
function filterTransactions(transactions, async_callback) {
async_callback(null, transactionFilter(transactions, options));
}
function sortTransactions(transactions, async_callback) {
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0);
transactions.sort(compare);
async_callback(null, transactions);
}
function mergeAndTruncateResults(txns, async_callback) {
let transactions = txns;
if (options.previous_transactions
&& options.previous_transactions.length > 0) {
transactions = options.previous_transactions.concat(transactions);
}
if (options.offset && options.offset > 0) {
const offset_remaining = options.offset - transactions.length;
transactions = transactions.slice(options.offset);
options.offset = offset_remaining;
}
if (transactions.length > options.max) {
transactions = transactions.slice(0, options.max);
}
async_callback(null, transactions);
}
function asyncWaterfallCallback(error, transactions) {
if (error) {
return callback(error);
}
if (!options.min || transactions.length >= options.min || !options.marker) {
callback(null, transactions);
} else {
options.previous_transactions = transactions;
setImmediate(function() {
getAccountTransactions(api, options, callback);
});
}
}
const steps = [
queryTransactions,
filterTransactions,
sortTransactions,
mergeAndTruncateResults
];
async.waterfall(steps, asyncWaterfallCallback);
const limit = options.limit || DEFAULT_LIMIT;
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0);
getAccountTransactionsRecursive(
this.remote, address, limit, null, options, (error, transactions) => {
return error ? callback(error) : callback(null, transactions.sort(compare));
});
}
module.exports = {
DEFAULT_LIMIT: 200,
DEFAULT_RESULTS_PER_PAGE: DEFAULT_RESULTS_PER_PAGE,
NUM_TRANSACTION_TYPES: 5,
DEFAULT_LEDGER_BUFFER: 3,
getTransaction: utils.wrapCatch(getTransaction),
getAccountTransactions: getAccountTransactions
getAccountTransactions: utils.wrapCatch(getAccountTransactions)
};

View File

@@ -58,12 +58,10 @@ function signum(num) {
* @returns {Number} [-1, 0, 1]
*/
function compareTransactions(first, second) {
if (first.ledger_index === second.ledger_index) {
return signum(
Number(first.meta.TransactionIndex) -
Number(second.meta.TransactionIndex));
if (first.ledgerVersion === second.ledgerVersion) {
return signum(Number(first.indexInLedger) - Number(second.indexInLedger));
}
return Number(first.ledger_index) < Number(second.ledger_index) ? -1 : 1;
return Number(first.ledgerVersion) < Number(second.ledgerVersion) ? -1 : 1;
}
function attachDate(api, baseTransactions, callback) {

View File

@@ -22,6 +22,8 @@ const MockPRNG = require('./mock-prng');
const sjcl = require('../src').sjcl;
const submitResponse = require('./fixtures/submit-response');
const transactionResponse = require('./fixtures/transaction-response');
const accountTransactionsResponse =
require('./fixtures/account-transactions-response');
function checkResult(expected, done, error, response) {
if (error) {
@@ -91,4 +93,10 @@ describe('RippleAPI', function() {
this.api.getTransaction(hashes.VALID_TRANSACTION_HASH, {},
_.partial(checkResult, transactionResponse, done));
});
it('getAccountTransactions', function(done) {
const options = {types: ['payment', 'order'], outgoing: true, limit: 2};
this.api.getAccountTransactions(address, options,
_.partial(checkResult, accountTransactionsResponse, done));
});
});

View File

@@ -0,0 +1,180 @@
[
{
"type": "payment",
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"specification": {
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "XRP",
"value": "1.112209"
}
},
"destination": {
"address": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"amount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"paths": "[[{\"currency\":\"USD\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"currency\":\"USD\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]",
"allowPartialPayment": false,
"noDirectRipple": false
},
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"currency": "USD",
"value": "-0.001"
},
{
"counterparty": "r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr",
"currency": "USD",
"value": "0.001002"
}
],
"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM": [
{
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"currency": "USD",
"value": "0.001"
}
],
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59": [
{
"counterparty": "",
"currency": "XRP",
"value": "-1.101208"
}
],
"r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr": [
{
"counterparty": "",
"currency": "XRP",
"value": "1.101198"
},
{
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"currency": "USD",
"value": "-0.001002"
}
]
},
"orderbookChanges": {
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59": [
{
"taker_pays": {
"currency": "XRP",
"counterparty": "",
"value": "-1.101198"
},
"taker_gets": {
"currency": "USD",
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"value": "-0.001002"
},
"sequence": 58,
"status": "open"
}
]
},
"ledgerVersion": 348860,
"indexInLedger": 0,
"sequence": 4
}
},
{
"type": "payment",
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"specification": {
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"amount": {
"currency": "XRP",
"value": "1.112209"
}
},
"destination": {
"address": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"amount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"paths": "[[{\"currency\":\"USD\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"currency\":\"USD\",\"issuer\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]",
"allowPartialPayment": false,
"noDirectRipple": false
},
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
"currency": "USD",
"value": "-0.001"
},
{
"counterparty": "r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr",
"currency": "USD",
"value": "0.001002"
}
],
"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM": [
{
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"currency": "USD",
"value": "0.001"
}
],
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59": [
{
"counterparty": "",
"currency": "XRP",
"value": "-1.101208"
}
],
"r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr": [
{
"counterparty": "",
"currency": "XRP",
"value": "1.101198"
},
{
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"currency": "USD",
"value": "-0.001002"
}
]
},
"orderbookChanges": {
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59": [
{
"taker_pays": {
"currency": "XRP",
"counterparty": "",
"value": "-1.101198"
},
"taker_gets": {
"currency": "USD",
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"value": "-0.001002"
},
"sequence": 58,
"status": "open"
}
]
},
"ledgerVersion": 348860,
"indexInLedger": 0,
"sequence": 4
}
}
]

209
test/fixtures/acct-tx-response.js vendored Normal file
View File

@@ -0,0 +1,209 @@
/* eslint-disable max-len */
'use strict';
const _ = require('lodash');
const hashes = require('./hashes');
const addresses = require('./addresses');
const SerializedObject = require('../../src/core').SerializedObject;
module.exports = function(request, options={}) {
_.defaults(options, {
memos: [],
hash: hashes.VALID_TRANSACTION_HASH,
validated: true
});
const tx = {
Account: addresses.ACCOUNT,
Amount: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '0.001'
},
Destination: addresses.ISSUER,
Fee: '10',
Flags: 0,
Memos: options.memos,
Paths: [
[
{
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
type: 48,
type_hex: '0000000000000030'
},
{
account: addresses.OTHER_ACCOUNT,
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
type: 49,
type_hex: '0000000000000031'
}
]
],
SendMax: '1112209',
Sequence: 4,
SigningPubKey: '02BC8C02199949B15C005B997E7C8594574E9B02BA2D0628902E0532989976CF9D',
TransactionType: 'Payment',
TxnSignature: '304502204EE3E9D1B01D8959B08450FCA9E22025AF503DEF310E34A93863A85CAB3C0BC5022100B61F5B567F77026E8DEED89EED0B7CAF0E6C96C228A2A65216F0DC2D04D52083'
};
const meta = {
AffectedNodes: [
{
ModifiedNode: {
FinalFields: {
Account: addresses.ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5E03E788E09BB000',
BookNode: '0000000000000000',
Flags: 0,
OwnerNode: '0000000000000000',
Sequence: 58,
TakerGets: {
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
value: '5.648998'
},
TakerPays: '6208248802'
},
LedgerEntryType: 'Offer',
LedgerIndex: '3CFB3C79D4F1BDB1EE5245259372576D926D9A875713422F7169A6CC60AFA68B',
PreviousFields: {
TakerGets: {
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
value: '5.65'
},
TakerPays: '6209350000'
},
PreviousTxnID: '8F571C346688D89AC1F737AE3B6BB5D976702B171CC7B4DE5CA3D444D5B8D6B4',
PreviousTxnLgrSeq: 348433
}
},
{
ModifiedNode: {
FinalFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '-0.001'
},
Flags: 131072,
HighLimit: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '1'
},
HighNode: '0000000000000000',
LowLimit: {
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
value: '0'
},
LowNode: '0000000000000002'
},
LedgerEntryType: 'RippleState',
LedgerIndex: '4BD1874F8F3A60EDB0C23F5BD43E07953C2B8741B226648310D113DE2B486F01',
PreviousFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '0'
}
},
PreviousTxnID: '5B2006DAD0B3130F57ACF7CC5CCAC2EEBCD4B57AAA091A6FD0A24B073D08ABB8',
PreviousTxnLgrSeq: 343703
}
},
{
ModifiedNode: {
FinalFields: {
Account: addresses.ACCOUNT,
Balance: '9998898762',
Flags: 0,
OwnerCount: 3,
Sequence: 5
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05',
PreviousFields: {
Balance: '9999999970',
Sequence: 4
},
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
PreviousTxnLgrSeq: 343570
}
},
{
ModifiedNode: {
FinalFields: {
Account: 'r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr',
Balance: '912695302618',
Flags: 0,
OwnerCount: 10,
Sequence: 59
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: 'F3E119AAA87AF3607CF87F5523BB8278A83BCB4142833288305D767DD30C392A',
PreviousFields: {
Balance: '912694201420'
},
PreviousTxnID: '8F571C346688D89AC1F737AE3B6BB5D976702B171CC7B4DE5CA3D444D5B8D6B4',
PreviousTxnLgrSeq: 348433
}
},
{
ModifiedNode: {
FinalFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '-5.5541638883365'
},
Flags: 131072,
HighLimit: {
currency: 'USD',
issuer: 'r9tGqzZgKxVFvzKFdUqXAqTzazWBUia8Qr',
value: '1000'
},
HighNode: '0000000000000000',
LowLimit: {
currency: 'USD',
issuer: addresses.OTHER_ACCOUNT,
value: '0'
},
LowNode: '000000000000000C'
},
LedgerEntryType: 'RippleState',
LedgerIndex: 'FA1255C2E0407F1945BCF9351257C7C5C28B0F5F09BB81C08D35A03E9F0136BC',
PreviousFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '-5.5551658883365'
}
},
PreviousTxnID: '8F571C346688D89AC1F737AE3B6BB5D976702B171CC7B4DE5CA3D444D5B8D6B4',
PreviousTxnLgrSeq: 348433
}
}
],
TransactionIndex: 0,
TransactionResult: 'tesSUCCESS'
};
return JSON.stringify({
id: request.id,
status: 'success',
type: 'response',
result: {
transactions: [
{
ledger_index: 348860,
tx_blob: SerializedObject.from_json(tx).to_hex(),
meta: SerializedObject.from_json(meta).to_hex(),
validated: options.validated
}
]
}
});
};

View File

@@ -2,9 +2,12 @@
'use strict';
const _ = require('lodash');
const addresses = require('./addresses');
const accountTransactionsResponse = require('./acct-tx-response');
const SerializedObject = require('ripple-lib').SerializedObject;
const BASE_LEDGER_INDEX = 8819951;
module.exports.accountTransactionsResponse = accountTransactionsResponse;
module.exports.subscribeResponse = function(request) {
return JSON.stringify({
id: request.id,

View File

@@ -84,6 +84,7 @@
]
},
"ledgerVersion": 348860,
"indexInLedger": 0,
"sequence": 4
}
}

View File

@@ -112,5 +112,13 @@ module.exports = function(port) {
}
});
mock.on('request_account_tx', function(request, conn) {
if (request.account === addresses.ACCOUNT) {
conn.send(fixtures.accountTransactionsResponse(request));
} else {
assert(false, 'Unrecognized account address: ' + request.account);
}
});
return mock;
};