Compare commits

...

46 Commits

Author SHA1 Message Date
Geert Weening
fcc9bacb4e bump version to 0.13.0-rc10 2015-09-22 18:33:48 -07:00
wltsmrz
9a5e8fd2ba Merge pull request #558 from clark800/wss
Add pattern for servers option
2015-09-22 18:25:17 -07:00
Chris Clark
c213b98329 Add pattern for servers option 2015-09-21 17:34:39 -07:00
Alan Cohen
27d2e6e519 Merge pull request #557 from clark800/ledger-close
Add ledgerClosed event
2015-09-21 15:33:44 -07:00
Chris Clark
7ee368965c Add ledgerClosed event 2015-09-21 14:38:51 -07:00
Alan Cohen
d8b5b825b3 Update verison to 0.13.0-rc9 2015-09-21 10:48:03 -07:00
Chris Clark
60c604fbe6 Merge pull request #536 from sentientwaffle/dj/suspay
suspended payments
2015-09-18 10:58:10 -07:00
sentientwaffle
2f6d25ed01 lint 2015-09-18 10:52:00 -07:00
sentientwaffle
b134081293 Add SuspendedPayment{Create,Finish,Cancel}
* Add SusPay core tests
* Rename SusPay -> SuspendedPayment (code review)
* Rename cancelAfter -> allowCancelAfter
* Rename suspendedPayment{Finish,Execution}
2015-09-18 10:51:34 -07:00
Chris Clark
a0528d7f9c Merge pull request #553 from darkdarkdragon/develop-master-merge
Merge OrderBook optimizations from master to develop
2015-09-18 10:41:22 -07:00
Alan Cohen
348335ddf0 Merge pull request #556 from clark800/tel-fix
Remove "tel" errors from submit's immediate failure category
2015-09-17 17:20:42 -07:00
Ivan Tivonenko
01752e5486 fix eslint errors 2015-09-18 03:15:09 +03:00
Chris Clark
3e758e1b86 Remove "tel" errors from submit's immediate failure category 2015-09-17 17:06:12 -07:00
Chris Clark
fb0b30a9a7 Merge pull request #555 from lumberj/add-data-to-error
RippleAPI: Add data property to error object
2015-09-17 16:29:12 -07:00
Alan Cohen
0c9aea454e RippleAPI: Add data property to error object 2015-09-17 16:05:43 -07:00
Ivan Tivonenko
20fa8bc953 optimize AutobridgeCalculator and Amount for speed 2015-09-17 19:06:47 +03:00
Alan Cohen
778f59b4fd Merge pull request #535 from clark800/submit-error
Return promise error if submit result is an immediate failure
2015-09-16 17:53:35 -07:00
Chris Clark
49623cb4dd Merge pull request #550 from lumberj/max-ledger-sequence
Set default maxLedgerVersion to last closed ledger when requesting ac…
2015-09-16 17:11:11 -07:00
Alan Cohen
90b53002aa Set default maxLedgerVersion to last closed ledger when requesting account_tx 2015-09-16 16:45:25 -07:00
Alan Cohen
93c12af305 Merge pull request #548 from lumberj/check-disjoint
Add PendingLedgerVersionError
2015-09-16 16:29:46 -07:00
Alan Cohen
60f2419b5c Add PendingLedgerVersionError
MissingLedgerHistoryError - no minLedgerVersion or maxLedgerVersion
There is a ledger gap, but a range should be provided to narrow down the range
of the gap.

MissingLedgerHistoryError
When requesting a tx, if maxLedgerVersion and minLedgerVersion provided, this
means there is a ledger gap in the provided range.

PendingLedgerVersionError:
If maxLedgerVersion provided, check if ledger is ahead of the server's last
validated ledger.
2015-09-16 15:57:55 -07:00
Chris Clark
80494ad813 Return promise error if submit result is an immediate failure 2015-09-16 15:56:24 -07:00
Alan Cohen
b43c4a7ad4 Merge pull request #549 from lumberj/validated-tx
Do not return non-validated transaction from RippleAPI#getTransaction
2015-09-16 12:29:23 -07:00
Alan Cohen
3c608de5bb Do not return non-validated transaction from RippleAPI#getTransaction 2015-09-16 12:19:20 -07:00
Chris Clark
fe5bc1d215 Merge pull request #538 from darkdarkdragon/develop-negative-error
shows event that leads to "Offer total cannot be negative" error
2015-09-16 10:26:19 -07:00
Ivan Tivonenko
580bf9a755 shows event that leads to "Offer total cannot be negative" error 2015-09-16 09:57:51 +03:00
Ivan Tivonenko
c7df5df163 make tests pass 2015-09-15 09:14:12 +03:00
Ivan Tivonenko
a08c52af55 Merge branch 'master' into develop-master-merge 2015-09-15 06:29:26 +03:00
Chris Clark
e11db0f0f3 Merge pull request #547 from vhpoet/develop
Remote: Add LowFreeze, HighFreeze flags
2015-09-14 16:59:20 -07:00
Vahe Hovhannisyan
c6e0582729 Remote: Add LowFreeze, HighFreeze flags 2015-09-14 16:57:13 -07:00
wltsmrz
6e98629f9b Merge pull request #543 from darkdarkdragon/develop-reconnect
resend request in case server was disconnected after request was sent
2015-09-13 02:14:22 -07:00
sublimator
2243760442 Merge pull request #541 from wltsmrz/add-tecOVERSIZE
Add tecOVERSIZE transaction result
2015-09-13 16:11:26 +07:00
wltsmrz
91dd6877aa Add tecOVERSIZE transaction result 2015-09-13 02:07:53 -07:00
sublimator
e73bcd8fc1 Merge pull request #545 from wltsmrz/add-multisign
Add multisign helpers
2015-09-13 15:10:19 +07:00
wltsmrz
7e886b3260 Add multisign helpers 2015-09-13 00:59:27 -07:00
Ivan Tivonenko
5c9451d3ed allow Request.request(callback) 2015-09-12 22:18:52 +03:00
wltsmrz
c6c2dcc6c0 Merge pull request #533 from clark800/webpack-net-fix
Fix browser build
2015-09-11 13:15:58 -07:00
wltsmrz
0bdd37090e Merge pull request #537 from darkdarkdragon/master-performance
Optimize performance (for 0.12 master branch)
2015-09-10 17:04:16 -07:00
Ivan Tivonenko
c745faaaf0 optimize baseconverter , OrderBook and AutobridgeCalculator for speed 2015-09-11 02:34:48 +03:00
Ivan Tivonenko
77068667e4 move reconnect logic from Request.callback to Request.request 2015-09-09 21:16:10 +03:00
Ivan Tivonenko
c57cef4a21 resend request in case server was disconnected after request was sent 2015-09-09 02:22:02 +03:00
Ivan Tivonenko
0c62fa2112 remove Firefox warning about prototype overwrite 2015-09-01 01:34:59 +03:00
Ivan Tivonenko
806547dd15 fix Amount compare bug
return 0 if values not comparable
2015-09-01 01:29:37 +03:00
Chris Clark
0cda15f2b5 Update is-my-json-valid depedency to fix webpack issue 2015-08-28 14:18:02 -07:00
Chris Clark
e343f3beb8 Remove usage of fs module because it does not work in browser 2015-08-28 11:53:45 -07:00
Chris Clark
30d5134394 Fix require failure in browser code due to proxy library 2015-08-27 11:57:15 -07:00
69 changed files with 3320 additions and 896 deletions

View File

@@ -26,6 +26,9 @@ function webpackConfig(extension, overrides) {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional=runtime'
}, {
test: /\.json/,
loader: 'json-loader'
}]
}
};

18
npm-shrinkwrap.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.13.0-rc8",
"version": "0.13.0-rc10",
"npm-shrinkwrap-version": "5.4.0",
"node-version": "v0.12.7",
"dependencies": {
@@ -9,12 +9,12 @@
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
},
"babel-runtime": {
"version": "5.8.20",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.20.tgz",
"version": "5.8.24",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.24.tgz",
"dependencies": {
"core-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.2.tgz"
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.4.tgz"
}
}
},
@@ -55,12 +55,12 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
"dependencies": {
"agent-base": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.0.tgz",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
"dependencies": {
"semver": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz"
}
}
},

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.13.0-rc8",
"version": "0.13.0-rc10",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
@@ -23,7 +23,7 @@
"extend": "~1.2.1",
"hash.js": "^1.0.3",
"https-proxy-agent": "^1.0.0",
"is-my-json-valid": "^2.12.0",
"is-my-json-valid": "^2.12.2",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"ripple-address-codec": "^1.6.0",
@@ -48,6 +48,7 @@
"gulp-rename": "~1.2.0",
"gulp-uglify": "~1.1.0",
"istanbul": "~0.3.5",
"json-loader": "^0.5.2",
"mocha": "~2.1.0",
"webpack": "~1.5.3",
"yargs": "~1.3.1"

View File

@@ -58,6 +58,13 @@ function MissingLedgerHistoryError(message) {
MissingLedgerHistoryError.prototype = new RippleError();
MissingLedgerHistoryError.prototype.name = 'MissingLedgerHistoryError';
function PendingLedgerVersionError(message) {
this.message = message ||
'maxLedgerVersion is greater than server\'s most recent validated ledger';
}
PendingLedgerVersionError.prototype = new RippleError();
PendingLedgerVersionError.prototype.name = 'PendingLedgerVersionError';
/**
* Request timed out
*/
@@ -82,6 +89,7 @@ module.exports = {
TransactionError,
RippledNetworkError,
NotFoundError,
PendingLedgerVersionError,
MissingLedgerHistoryError,
TimeOutError,
ApiError,

View File

@@ -1,9 +1,7 @@
/* @flow */
// flow is disabled for this file until support for requiring json is added:
// https://github.com/facebook/flow/issues/167
'use strict';
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const validator = require('is-my-json-valid');
const core = require('./utils').core;
@@ -21,21 +19,71 @@ function isValidLedgerHash(ledgerHash) {
return core.UInt256.is_valid(ledgerHash);
}
function loadSchema(filepath: string): {} {
try {
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
} catch (e) {
throw new Error('Failed to parse schema: ' + filepath);
}
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function loadSchemas(dir) {
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
function loadSchemas() {
// listed explicitly for webpack (instead of scanning schemas directory)
const schemas = [
require('./schemas/address.json'),
require('./schemas/adjustment.json'),
require('./schemas/amount.json'),
require('./schemas/amountbase.json'),
require('./schemas/balance.json'),
require('./schemas/blob.json'),
require('./schemas/currency.json'),
require('./schemas/get-account-info.json'),
require('./schemas/get-balances.json'),
require('./schemas/get-ledger.json'),
require('./schemas/get-orderbook.json'),
require('./schemas/get-orders.json'),
require('./schemas/get-paths.json'),
require('./schemas/get-server-info.json'),
require('./schemas/get-settings.json'),
require('./schemas/get-transaction.json'),
require('./schemas/get-transactions.json'),
require('./schemas/get-trustlines.json'),
require('./schemas/hash128.json'),
require('./schemas/hash256.json'),
require('./schemas/instructions.json'),
require('./schemas/issue.json'),
require('./schemas/ledger-options.json'),
require('./schemas/ledgerversion.json'),
require('./schemas/max-adjustment.json'),
require('./schemas/memo.json'),
require('./schemas/order-cancellation-transaction.json'),
require('./schemas/order-cancellation.json'),
require('./schemas/order-change.json'),
require('./schemas/order-transaction.json'),
require('./schemas/order.json'),
require('./schemas/orderbook-orders.json'),
require('./schemas/orderbook.json'),
require('./schemas/orders-options.json'),
require('./schemas/outcome.json'),
require('./schemas/pathfind.json'),
require('./schemas/payment-transaction.json'),
require('./schemas/payment.json'),
require('./schemas/quality.json'),
require('./schemas/remote-options.json'),
require('./schemas/sequence.json'),
require('./schemas/settings-options.json'),
require('./schemas/settings-transaction.json'),
require('./schemas/settings.json'),
require('./schemas/sign.json'),
require('./schemas/signed-value.json'),
require('./schemas/submit.json'),
require('./schemas/suspended-payment-cancellation.json'),
require('./schemas/suspended-payment-execution.json'),
require('./schemas/suspended-payment-creation.json'),
require('./schemas/timestamp.json'),
require('./schemas/transaction-options.json'),
require('./schemas/transactions-options.json'),
require('./schemas/trustline-transaction.json'),
require('./schemas/trustline.json'),
require('./schemas/trustlines-options.json'),
require('./schemas/tx.json'),
require('./schemas/uint32.json'),
require('./schemas/value.json'),
require('./schemas/prepare.json'),
require('./schemas/ledger-closed.json')
];
const titles = _.map(schemas, schema => schema.title);
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
@@ -67,9 +115,8 @@ function schemaValidate(schemaName: string, object: any): void {
}
}
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
SCHEMAS = loadSchemas();
module.exports = {
schemaValidate: schemaValidate,
loadSchema: loadSchema,
SCHEMAS: SCHEMAS
};

View File

@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ledgerClosed",
"description": "A ledgerClosed event message",
"type": "object",
"properties": {
"feeBase": {"type": "integer", "minimum": 0},
"feeReference": {"type": "integer", "minimum": 0},
"ledgerHash": {"$ref": "ledgerHash"},
"ledgerVersion": {"$ref": "ledgerVersion"},
"ledgerTimestamp": {"type": "string", "format": "date-time"},
"reserveBase": {"type": "integer", "minimum": 0},
"reserveIncrement": {"type": "integer", "minimum": 0},
"transactionCount": {"type": "integer", "minimum": 0},
"validatedLedgerVersions": {"type": "string"}
},
"addtionalProperties": false,
"required": ["feeBase", "feeReference", "ledgerHash", "ledgerTimestamp",
"reserveBase", "reserveIncrement", "transactionCount",
"ledgerVersion", "validatedLedgerVersions"]
}

View File

@@ -4,7 +4,7 @@
"type": "object",
"properties": {
"result": {"type": "string"},
"timestamp": {"type": "string"},
"timestamp": {"type": "string", "format": "date-time"},
"fee": {"$ref": "value"},
"balanceChanges": {
"type": "object",

View File

@@ -4,7 +4,14 @@
"type": "object",
"properties": {
"trace": {"type": "boolean"},
"servers": {"type": "array", "items": {"type": "string", "format": "uri"}}
"servers": {
"type": "array",
"items": {
"type": "string",
"format": "uri",
"pattern": "^wss?://"
}
}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-cancellation",
"type": "object",
"properties": {
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"owner": {"$ref": "address"},
"paymentSequence": {"$ref": "uint32"}
},
"required": ["owner", "paymentSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-creation",
"type": "object",
"properties": {
"source": {"$ref": "maxAdjustment"},
"destination": {"$ref": "adjustment"},
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"digest": {"$ref": "hash256"},
"allowCancelAfter": {
"type": "integer",
"minimum": 0,
"description": "milliseconds since unix epoch"
},
"allowExecuteAfter": {
"type": "integer",
"minimum": 0,
"description": "milliseconds since unix epoch"
}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspended-payment-execution",
"type": "object",
"properties": {
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"owner": {"$ref": "address"},
"paymentSequence": {"$ref": "uint32"},
"method": {"type": "integer", "minimum": 0, "maximum": 255},
"digest": {"$ref": "hash256"},
"proof": {"type": "string"}
},
"required": ["owner", "paymentSequence"],
"additionalProperties": false
}

View File

@@ -52,7 +52,7 @@ type Wrapper = (data: any) => any
function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
return function(error, data) {
if (error) {
callback(error);
callback(error, data);
return;
}
let result;
@@ -66,10 +66,15 @@ function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
};
}
function convertErrors(callback: () => void): () => void {
function convertErrors(callback: Callback): () => void {
return function(error, data) {
if (error && !(error instanceof errors.RippleError)) {
callback(new errors.RippleError(error));
const error_ = new errors.RippleError(error);
error_.data = data;
callback(error_, data);
} else if (error) {
error.data = data;
callback(error, data);
} else {
callback(error, data);
}

View File

@@ -61,6 +61,12 @@ module.exports = {
order: _.partial(schemaValidate, 'order'),
orderbook: _.partial(schemaValidate, 'orderbook'),
payment: _.partial(schemaValidate, 'payment'),
suspendedPaymentCreation:
_.partial(schemaValidate, 'suspended-payment-creation'),
suspendedPaymentExecution:
_.partial(schemaValidate, 'suspended-payment-execution'),
suspendedPaymentCancellation:
_.partial(schemaValidate, 'suspended-payment-cancellation'),
pathfind: _.partial(schemaValidate, 'pathfind'),
settings: _.partial(schemaValidate, 'settings'),
trustline: _.partial(schemaValidate, 'trustline'),

View File

@@ -2,6 +2,8 @@
'use strict';
const _ = require('lodash');
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const common = require('./common');
const server = require('./server/server');
const connect = server.connect;
@@ -23,6 +25,12 @@ const preparePayment = require('./transaction/payment');
const prepareTrustline = require('./transaction/trustline');
const prepareOrder = require('./transaction/order');
const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSuspendedPaymentCreation =
require('./transaction/suspended-payment-creation');
const prepareSuspendedPaymentExecution =
require('./transaction/suspended-payment-execution');
const prepareSuspendedPaymentCancellation =
require('./transaction/suspended-payment-cancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
@@ -34,11 +42,19 @@ const getLedger = require('./ledger/ledger');
function RippleAPI(options: {}) {
common.validate.remoteOptions(options);
if (EventEmitter instanceof Function) { // always true, needed for flow
EventEmitter.call(this);
}
const _options = _.assign({}, options, {automatic_resubmission: false});
this.remote = new common.core.Remote(_options);
this.remote.on('ledger_closed', message => {
this.emit('ledgerClosed', server.formatLedgerClose(message));
});
}
RippleAPI.prototype = {
util.inherits(RippleAPI, EventEmitter);
_.assign(RippleAPI.prototype, {
connect,
disconnect,
isConnected,
@@ -61,13 +77,16 @@ RippleAPI.prototype = {
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSuspendedPaymentCreation,
prepareSuspendedPaymentExecution,
prepareSuspendedPaymentCancellation,
prepareSettings,
sign,
submit,
generateAddress,
errors
};
});
// these are exposed only for use by unit tests; they are not part of the API
RippleAPI._PRIVATE = {

View File

@@ -18,19 +18,6 @@ function isQualityLimited(tx) {
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0;
}
function parsePaymentMemos(tx) {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return utils.removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
@@ -55,7 +42,7 @@ function parsePayment(tx: Object): Object {
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: parsePaymentMemos(tx),
memos: utils.parseMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,

View File

@@ -0,0 +1,16 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
function parseSuspendedPaymentCancellation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCancel');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
paymentSequence: tx.OfferSequence
});
}
module.exports = parseSuspendedPaymentCancellation;

View File

@@ -0,0 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
}
function parseSuspendedPaymentCreation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCreate');
const source = {
address: tx.Account,
maxAmount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
tag: tx.SourceTag
};
const destination = {
address: tx.Destination,
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
tag: tx.DestinationTag
};
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: utils.parseMemos(tx),
digest: tx.Digest,
allowCancelAfter: tx.CancelAfter,
allowExecuteAfter: tx.FinishAfter
});
}
module.exports = parseSuspendedPaymentCreation;

View File

@@ -0,0 +1,25 @@
/* @flow */
'use strict';
const assert = require('assert');
const sjclcodec = require('sjcl-codec');
const utils = require('./utils');
function convertHexToString(hexString) {
const bits = sjclcodec.hex.toBits(hexString);
return sjclcodec.utf8String.fromBits(bits);
}
function parseSuspendedPaymentExecution(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentFinish');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
paymentSequence: tx.OfferSequence,
method: tx.Method,
digest: tx.Digest,
proof: tx.Proof ? convertHexToString(tx.Proof) : undefined
});
}
module.exports = parseSuspendedPaymentExecution;

View File

@@ -7,6 +7,10 @@ const parseTrustline = require('./trustline');
const parseOrder = require('./order');
const parseOrderCancellation = require('./cancellation');
const parseSettings = require('./settings');
const parseSuspendedPaymentCreation = require('./suspended-payment-creation');
const parseSuspendedPaymentExecution = require('./suspended-payment-execution');
const parseSuspendedPaymentCancellation =
require('./suspended-payment-cancellation');
function parseTransactionType(type) {
const mapping = {
@@ -15,7 +19,10 @@ function parseTransactionType(type) {
OfferCreate: 'order',
OfferCancel: 'orderCancellation',
AccountSet: 'settings',
SetRegularKey: 'settings'
SetRegularKey: 'settings',
SuspendedPaymentCreate: 'suspendedPaymentCreation',
SuspendedPaymentFinish: 'suspendedPaymentExecution',
SuspendedPaymentCancel: 'suspendedPaymentCancellation'
};
return mapping[type] || null;
}
@@ -27,7 +34,10 @@ function parseTransaction(tx: Object): Object {
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings
'settings': parseSettings,
'suspendedPaymentCreation': parseSuspendedPaymentCreation,
'suspendedPaymentExecution': parseSuspendedPaymentExecution,
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation
};
const parser = mapping[type];
assert(parser !== undefined, 'Unrecognized transaction type');

View File

@@ -67,8 +67,22 @@ function parseOutcome(tx: Object): ?Object {
};
}
function parseMemos(tx: Object): ?Array<Object> {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
module.exports = {
parseOutcome,
parseMemos,
removeUndefined,
adjustQualityForXRP,
dropsToXrp: utils.common.dropsToXrp,

View File

@@ -52,23 +52,34 @@ function getTransactionAsync(identifier: string, options: TransactionOptions,
validate.getTransactionOptions(options);
const remote = this.remote;
const maxLedgerVersion = Math.min(options.maxLedgerVersion || Infinity,
remote.getLedgerSequence());
const maxLedgerVersion =
options.maxLedgerVersion || remote.getLedgerSequence();
function callbackWrapper(error_?: Error, tx?: Object) {
let error = error_;
if (!error && tx && tx.validated !== true) {
return callback(new errors.NotFoundError('Transaction not found'));
}
if (error instanceof RippleError && error.remote &&
error.remote.error === 'txnNotFound') {
error = new errors.NotFoundError('Transaction not found');
}
// Missing complete ledger range
if (error instanceof errors.NotFoundError
&& !utils.hasCompleteLedgerRange(remote,
options.minLedgerVersion, maxLedgerVersion)) {
callback(new errors.MissingLedgerHistoryError('Transaction not found,'
+ ' but the server\'s ledger history is incomplete'));
&& !utils.hasCompleteLedgerRange(remote, options.minLedgerVersion,
maxLedgerVersion)) {
if (utils.isPendingLedgerVersion(remote, maxLedgerVersion)) {
callback(new errors.PendingLedgerVersionError());
} else {
callback(new errors.MissingLedgerHistoryError());
}
// Transaction is found, but not in specified range
} else if (!error && tx && !isTransactionInRange(tx, options)) {
callback(new errors.NotFoundError('Transaction not found'));
// Transaction is not found
} else if (error) {
convertErrors(callback)(error);
} else if (!tx) {

View File

@@ -71,7 +71,7 @@ function getAccountTx(remote, address, options, marker, limit, callback) {
const params = {
account: address,
ledger_index_min: options.minLedgerVersion || -1,
ledger_index_max: options.maxLedgerVersion || -1,
ledger_index_max: options.maxLedgerVersion || remote.getLedgerSequence(),
forward: options.earliestFirst,
binary: options.binary,
limit: utils.clamp(limit, 10, 400),

View File

@@ -99,12 +99,19 @@ function compareTransactions(first: Outcome, second: Outcome): number {
function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number,
maxLedgerVersion?: number
): boolean {
const firstLedgerVersion = 32570; // earlier versions have been lost
return remote.getServer().hasLedgerRange(
minLedgerVersion || firstLedgerVersion,
maxLedgerVersion || remote.getLedgerSequence());
}
function isPendingLedgerVersion(remote: Remote, maxLedgerVersion: ?number
): boolean {
const currentLedger = remote.getLedgerSequence();
return currentLedger < (maxLedgerVersion || 0);
}
module.exports = {
getXRPBalance,
compareTransactions,
@@ -112,6 +119,7 @@ module.exports = {
renameCounterpartyToIssuerInOrder,
getRecursive,
hasCompleteLedgerRange,
isPendingLedgerVersion,
promisify: common.promisify,
clamp: clamp,
common: common

View File

@@ -87,11 +87,30 @@ function getServerInfo(): Promise<GetServerInfoResponse> {
return common.promisify(getServerInfoAsync.bind(this))();
}
function rippleTimeToISO8601(rippleTime: string): string {
return new Date(common.core.utils.toTimestamp(rippleTime)).toISOString();
}
function formatLedgerClose(ledgerClose: Object): Object {
return {
feeBase: ledgerClose.fee_base,
feeReference: ledgerClose.fee_ref,
ledgerHash: ledgerClose.ledger_hash,
ledgerVersion: ledgerClose.ledger_index,
ledgerTimestamp: rippleTimeToISO8601(ledgerClose.ledger_time),
reserveBase: ledgerClose.reserve_base,
reserveIncrement: ledgerClose.reserve_inc,
transactionCount: ledgerClose.txn_count,
validatedLedgerVersions: ledgerClose.validated_ledgers
};
}
module.exports = {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion
getLedgerVersion,
formatLedgerClose
};

View File

@@ -1,10 +1,31 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Request = utils.common.core.Request;
const convertErrors = utils.common.convertErrors;
function isImmediateRejection(engineResult) {
// note: "tel" errors mean the local server refused to process the
// transaction *at that time*, but it could potentially buffer the
// transaction and then process it at a later time, for example
// if the required fee changes (this does not occur at the time of
// this writing, but it could change in the future)
// all other error classes can potentially result in transcation validation
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
}
function convertSubmitErrors(callback) {
return function(error, data) {
if (isImmediateRejection(data.engineResult)) {
callback(new utils.common.errors.RippleError('Submit failed'), data);
} else {
callback(error, data);
}
};
}
function submitAsync(txBlob: string, callback: (err: any, data: any) => void
): void {
validate.blob(txBlob);
@@ -13,7 +34,7 @@ function submitAsync(txBlob: string, callback: (err: any, data: any) => void
request.request(null,
utils.common.composeAsync(
data => utils.common.convertKeysFromSnakeCaseToCamelCase(data),
convertErrors(callback)));
convertSubmitErrors(convertErrors(callback))));
}
function submit(txBlob: string) {

View File

@@ -0,0 +1,40 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentCancellationTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentCancellation(payment);
const transaction = new Transaction();
transaction.suspendedPaymentCancel({
account: account,
owner: payment.owner,
paymentSequence: payment.paymentSequence
});
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentCancellationAsync(account, payment,
instructions, callback) {
const transaction =
createSuspendedPaymentCancellationTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentCancellation(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentCancellationAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentCancellation;

View File

@@ -0,0 +1,57 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentCreationTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentCreation(payment);
const transaction = new Transaction();
transaction.suspendedPaymentCreate({
account: account,
destination: payment.destination.address,
amount: toRippledAmount(payment.destination.amount)
});
if (payment.digest) {
transaction.setDigest(payment.digest);
}
if (payment.allowCancelAfter) {
transaction.setAllowCancelAfter(payment.allowCancelAfter);
}
if (payment.allowExecuteAfter) {
transaction.setAllowExecuteAfter(payment.allowExecuteAfter);
}
if (payment.source.tag) {
transaction.sourceTag(payment.source.tag);
}
if (payment.destination.tag) {
transaction.destinationTag(payment.destination.tag);
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentCreationAsync(account, payment, instructions,
callback) {
const transaction =
createSuspendedPaymentCreationTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentCreation(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentCreationAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentCreation;

View File

@@ -0,0 +1,50 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createSuspendedPaymentExecutionTransaction(account, payment) {
validate.address(account);
validate.suspendedPaymentExecution(payment);
const transaction = new Transaction();
transaction.suspendedPaymentFinish({
account: account,
owner: payment.owner,
paymentSequence: payment.paymentSequence
});
if (payment.method) {
transaction.setMethod(payment.method);
}
if (payment.digest) {
transaction.setDigest(payment.digest);
}
if (payment.proof) {
transaction.setProof(payment.proof);
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
return transaction;
}
function prepareSuspendedPaymentExecutionAsync(account, payment, instructions,
callback) {
const transaction =
createSuspendedPaymentExecutionTransaction(account, payment);
utils.prepareTransaction(transaction, this.remote, instructions, callback);
}
function prepareSuspendedPaymentExecution(account: string, payment: Object,
instructions = {}) {
return utils.promisify(prepareSuspendedPaymentExecutionAsync)
.call(this, account, payment, instructions);
}
module.exports = prepareSuspendedPaymentExecution;

View File

@@ -105,6 +105,19 @@ Amount.NaN = function() {
return result; // but let's be careful
};
Amount.from_components_unsafe = function(value: Value, currency: Currency,
issuer: UInt160, isNative: boolean
) {
const result = new Amount(value);
result._is_native = isNative;
result._currency = currency;
result._issuer = issuer;
result._value = value.isZero() && value.isNegative() ?
value.negate() : value;
return result;
};
// be sure that _is_native is set properly BEFORE calling _set_value
Amount.prototype._set_value = function(value: Value) {
@@ -122,27 +135,30 @@ Amount.prototype.abs = function() {
};
Amount.prototype.add = function(addend) {
const addendAmount = Amount.from_json(addend);
const addendAmount = addend instanceof Amount ?
addend : Amount.from_json(addend);
if (!this.is_comparable(addendAmount)) {
return new Amount();
}
return this._copy(this._value.add(addendAmount._value));
};
Amount.prototype.subtract = function(subtrahend) {
// Correctness over speed, less code has less bugs, reuse add code.
return this.add(Amount.from_json(subtrahend).negate());
const subsAmount = subtrahend instanceof Amount ?
subtrahend : Amount.from_json(subtrahend);
return this.add(subsAmount.negate());
};
// XXX Diverges from cpp.
Amount.prototype.multiply = function(multiplicand) {
const multiplicandValue = multiplicand instanceof Amount ?
multiplicand._value :
Amount.from_json(multiplicand)._value;
const multiplicandAmount = Amount.from_json(multiplicand);
return this._copy(this._value.multiply(multiplicandAmount._value));
return this._copy(this._value.multiply(multiplicandValue));
};
@@ -151,9 +167,11 @@ Amount.prototype.scale = function(scaleFactor) {
};
Amount.prototype.divide = function(divisor) {
const divisorAmount = Amount.from_json(divisor);
const divisorValue = divisor instanceof Amount ?
divisor._value :
Amount.from_json(divisor)._value;
return this._copy(this._value.divide(divisorAmount._value));
return this._copy(this._value.divide(divisorValue));
};
/**
@@ -344,7 +362,7 @@ Amount.prototype._check_limits = function() {
};
Amount.prototype.clone = function(negate) {
return this.copyTo(new Amount(), negate);
return this.copyTo(new Amount(this._value), negate);
};
Amount.prototype._copy = function(value) {
@@ -354,9 +372,10 @@ Amount.prototype._copy = function(value) {
};
Amount.prototype.compareTo = function(to) {
const toAmount = Amount.from_json(to);
const toAmount = to instanceof Amount ? to : Amount.from_json(to);
if (!this.is_comparable(toAmount)) {
return new Amount();
throw new Error('Not comparable');
}
return this._value.comparedTo(toAmount._value);
};
@@ -730,6 +749,14 @@ Amount.prototype.to_number = function() {
return Number(this.to_text());
};
// this one is needed because Value.abs creates new BigNumber,
// and BigNumber constructor is very slow, so we want to
// call it only if absolutely necessary
function absValue(value: Value): Value {
return value.isNegative() ? value.abs() : value;
}
// Convert only value to JSON wire format.
Amount.prototype.to_text = function() {
if (!this.is_valid()) {
@@ -743,8 +770,8 @@ Amount.prototype.to_text = function() {
// not native
const offset = this._value.getExponent() - 15;
const sign = this._value.isNegative() ? '-' : '';
const mantissa = utils.getMantissa16FromString(
this._value.abs().toString());
const mantissa =
utils.getMantissa16FromString(absValue(this._value).toString());
if (offset !== 0 && (offset < -25 || offset > -4)) {
// Use e notation.
// XXX Clamp output.

View File

@@ -18,17 +18,24 @@ function assertValidLegOneOffer(legOneOffer, message) {
}
function AutobridgeCalculator(currencyGets, currencyPays,
legOneOffers, legTwoOffers, issuerGets, issuerPays) {
legOneOffers, legTwoOffers, issuerGets, issuerPays
) {
this._currencyGets = currencyGets;
this._currencyPays = currencyPays;
this._currencyGetsHex = currencyGets.to_hex();
this._currencyPaysHex = currencyPays.to_hex();
this._issuerGets = issuerGets;
this._issuerGetsObj = UInt160.from_json(issuerGets);
this._issuerPays = issuerPays;
this._issuerPaysObj = UInt160.from_json(issuerPays);
this.legOneOffers = _.cloneDeep(legOneOffers);
this.legTwoOffers = _.cloneDeep(legTwoOffers);
this._ownerFundsLeftover = {};
}
const NULL_AMOUNT = Utils.normalizeAmount('0');
/**
* Calculates an ordered array of autobridged offers by quality
*
@@ -36,10 +43,13 @@ function AutobridgeCalculator(currencyGets, currencyPays,
*/
AutobridgeCalculator.prototype.calculate = function() {
const oldMode = Amount.strict_mode;
Amount.strict_mode = false;
let legOnePointer = 0;
let legTwoPointer = 0;
let offersAutobridged = [];
const offersAutobridged = [];
this.clearOwnerFundsLeftover();
@@ -55,8 +65,10 @@ AutobridgeCalculator.prototype.calculate = function() {
this.adjustLegOneFundedAmount(legOneOffer);
}
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
if (legOneTakerGetsFunded.is_zero()) {
legOnePointer++;
@@ -70,14 +82,17 @@ AutobridgeCalculator.prototype.calculate = function() {
continue;
}
if (legOneTakerGetsFunded.compareTo(legTwoTakerPaysFunded) > 0) {
// using private fields for speed
if (legOneTakerGetsFunded._value.comparedTo(
legTwoTakerPaysFunded._value) > 0) {
autobridgedOffer = this.getAutobridgedOfferWithClampedLegOne(
legOneOffer,
legTwoOffer
);
legTwoPointer++;
} else if (legTwoTakerPaysFunded.compareTo(legOneTakerGetsFunded) > 0) {
} else if (legTwoTakerPaysFunded._value.comparedTo(
legOneTakerGetsFunded._value) > 0) {
autobridgedOffer = this.getAutobridgedOfferWithClampedLegTwo(
legOneOffer,
legTwoOffer
@@ -97,6 +112,7 @@ AutobridgeCalculator.prototype.calculate = function() {
offersAutobridged.push(autobridgedOffer);
}
Amount.strict_mode = oldMode;
return offersAutobridged;
};
@@ -112,15 +128,20 @@ AutobridgeCalculator.prototype.calculate = function() {
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne =
function(legOneOffer, legTwoOffer) {
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
this._currencyPays, this._issuerPaysObj);
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality);
if (legOneOffer.Account === legTwoOffer.Account) {
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded);
this.setLegOneTakerGets(legOneOffer, updatedTakerGets);
@@ -152,15 +173,20 @@ function(legOneOffer, legTwoOffer) {
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo =
function(legOneOffer, legTwoOffer) {
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets);
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets,
this._currencyGets, this._issuerGetsObj);
const autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
// Update funded amount since leg two offer was not completely consumed
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer)
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj)
.subtract(autobridgedTakerGets)
.to_text();
legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded
@@ -184,8 +210,10 @@ function(legOneOffer, legTwoOffer) {
AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps =
function(legOneOffer, legTwoOffer) {
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
return this.formatAutobridgedOffer(
autobridgedTakerGets,
@@ -210,9 +238,7 @@ AutobridgeCalculator.prototype.clearOwnerFundsLeftover = function() {
*/
AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
this._ownerFundsLeftover[account] = Utils.normalizeAmount('0');
this._ownerFundsLeftover[account] = NULL_AMOUNT.clone();
return this._ownerFundsLeftover[account];
};
@@ -226,12 +252,10 @@ AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
*/
AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
let amount = this._ownerFundsLeftover[account];
if (!amount) {
amount = Utils.normalizeAmount('0');
amount = NULL_AMOUNT.clone();
}
return amount;
@@ -248,7 +272,6 @@ AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
AutobridgeCalculator.prototype.addLeftoverOwnerFunds =
function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(amount instanceof Amount, 'Amount is invalid');
this._ownerFundsLeftover[account] = this.getLeftoverOwnerFunds(account)
@@ -266,7 +289,6 @@ function(account, amount) {
AutobridgeCalculator.prototype.setLeftoverOwnerFunds =
function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(amount instanceof Amount, 'Amount is invalid');
this._ownerFundsLeftover[account] = amount;
@@ -291,13 +313,13 @@ function(takerGets, takerPays) {
autobridgedOffer.TakerGets = {
value: takerGets.to_text(),
currency: this._currencyGets.to_hex(),
currency: this._currencyGetsHex,
issuer: this._issuerGets
};
autobridgedOffer.TakerPays = {
value: takerPays.to_text(),
currency: this._currencyPays.to_hex(),
currency: this._currencyPaysHex,
issuer: this._issuerPays
};
@@ -308,7 +330,9 @@ function(takerGets, takerPays) {
autobridgedOffer.autobridged = true;
autobridgedOffer.BookDirectory = Utils.convertOfferQualityToHex(quality);
autobridgedOffer.BookDirectory =
Utils.convertOfferQualityToHexFromText(autobridgedOffer.quality);
autobridgedOffer.qualityHex = autobridgedOffer.BookDirectory;
return autobridgedOffer;
};
@@ -325,11 +349,13 @@ function(takerGets, takerPays) {
AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
this.setLegOneTakerGetsFunded(
legOneOffer,
Utils.getOfferTakerGets(legOneOffer)
Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
this._issuerPaysObj)
);
};
@@ -350,7 +376,8 @@ AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
const takerGets = Utils.getOfferTakerGets(legOneOffer);
const takerGets = Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
this._issuerPaysObj);
if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) {
// After clamping, TakerGets is still greater than initial funded amount
@@ -375,12 +402,16 @@ function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded');
const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer)
const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj)
.add(this.getLeftoverOwnerFunds(legOneOffer.Account));
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer)) >= 0) {
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj)) >= 0
) {
// There are enough extra funds to fully fund the offer
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const updatedLeftover = fundedSum.subtract(legOneTakerGets);
this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets);
@@ -407,8 +438,9 @@ function setLegOneTakerGetsFunded(legOneOffer, takerGetsFunded) {
legOneOffer.taker_gets_funded = takerGetsFunded.to_text();
legOneOffer.taker_pays_funded = takerGetsFunded
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets))
.to_text();
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets,
this._currencyPays, this._issuerPaysObj))
.to_text();
if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) {
legOneOffer.is_fully_funded = true;
@@ -428,10 +460,11 @@ function(legOneOffer, takerGets) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(takerGets instanceof Amount, 'Taker gets funded is invalid');
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
this._currencyPays, this._issuerPaysObj);
legOneOffer.TakerGets = takerGets.to_text();
legOneOffer.TakerPays = takerGets.multiply(legOneQuality);
legOneOffer.TakerPays = takerGets.multiply(legOneQuality).to_json();
};
module.exports = AutobridgeCalculator;

View File

@@ -1,28 +1,41 @@
'use strict';
function normalize(digitArray) {
while (digitArray[0] === 0) {
digitArray.shift();
let i = 0;
while (digitArray[i] === 0) {
++i;
}
if (i > 0) {
digitArray.splice(0, i);
}
return digitArray;
}
function divmod(digitArray, base, divisor) {
var remainder = 0;
var quotient = [];
for (var j = 0; j < digitArray.length; j++) {
var temp = remainder * base + parseInt(digitArray[j], 10);
quotient.push(Math.floor(temp / divisor));
let remainder = 0;
let temp;
let divided;
let j = -1;
const length = digitArray.length;
const quotient = new Array(length);
while (++j < length) {
temp = remainder * base + digitArray[j];
divided = temp / divisor;
quotient[j] = divided << 0;
remainder = temp % divisor;
}
return {quotient: normalize(quotient), remainder: remainder};
}
function convertBase(digitArray, fromBase, toBase) {
var result = [];
var dividend = digitArray;
const result = [];
let dividend = digitArray;
let qr;
while (dividend.length > 0) {
var qr = divmod(dividend, fromBase, toBase);
qr = divmod(dividend, fromBase, toBase);
result.unshift(qr.remainder);
dividend = qr.quotient;
}

View File

@@ -1,6 +1,8 @@
'use strict';
/*eslint no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
/*eslint-disable max-len,spaced-comment,array-bracket-spacing,key-spacing*/
/*eslint-disable no-multi-spaces,comma-spacing*/
/*eslint-disable no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
/**
* Data type map.
@@ -52,7 +54,8 @@ const FIELDS_MAP = exports.fields = {
// Common types
1: { // Int16
1: 'LedgerEntryType',
2: 'TransactionType'
2: 'TransactionType',
3: 'SignerWeight'
},
2: { // Int32
2: 'Flags',
@@ -87,7 +90,11 @@ const FIELDS_MAP = exports.fields = {
31: 'ReserveBase',
32: 'ReserveIncrement',
33: 'SetFlag',
34: 'ClearFlag'
34: 'ClearFlag',
35: 'SignerQuorum',
36: 'CancelAfter',
37: 'FinishAfter',
38: 'SignerListID'
},
3: { // Int64
1: 'IndexNext',
@@ -116,7 +123,8 @@ const FIELDS_MAP = exports.fields = {
17: 'InvoiceID',
18: 'Nickname',
19: 'Amendment',
20: 'TicketID'
20: 'TicketID',
21: 'Digest'
},
6: { // Amount
1: 'Amount',
@@ -146,7 +154,8 @@ const FIELDS_MAP = exports.fields = {
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData',
14: 'MemoFormat'
14: 'MemoFormat',
17: 'Proof'
},
8: { // Account
1: 'Account',
@@ -166,13 +175,15 @@ const FIELDS_MAP = exports.fields = {
7: 'FinalFields',
8: 'NewFields',
9: 'TemplateEntry',
10: 'Memo'
10: 'Memo',
11: 'SignerEntry',
16: 'Signer'
},
15: { // Array
1: undefined, // end of Array
2: 'SigningAccounts',
3: 'TxnSignatures',
4: 'Signatures',
3: 'Signers',
4: 'SignerEntries',
5: 'Template',
6: 'Necessary',
7: 'Sufficient',
@@ -183,7 +194,7 @@ const FIELDS_MAP = exports.fields = {
// Uncommon types
16: { // Int8
1: 'CloseResolution',
2: 'TemplateEntryType',
2: 'Method',
3: 'TransactionResult'
},
17: { // Hash160
@@ -202,17 +213,17 @@ const FIELDS_MAP = exports.fields = {
}
};
let INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
const INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
Object.keys(FIELDS_MAP).forEach(function(k1) {
Object.keys(FIELDS_MAP[k1]).forEach(function(k2) {
INVERSE_FIELDS_MAP[FIELDS_MAP[k1][k2]] = [ Number(k1), Number(k2) ];
INVERSE_FIELDS_MAP[FIELDS_MAP[k1][k2]] = [Number(k1), Number(k2)];
});
});
const REQUIRED = exports.REQUIRED = 0,
OPTIONAL = exports.OPTIONAL = 1,
DEFAULT = exports.DEFAULT = 2;
const REQUIRED = exports.REQUIRED = 0;
const OPTIONAL = exports.OPTIONAL = 1;
const DEFAULT = exports.DEFAULT = 2;
const base = [
[ 'TransactionType' , REQUIRED ],
@@ -226,76 +237,100 @@ const base = [
[ 'SigningPubKey' , REQUIRED ],
[ 'TxnSignature' , OPTIONAL ],
[ 'AccountTxnID' , OPTIONAL ],
[ 'Memos' , OPTIONAL ]
[ 'Memos' , OPTIONAL ],
[ 'Signers' , OPTIONAL ]
];
exports.tx = {
AccountSet: [3].concat(base, [
[ 'EmailHash' , OPTIONAL ],
[ 'WalletLocator' , OPTIONAL ],
[ 'WalletSize' , OPTIONAL ],
[ 'MessageKey' , OPTIONAL ],
[ 'Domain' , OPTIONAL ],
[ 'TransferRate' , OPTIONAL ],
[ 'SetFlag' , OPTIONAL ],
[ 'ClearFlag' , OPTIONAL ]
['EmailHash' , OPTIONAL],
['WalletLocator' , OPTIONAL],
['WalletSize' , OPTIONAL],
['MessageKey' , OPTIONAL],
['Domain' , OPTIONAL],
['TransferRate' , OPTIONAL],
['SetFlag' , OPTIONAL],
['ClearFlag' , OPTIONAL]
]),
TrustSet: [20].concat(base, [
[ 'LimitAmount' , OPTIONAL ],
[ 'QualityIn' , OPTIONAL ],
[ 'QualityOut' , OPTIONAL ]
['LimitAmount' , OPTIONAL],
['QualityIn' , OPTIONAL],
['QualityOut' , OPTIONAL]
]),
OfferCreate: [7].concat(base, [
[ 'TakerPays' , REQUIRED ],
[ 'TakerGets' , REQUIRED ],
[ 'Expiration' , OPTIONAL ],
[ 'OfferSequence' , OPTIONAL ]
['TakerPays' , REQUIRED],
['TakerGets' , REQUIRED],
['Expiration' , OPTIONAL],
['OfferSequence' , OPTIONAL]
]),
OfferCancel: [8].concat(base, [
[ 'OfferSequence' , REQUIRED ]
['OfferSequence' , REQUIRED]
]),
SetRegularKey: [5].concat(base, [
[ 'RegularKey' , OPTIONAL ]
['RegularKey' , OPTIONAL]
]),
Payment: [0].concat(base, [
[ 'Destination' , REQUIRED ],
[ 'Amount' , REQUIRED ],
[ 'SendMax' , OPTIONAL ],
[ 'Paths' , DEFAULT ],
[ 'InvoiceID' , OPTIONAL ],
[ 'DestinationTag' , OPTIONAL ]
['Destination' , REQUIRED],
['Amount' , REQUIRED],
['SendMax' , OPTIONAL],
['Paths' , DEFAULT],
['InvoiceID' , OPTIONAL],
['DestinationTag' , OPTIONAL]
]),
Contract: [9].concat(base, [
[ 'Expiration' , REQUIRED ],
[ 'BondAmount' , REQUIRED ],
[ 'StampEscrow' , REQUIRED ],
[ 'RippleEscrow' , REQUIRED ],
[ 'CreateCode' , OPTIONAL ],
[ 'FundCode' , OPTIONAL ],
[ 'RemoveCode' , OPTIONAL ],
[ 'ExpireCode' , OPTIONAL ]
['Expiration' , REQUIRED],
['BondAmount' , REQUIRED],
['StampEscrow' , REQUIRED],
['RippleEscrow' , REQUIRED],
['CreateCode' , OPTIONAL],
['FundCode' , OPTIONAL],
['RemoveCode' , OPTIONAL],
['ExpireCode' , OPTIONAL]
]),
RemoveContract: [10].concat(base, [
[ 'Target' , REQUIRED ]
['Target' , REQUIRED]
]),
EnableFeature: [100].concat(base, [
[ 'Feature' , REQUIRED ]
['Feature' , REQUIRED]
]),
EnableAmendment: [100].concat(base, [
[ 'Amendment' , REQUIRED ]
['Amendment' , REQUIRED]
]),
SetFee: [101].concat(base, [
[ 'BaseFee' , REQUIRED ],
[ 'ReferenceFeeUnits' , REQUIRED ],
[ 'ReserveBase' , REQUIRED ],
[ 'ReserveIncrement' , REQUIRED ]
['BaseFee' , REQUIRED],
['ReferenceFeeUnits' , REQUIRED],
['ReserveBase' , REQUIRED],
['ReserveIncrement' , REQUIRED]
]),
TicketCreate: [10].concat(base, [
[ 'Target' , OPTIONAL ],
[ 'Expiration' , OPTIONAL ]
['Target' , OPTIONAL],
['Expiration' , OPTIONAL]
]),
TicketCancel: [11].concat(base, [
[ 'TicketID' , REQUIRED ]
['TicketID' , REQUIRED]
]),
SignerListSet: [12].concat(base, [
['SignerQuorum', REQUIRED],
['SignerEntries', OPTIONAL]
]),
SuspendedPaymentCreate: [1].concat(base, [
['Destination' , REQUIRED],
['Amount' , REQUIRED],
['Digest' , OPTIONAL],
['CancelAfter' , OPTIONAL],
['FinishAfter' , OPTIONAL],
['DestinationTag' , OPTIONAL]
]),
SuspendedPaymentFinish: [2].concat(base, [
['Owner' , REQUIRED],
['OfferSequence' , REQUIRED],
['Method' , OPTIONAL],
['Digest' , OPTIONAL],
['Proof' , OPTIONAL]
]),
SuspendedPaymentCancel: [4].concat(base, [
['Owner' , REQUIRED],
['OfferSequence' , REQUIRED]
])
};
@@ -396,14 +431,22 @@ exports.ledger = {
['LedgerIndex', OPTIONAL],
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]])
['HighLimit', REQUIRED]]),
SignerList: [83].concat(sleBase,[
['OwnerNode', REQUIRED],
['SignerQuorum', REQUIRED],
['SignerEntries', REQUIRED],
['SignerListID', REQUIRED],
['PreviousTxnID', REQUIRED],
['PreviousTxnLgrSeq', REQUIRED]
])
};
exports.metadata = [
[ 'DeliveredAmount' , OPTIONAL ],
[ 'TransactionIndex' , REQUIRED ],
[ 'TransactionResult' , REQUIRED ],
[ 'AffectedNodes' , REQUIRED ]
['DeliveredAmount' , OPTIONAL],
['TransactionIndex' , REQUIRED],
['TransactionResult' , REQUIRED],
['AffectedNodes' , REQUIRED]
];
exports.ter = {
@@ -423,7 +466,7 @@ exports.ter = {
tecNO_LINE_REDUNDANT : 127,
tecPATH_DRY : 128,
tecUNFUNDED : 129, // Deprecated, old ambiguous unfunded.
tecMASTER_DISABLED : 130,
tecNO_ALTERNATIVE_KEY : 130,
tecNO_REGULAR_KEY : 131,
tecOWNERS : 132,
tecNO_ISSUER : 133,
@@ -437,5 +480,6 @@ exports.ter = {
tecINSUFFICIENT_RESERVE : 141,
tecNEED_MASTER_KEY : 142,
tecDST_TAG_NEEDED : 143,
tecINTERNAL : 144
tecINTERNAL : 144,
tecOVERSIZE : 145
};

View File

@@ -1,15 +1,15 @@
'use strict';
var extend = require('extend');
var UInt160 = require('./uint160').UInt160;
var utils = require('./utils');
var Float = require('./ieee754').Float;
const extend = require('extend');
const UInt160 = require('./uint160').UInt160;
const utils = require('./utils');
const Float = require('./ieee754').Float;
//
// Currency support
//
var Currency = extend(function() {
const Currency = extend(function() {
// Internal form: 0 = XRP. 3 letter-code.
// XXX Internal should be 0 or hex with three letter annotation when valid.
@@ -22,7 +22,7 @@ var Currency = extend(function() {
this._update();
}, UInt160);
Currency.prototype = extend({}, UInt160.prototype);
Currency.prototype = Object.create(extend({}, UInt160.prototype));
Currency.prototype.constructor = Currency;
Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
@@ -61,12 +61,12 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
*
*/
/*eslint-disable max-len*/
/* eslint-disable max-len*/
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
/*eslint-enable max-len*/
/* eslint-enable max-len*/
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
};
Currency.from_human = function(j, opts) {
@@ -78,7 +78,7 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
this._value = NaN;
if (j instanceof Currency) {
this._value = j.copyTo({})._value;
this._value = j._value;
this._update();
return this;
}
@@ -111,10 +111,10 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
}
// match the given string to see if it's in an allowed format
var matches = j.match(this.human_RE);
const matches = j.match(this.human_RE);
if (matches) {
var currencyCode = matches[1];
let currencyCode = matches[1];
// for the currency 'XRP' case
// we drop everything else that could have been provided
@@ -131,14 +131,14 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
// the full currency is matched as it is part of the valid currency
// format, but not stored
// var full_currency = matches[2] || '';
var interest = matches[3] || '';
const interest = matches[3] || '';
// interest is defined as interest per year, per annum (pa)
var percentage = interest.match(/(-?\d+\.?\d+)/);
let percentage = interest.match(/(-?\d+\.?\d+)/);
currencyCode = currencyCode.toUpperCase();
var currencyData = utils.arraySet(20, 0);
const currencyData = utils.arraySet(20, 0);
if (percentage) {
/*
@@ -164,15 +164,15 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
// the interest or demurrage is expressed as a yearly (per annum)
// value
var secondsPerYear = 31536000; // 60 * 60 * 24 * 365
const secondsPerYear = 31536000; // 60 * 60 * 24 * 365
// Calculating the interest e-fold
// 0.5% demurrage is expressed 0.995, 0.005 less than 1
// 0.5% interest is expressed as 1.005, 0.005 more than 1
var interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
var bytes = Float.toIEEE754Double(interestEfold);
const interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
const bytes = Float.toIEEE754Double(interestEfold);
for (var i = 0; i <= bytes.length; i++) {
for (let i = 0; i <= bytes.length; i++) {
currencyData[8 + i] = bytes[i] & 0xff;
}
@@ -204,10 +204,10 @@ Currency.prototype.parse_human = function(j) {
*/
Currency.prototype._update = function() {
var bytes = this.to_bytes();
const bytes = this.to_bytes();
// is it 0 everywhere except 12, 13, 14?
var isZeroExceptInStandardPositions = true;
let isZeroExceptInStandardPositions = true;
if (!bytes) {
return;
@@ -219,7 +219,7 @@ Currency.prototype._update = function() {
this._interest_period = NaN;
this._iso_code = '';
for (var i = 0; i < 20; i++) {
for (let i = 0; i < 20; i++) {
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
&& (i === 12 || i === 13 || i === 14 || bytes[i] === 0);
}
@@ -249,6 +249,34 @@ Currency.prototype._update = function() {
}
};
/**
* Returns copy.
*
* This copies code from UInt.copyTo so we do not call _update,
* bvecause to_bytes is very expensive.
*/
Currency.prototype.copyTo = function(d) {
d._value = this._value;
if (this._version_byte !== undefined) {
d._version_byte = this._version_byte;
}
if (!d.is_valid()) {
return d;
}
d._native = this._native;
d._type = this._type;
d._interest_start = this._interest_start;
d._interest_period = this._interest_period;
d._iso_code = this._iso_code;
return d;
};
// XXX Probably not needed anymore?
/*
Currency.prototype.parse_bytes = function(byte_array) {
@@ -300,18 +328,20 @@ Currency.prototype.has_interest = function() {
/**
*
* @param {number} referenceDate number of seconds since the Ripple Epoch
* @param {number} referenceDate_ number of seconds since the Ripple Epoch
* (0:00 on January 1, 2000 UTC) used to calculate the
* interest over provided interval pass in one years
* worth of seconds to ge the yearly interest
* @returns {number} interest for provided interval, can be negative for
* demurred currencies
*/
Currency.prototype.get_interest_at = function(referenceDate) {
Currency.prototype.get_interest_at = function(referenceDate_) {
if (!this.has_interest()) {
return 0;
}
let referenceDate = referenceDate_;
// use one year as a default period
if (!referenceDate) {
referenceDate = this._interest_start + 3600 * 24 * 365;
@@ -326,13 +356,14 @@ Currency.prototype.get_interest_at = function(referenceDate) {
/ this._interest_period);
};
Currency.prototype.get_interest_percentage_at
= function(referenceDate, decimals) {
var interest = this.get_interest_at(referenceDate, decimals);
Currency.prototype.get_interest_percentage_at = function(referenceDate,
decimals
) {
let interest = this.get_interest_at(referenceDate, decimals);
// convert to percentage
interest = (interest * 100) - 100;
var decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
const decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
// round to two decimals behind the dot
return Math.round(interest * decimalMultiplier) / decimalMultiplier;
@@ -347,18 +378,14 @@ Currency.prototype.get_interest_percentage_at
// return UInt.prototype.is_valid() && ...;
// };
Currency.prototype.to_json = function(opts) {
Currency.prototype.to_json = function(opts = {}) {
if (!this.is_valid()) {
// XXX This is backwards compatible behavior, but probably not very good.
return 'XRP';
}
if (!opts) {
opts = {};
}
var currency;
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
let currency;
const fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
opts.show_interest = opts.show_interest !== undefined
? opts.show_interest
: this.has_interest();
@@ -366,8 +393,8 @@ Currency.prototype.to_json = function(opts) {
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
currency = this._iso_code + fullName;
if (opts.show_interest) {
var decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
var interestPercentage = this.has_interest()
const decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
const interestPercentage = this.has_interest()
? this.get_interest_percentage_at(
this._interest_start + 3600 * 24 * 365, decimals
)

View File

@@ -30,6 +30,8 @@ exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
exports.HASH_TX_SIGN = 0x53545800; // 'STX'
// inner transaction to sign (TESTNET)
exports.HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
// inner transaction to multisign
exports.HASH_TX_MULTISIGN = 0x534D5400; // 'SMT'
Object.keys(exports).forEach(function(k) {
exports[k + '_BYTES'] = toBytes(exports[k]);

View File

@@ -21,10 +21,12 @@ exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
exports.convertBase = require('./baseconverter');
exports._test = {
IOUValue: require('./iouvalue').IOUValue,
Log: require('./log'),
PathFind: require('./pathfind').PathFind,
TransactionManager: require('./transactionmanager').TransactionManager,
RangeSet: require('./rangeset').RangeSet
RangeSet: require('./rangeset').RangeSet,
HashPrefixes: require('./hashprefixes')
};
exports.types = require('./serializedtypes');

View File

@@ -24,8 +24,15 @@ const OrderBookUtils = require('./orderbookutils');
const log = require('./log').internal.sub('orderbook');
const IOUValue = require('./iouvalue').IOUValue;
function assertValidNumber(number, message) {
assert(!_.isNull(number) && !isNaN(number), message);
function _sortOffers(a, b) {
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);
const bQuality = OrderBookUtils.getOfferQuality(b, this._currencyGets);
return aQuality._value.comparedTo(bQuality._value);
}
function _sortOffersQuick(a, b) {
return a.qualityHex.localeCompare(b.qualityHex);
}
/**
@@ -36,11 +43,13 @@ function assertValidNumber(number, message) {
* @param {String} bid currency
* @param {String} bid issuer
* @param {String} orderbook key
* @param {Boolean} fire 'model' event after receiving transaction
only once in 10 seconds
*/
function OrderBook(remote,
currencyGets, issuerGets, currencyPays, issuerPays,
key) {
currencyGets, issuerGets, currencyPays, issuerPays, key
) {
EventEmitter.call(this);
const self = this;
@@ -61,6 +70,8 @@ function OrderBook(remote,
this._ownerFundsUnadjusted = {};
this._ownerFunds = {};
this._ownerOffersTotal = {};
this._validAccounts = {};
this._validAccountsCount = 0;
// We consider ourselves synced if we have a current
// copy of the offers, we are online and subscribed to updates
@@ -73,13 +84,44 @@ function OrderBook(remote,
// books that we must keep track of to compute autobridged offers
this._legOneBook = null;
this._legTwoBook = null;
this._gotOffersFromLegOne = false;
this._gotOffersFromLegTwo = false;
this.sortOffers = this._currencyGets.has_interest() ?
_sortOffers.bind(this) : _sortOffersQuick;
this.notifyDirectOffersChanged =
_.debounce(
this.notifyDirectOffersChangedInternal,
OrderBook.NOTIFY_TIMEOUT,
{maxWait: OrderBook.NOTIFY_MAXWAIT});
this._isAutobridgeable = !this._currencyGets.is_native()
&& !this._currencyPays.is_native();
function computeAutobridgedOffersWrapper() {
self.computeAutobridgedOffers();
self.mergeDirectAndAutobridgedBooks();
this._autobridgeThrottleTimeMultiplier = 1;
this.createDebouncedOffersWrapper();
function computeAutobridgedOffersWrapperOne() {
self._gotOffersFromLegOne = true;
self.computeAutobridgedOffersThrottled();
}
function computeAutobridgedOffersWrapperTwo() {
self._gotOffersFromLegTwo = true;
self.computeAutobridgedOffersThrottled();
}
function onDisconnect() {
self.resetCache();
self._gotOffersFromLegOne = false;
self._gotOffersFromLegTwo = false;
if (!self._destroyed) {
self._remote.once('disconnect', onDisconnect);
self._remote.once('connect', function() {
self.subscribe();
});
}
}
if (this._isAutobridgeable) {
@@ -89,15 +131,15 @@ function OrderBook(remote,
issuer_pays: issuerPays
});
this._legOneBook.on('model', computeAutobridgedOffersWrapper);
this._legTwoBook = remote.createOrderBook({
currency_gets: currencyGets,
issuer_gets: issuerGets,
currency_pays: 'XRP'
});
}
this._legTwoBook.on('model', computeAutobridgedOffersWrapper);
function updateFundedAmountsWrapper(transaction) {
self.updateFundedAmounts(transaction);
}
function listenersModified(action, event) {
@@ -107,7 +149,16 @@ function OrderBook(remote,
switch (action) {
case 'add':
if (++self._listeners === 1) {
self._shouldSubscribe = true;
self.subscribe();
self._remote.on('transaction', updateFundedAmountsWrapper);
self._remote.once('disconnect', onDisconnect);
if (self._isAutobridgeable) {
self._legOneBook.on('model', computeAutobridgedOffersWrapperOne);
self._legTwoBook.on('model', computeAutobridgedOffersWrapperTwo);
}
}
break;
case 'remove':
@@ -119,10 +170,6 @@ function OrderBook(remote,
}
}
function updateFundedAmountsWrapper(transaction) {
self.updateFundedAmounts(transaction);
}
this.on('newListener', function(event) {
listenersModified('add', event);
});
@@ -131,23 +178,21 @@ function OrderBook(remote,
listenersModified('remove', event);
});
this._remote.on('transaction', updateFundedAmountsWrapper);
this.on('unsubscribe', function() {
self.resetCache();
self._remote.removeListener('transaction', updateFundedAmountsWrapper);
});
self._remote.removeListener('disconnect', onDisconnect);
this._remote.once('prepare_subscribe', function() {
self.subscribe();
});
self._gotOffersFromLegOne = false;
self._gotOffersFromLegTwo = false;
this._remote.on('disconnect', function() {
self.resetCache();
self._remote.once('prepare_subscribe', function() {
self.subscribe();
});
if (self._isAutobridgeable) {
self._legOneBook.removeListener('model',
computeAutobridgedOffersWrapperOne);
self._legTwoBook.removeListener('model',
computeAutobridgedOffersWrapperTwo);
}
});
return this;
@@ -165,7 +210,21 @@ OrderBook.EVENTS = [
'offer_changed', 'offer_funds_changed'
];
OrderBook.DEFAULT_TRANSFER_RATE = 1000000000;
OrderBook.DEFAULT_TRANSFER_RATE = new IOUValue(1000000000);
OrderBook.NOTIFY_TIMEOUT = 100;
OrderBook.NOTIFY_MAXWAIT = 250;
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME = 1000;
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME = 250;
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT = 500;
OrderBook.ZERO_NATIVE_AMOUNT = Amount.from_json('0');
OrderBook.ZERO_NORMALIZED_AMOUNT = OrderBookUtils.normalizeAmount('0');
/**
* Normalize offers from book_offers and transaction stream
@@ -192,18 +251,20 @@ OrderBook.offerRewrite = function(offer) {
result.Flags = result.Flags || 0;
result.OwnerNode = result.OwnerNode || new Array(16 + 1).join('0');
result.BookNode = result.BookNode || new Array(16 + 1).join('0');
result.qualityHex = result.BookDirectory.slice(-16);
return result;
};
/**
* Initialize orderbook. Get orderbook offers and subscribe to transactions
* @api private
*/
OrderBook.prototype.subscribe = function() {
const self = this;
if (!this._shouldSubscribe) {
if (!this._shouldSubscribe || this._destroyed) {
return;
}
@@ -216,7 +277,7 @@ OrderBook.prototype.subscribe = function() {
self.requestTransferRate(callback);
},
function(callback) {
self.requestOffers(callback);
self.requestOffers(callback, true);
},
function(callback) {
self.subscribeTransactions(callback);
@@ -229,6 +290,7 @@ OrderBook.prototype.subscribe = function() {
/**
* Unhook event listeners and prevent ripple-lib from further work on this
* orderbook. There is no more orderbook stream, so "unsubscribe" is nominal
* @api private
*/
OrderBook.prototype.unsubscribe = function() {
@@ -250,33 +312,75 @@ OrderBook.prototype.unsubscribe = function() {
this.emit('unsubscribe');
};
/**
* After that you can't use this object.
*/
OrderBook.prototype.destroy = function() {
this._destroyed = true;
if (this._subscribed) {
this.unsubscribe();
}
if (this._remote._books.hasOwnProperty(this._key)) {
delete this._remote._books[this._key];
}
if (this._isAutobridgeable) {
this._legOneBook.destroy();
this._legTwoBook.destroy();
}
};
/**
* Request orderbook entries from server
*
* @param {Function} callback
*/
OrderBook.prototype.requestOffers = function(callback=function() {}) {
OrderBook.prototype.requestOffers = function(callback = function() {},
internal = false) {
const self = this;
if (!this._remote.isConnected()) {
// do not make request if not online.
// that requests will be queued and
// eventually all of them will fire back
return undefined;
}
if (!this._shouldSubscribe) {
return callback(new Error('Should not request offers'));
callback(new Error('Should not request offers'));
return undefined;
}
if (this._remote.trace) {
log.info('requesting offers', this._key);
}
if (this._isAutobridgeable && !internal) {
this._gotOffersFromLegOne = false;
this._gotOffersFromLegTwo = false;
this._legOneBook.requestOffers();
this._legTwoBook.requestOffers();
}
function handleOffers(res) {
if (self._destroyed) {
return;
}
if (!Array.isArray(res.offers)) {
// XXX What now?
return callback(new Error('Invalid response'));
callback(new Error('Invalid response'));
return;
}
if (self._remote.trace) {
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
}
self.setOffers(res.offers);
self.notifyDirectOffersChanged();
@@ -331,8 +435,10 @@ OrderBook.prototype.requestTransferRate = function(callback) {
// When transfer rate is not explicitly set on account, it implies the
// default transfer rate
self._issuerTransferRate = info.account_data.TransferRate ||
OrderBook.DEFAULT_TRANSFER_RATE;
self._issuerTransferRate =
info.account_data.TransferRate ?
new IOUValue(info.account_data.TransferRate) :
OrderBook.DEFAULT_TRANSFER_RATE;
callback(null, self._issuerTransferRate);
}
@@ -392,7 +498,7 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
* books, an additional merge step is also performed
*/
OrderBook.prototype.notifyDirectOffersChanged = function() {
OrderBook.prototype.notifyDirectOffersChangedInternal = function() {
if (this._isAutobridgeable) {
this.mergeDirectAndAutobridgedBooks();
} else {
@@ -409,6 +515,12 @@ OrderBook.prototype.resetCache = function() {
this._ownerOffersTotal = {};
this._offerCounts = {};
this._synced = false;
this._offers = [];
if (this._validAccountsCount > 3000) {
this._validAccounts = {};
this._validAccountsCount = 0;
}
};
/**
@@ -418,7 +530,6 @@ OrderBook.prototype.resetCache = function() {
*/
OrderBook.prototype.hasOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
return this._ownerFunds[account] !== undefined;
};
@@ -430,7 +541,6 @@ OrderBook.prototype.hasOwnerFunds = function(account) {
*/
OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(fundedAmount), 'Funded amount is invalid');
this._ownerFundsUnadjusted[account] = fundedAmount;
@@ -447,11 +557,10 @@ OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
OrderBook.prototype.applyTransferRate = function(balance) {
assert(!isNaN(balance), 'Balance is invalid');
assertValidNumber(this._issuerTransferRate, 'Transfer rate is invalid');
const adjustedBalance = (new IOUValue(balance))
.divide(new IOUValue(this._issuerTransferRate))
.multiply(new IOUValue(OrderBook.DEFAULT_TRANSFER_RATE)).toString();
.divide(this._issuerTransferRate)
.multiply(OrderBook.DEFAULT_TRANSFER_RATE).toString();
return adjustedBalance;
};
@@ -464,7 +573,6 @@ OrderBook.prototype.applyTransferRate = function(balance) {
*/
OrderBook.prototype.getOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
if (this.hasOwnerFunds(account)) {
if (this._currencyGets.is_native()) {
return Amount.from_json(this._ownerFunds[account]);
@@ -481,7 +589,6 @@ OrderBook.prototype.getOwnerFunds = function(account) {
*/
OrderBook.prototype.getUnadjustedOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
return this._ownerFundsUnadjusted[account];
};
@@ -492,7 +599,6 @@ OrderBook.prototype.getUnadjustedOwnerFunds = function(account) {
*/
OrderBook.prototype.deleteOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
this._ownerFunds[account] = undefined;
};
@@ -504,7 +610,6 @@ OrderBook.prototype.deleteOwnerFunds = function(account) {
*/
OrderBook.prototype.getOwnerOfferCount = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
return this._offerCounts[account] || 0;
};
@@ -516,7 +621,6 @@ OrderBook.prototype.getOwnerOfferCount = function(account) {
*/
OrderBook.prototype.incrementOwnerOfferCount = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
const result = (this._offerCounts[account] || 0) + 1;
this._offerCounts[account] = result;
return result;
@@ -531,7 +635,6 @@ OrderBook.prototype.incrementOwnerOfferCount = function(account) {
*/
OrderBook.prototype.decrementOwnerOfferCount = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
const result = (this._offerCounts[account] || 1) - 1;
this._offerCounts[account] = result;
@@ -552,8 +655,6 @@ OrderBook.prototype.decrementOwnerOfferCount = function(account) {
*/
OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
const previousAmount = this.getOwnerOfferTotal(account);
const currentAmount = previousAmount.add(Amount.from_json(amount));
@@ -572,14 +673,12 @@ OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
*/
OrderBook.prototype.subtractOwnerOfferTotal = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
const previousAmount = this.getOwnerOfferTotal(account);
const newAmount = previousAmount.subtract(Amount.from_json(amount));
this._ownerOffersTotal[account] = newAmount;
assert(!newAmount.is_negative(), 'Offer total cannot be negative');
return newAmount;
};
@@ -591,15 +690,14 @@ OrderBook.prototype.subtractOwnerOfferTotal = function(account, amount) {
*/
OrderBook.prototype.getOwnerOfferTotal = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
const amount = this._ownerOffersTotal[account];
if (amount) {
return amount;
}
if (this._currencyGets.is_native()) {
return Amount.from_json('0');
return OrderBook.ZERO_NATIVE_AMOUNT.clone();
}
return OrderBookUtils.normalizeAmount('0');
return OrderBook.ZERO_NORMALIZED_AMOUNT.clone();
};
/**
@@ -610,11 +708,10 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
*/
OrderBook.prototype.resetOwnerOfferTotal = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
if (this._currencyGets.is_native()) {
this._ownerOffersTotal[account] = Amount.from_json('0');
this._ownerOffersTotal[account] = OrderBook.ZERO_NATIVE_AMOUNT.clone();
} else {
this._ownerOffersTotal[account] = OrderBookUtils.normalizeAmount('0');
this._ownerOffersTotal[account] = OrderBook.ZERO_NORMALIZED_AMOUNT.clone();
}
};
@@ -632,17 +729,18 @@ OrderBook.prototype.resetOwnerOfferTotal = function(account) {
OrderBook.prototype.setOfferFundedAmount = function(offer) {
assert.strictEqual(typeof offer, 'object', 'Offer is invalid');
const takerGets = Amount.from_json(offer.TakerGets);
const fundedAmount = this.getOwnerFunds(offer.Account);
const previousOfferSum = this.getOwnerOfferTotal(offer.Account);
const currentOfferSum = previousOfferSum.add(
Amount.from_json(offer.TakerGets));
const currentOfferSum = previousOfferSum.add(takerGets);
offer.owner_funds = this.getUnadjustedOwnerFunds(offer.Account);
offer.is_fully_funded = fundedAmount.compareTo(currentOfferSum) >= 0;
offer.is_fully_funded = fundedAmount.is_comparable(currentOfferSum) &&
fundedAmount.compareTo(currentOfferSum) >= 0;
if (offer.is_fully_funded) {
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
offer.taker_gets_funded = takerGets.to_text();
offer.taker_pays_funded = Amount.from_json(offer.TakerPays).to_text();
} else if (previousOfferSum.compareTo(fundedAmount) < 0) {
offer.taker_gets_funded = fundedAmount.subtract(previousOfferSum).to_text();
@@ -698,7 +796,11 @@ OrderBook.prototype.parseAccountBalanceFromNode = function(node) {
}
assert(!isNaN(result.balance), 'node has an invalid balance');
assert(UInt160.is_valid(result.account), 'node has an invalid account');
if (this._validAccounts[result.Account] === undefined) {
assert(UInt160.is_valid(result.account), 'node has an invalid account');
this._validAccounts[result.Account] = true;
this._validAccountsCount++;
}
return result;
};
@@ -786,6 +888,7 @@ OrderBook.prototype.updateFundedAmounts = function(transaction) {
});
};
/**
* Update offers' funded amount with their owner's funds
*
@@ -793,10 +896,15 @@ OrderBook.prototype.updateFundedAmounts = function(transaction) {
*/
OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
// assert(UInt160.is_valid(account), 'Account is invalid');
const self = this;
if (!this.hasOwnerFunds(account)) {
// We are only updating owner funds that are already cached
return;
}
if (this._remote.trace) {
const ownerFunds = this.getOwnerFunds(account);
log.info('updating offer funds', this._key, account,
@@ -848,7 +956,7 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
OrderBook.prototype.notify = function(transaction) {
const self = this;
if (!(this._subscribed && this._synced)) {
if (!(this._subscribed && this._synced) || this._destroyed) {
return;
}
@@ -884,6 +992,12 @@ OrderBook.prototype.notify = function(transaction) {
function handleNode(node) {
switch (node.nodeType) {
case 'DeletedNode':
if (self._validAccounts[node.fields.Account] === undefined) {
assert(UInt160.is_valid(node.fields.Account),
'node has an invalid account');
self._validAccounts[node.fields.Account] = true;
self._validAccountsCount++;
}
self.deleteOffer(node, isOfferCancel);
// We don't want to count an OfferCancel as a trade
@@ -894,6 +1008,12 @@ OrderBook.prototype.notify = function(transaction) {
break;
case 'ModifiedNode':
if (self._validAccounts[node.fields.Account] === undefined) {
assert(UInt160.is_valid(node.fields.Account),
'node has an invalid account');
self._validAccounts[node.fields.Account] = true;
self._validAccountsCount++;
}
self.modifyOffer(node);
takerGetsTotal = takerGetsTotal
@@ -906,6 +1026,12 @@ OrderBook.prototype.notify = function(transaction) {
break;
case 'CreatedNode':
if (self._validAccounts[node.fields.Account] === undefined) {
assert(UInt160.is_valid(node.fields.Account),
'node has an invalid account');
self._validAccounts[node.fields.Account] = true;
self._validAccountsCount++;
}
// rippled does not set owner_funds if the order maker is the issuer
// because the value would be infinite
const fundedAmount = transactionOwnerFunds !== undefined ?
@@ -919,7 +1045,9 @@ OrderBook.prototype.notify = function(transaction) {
_.each(affectedNodes, handleNode);
this.emit('transaction', transaction);
this.notifyDirectOffersChanged();
if (!takerGetsTotal.is_zero()) {
this.emit('trade', takerPaysTotal, takerGetsTotal);
}
@@ -951,17 +1079,27 @@ OrderBook.prototype.insertOffer = function(node) {
const originalLength = this._offers.length;
for (let i = 0; i < originalLength; i++) {
const quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
const existingOfferQuality = OrderBookUtils.getOfferQuality(
this._offers[i],
this._currencyGets
);
if (!this._currencyGets.has_interest()) {
// use fast path
for (let i = 0; i < originalLength; i++) {
if (offer.qualityHex <= this._offers[i].qualityHex) {
this._offers.splice(i, 0, offer);
break;
}
}
} else {
for (let i = 0; i < originalLength; i++) {
const quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
const existingOfferQuality = OrderBookUtils.getOfferQuality(
this._offers[i],
this._currencyGets
);
if (quality.compareTo(existingOfferQuality) <= 0) {
this._offers.splice(i, 0, offer);
if (quality.compareTo(existingOfferQuality) <= 0) {
this._offers.splice(i, 0, offer);
break;
break;
}
}
}
@@ -1067,28 +1205,34 @@ OrderBook.prototype.deleteOffer = function(node, isOfferCancel) {
OrderBook.prototype.setOffers = function(offers) {
assert(Array.isArray(offers), 'Offers is not an array');
const self = this;
this.resetCache();
const newOffers = _.map(offers, function(rawOffer) {
const offer = OrderBook.offerRewrite(rawOffer);
let i = -1;
let offer;
const l = offers.length;
if (offer.hasOwnProperty('owner_funds')) {
while (++i < l) {
offer = OrderBook.offerRewrite(offers[i]);
if (this._validAccounts[offer.Account] === undefined) {
assert(UInt160.is_valid(offer.Account), 'Account is invalid');
this._validAccounts[offer.Account] = true;
this._validAccountsCount++;
}
if (offer.owner_funds !== undefined) {
// The first offer of each owner from book_offers contains owner balance
// of offer's output
self.setOwnerFunds(offer.Account, offer.owner_funds);
this.setOwnerFunds(offer.Account, offer.owner_funds);
}
self.incrementOwnerOfferCount(offer.Account);
this.incrementOwnerOfferCount(offer.Account);
self.setOfferFundedAmount(offer);
self.addOwnerOfferTotal(offer.Account, offer.TakerGets);
this.setOfferFundedAmount(offer);
this.addOwnerOfferTotal(offer.Account, offer.TakerGets);
offers[i] = offer;
}
return offer;
});
this._offers = newOffers;
this._offers = offers;
this._synced = true;
};
@@ -1191,6 +1335,14 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(),
'Autobridging is only for IOU:IOU orderbooks');
if (this._destroyed) {
return;
}
if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo) {
return;
}
const autobridgeCalculator = new AutobridgeCalculator(
this._currencyGets,
this._currencyPays,
@@ -1203,6 +1355,33 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
this._offersAutobridged = autobridgeCalculator.calculate();
};
OrderBook.prototype.computeAutobridgedOffersWrapper = function() {
const startTime = Date.now();
this.computeAutobridgedOffers();
this.mergeDirectAndAutobridgedBooks();
const lasted = (Date.now() - startTime);
const newMult =
Math.floor(lasted * 2 / OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME) + 1;
if (newMult !== this._autobridgeThrottleTimeMultiplier) {
this._autobridgeThrottleTimeMultiplier = newMult;
this.createDebouncedOffersWrapper();
}
};
OrderBook.prototype.createDebouncedOffersWrapper = function() {
const m = this._autobridgeThrottleTimeMultiplier;
this.computeAutobridgedOffersThrottled =
_.debounce(
_.throttle(
this.computeAutobridgedOffersWrapper,
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME * m,
{leading: true, trailing: true}),
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME,
{maxWait: OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT});
};
/**
* Merge direct and autobridged offers into a combined orderbook
*
@@ -1210,22 +1389,24 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
*/
OrderBook.prototype.mergeDirectAndAutobridgedBooks = function() {
const self = this;
if (this._destroyed) {
return;
}
if (_.isEmpty(this._offers) && _.isEmpty(this._offersAutobridged)) {
// still emit empty offers list to indicate that load is completed
this.emit('model', []);
if (this._synced && this._gotOffersFromLegOne &&
this._gotOffersFromLegTwo) {
// emit empty model to indicate to listeners that we've got offers,
// just there was no one
this.emit('model', []);
}
return;
}
this._mergedOffers = this._offers
.concat(this._offersAutobridged)
.sort(function(a, b) {
const aQuality = OrderBookUtils.getOfferQuality(a, self._currencyGets);
const bQuality = OrderBookUtils.getOfferQuality(b, self._currencyGets);
return aQuality.compareTo(bQuality);
});
.sort(this.sortOffers);
this.emit('model', this._mergedOffers);
};

View File

@@ -5,6 +5,9 @@ const assert = require('assert');
const SerializedObject = require('./serializedobject').SerializedObject;
const Types = require('./serializedtypes');
const Amount = require('./amount').Amount;
const Currency = require('./currency').Currency;
const UInt160 = require('./uint160').UInt160;
const IOUValue = require('./iouvalue').IOUValue;
const OrderBookUtils = {};
function assertValidNumber(number, message) {
@@ -19,10 +22,18 @@ function assertValidNumber(number, message) {
* @return JSON amount object
*/
function createAmount(value, currency, counterparty) {
const newJSON =
{'value': value, 'currency': currency, 'issuer': counterparty};
return Amount.from_json(newJSON);
function createAmount(value, currency_, counterparty_) {
const currency = currency_ instanceof Currency ?
currency_ :
Currency.from_json(currency_);
const counterparty = counterparty_ instanceof UInt160 ?
counterparty_ :
UInt160.from_json(counterparty_);
return Amount.from_components_unsafe(new IOUValue(value),
currency, counterparty, false);
}
/**
@@ -52,11 +63,11 @@ function getIssuerFromOffer(offer) {
* @return {Amount}
*/
OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
OrderBookUtils.getOfferTakerGetsFunded = function(offer, currency_, issuer_) {
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
const currency = getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer);
const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = issuer_ || getIssuerFromOffer(offer);
return createAmount(offer.taker_gets_funded, currency, issuer);
};
@@ -68,11 +79,11 @@ OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
* @return {Amount}
*/
OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
OrderBookUtils.getOfferTakerPaysFunded = function(offer, currency_, issuer_) {
assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid');
const currency = getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer);
const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = issuer_ || getIssuerFromOffer(offer);
return createAmount(offer.taker_pays_funded, currency, issuer);
};
@@ -85,11 +96,11 @@ OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
* @return {Amount}
*/
OrderBookUtils.getOfferTakerGets = function(offer) {
OrderBookUtils.getOfferTakerGets = function(offer, currency_, issuer_) {
assert(typeof offer, 'object', 'Offer is invalid');
const currency = offer.TakerPays.currency;
const issuer = offer.TakerPays.issuer;
const currency = currency_ || offer.TakerPays.currency;
const issuer = issuer_ || offer.TakerPays.issuer;
return createAmount(offer.TakerGets, currency, issuer);
};
@@ -101,7 +112,9 @@ OrderBookUtils.getOfferTakerGets = function(offer) {
* @param {Currency} currencyGets
*/
OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
OrderBookUtils.getOfferQuality = function(offer, currencyGets, currency_,
issuer_
) {
let amount;
if (currencyGets.has_interest()) {
@@ -113,8 +126,8 @@ OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
});
} else {
const currency = getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer);
const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = issuer_ || getIssuerFromOffer(offer);
amount = createAmount(offer.quality, currency, issuer);
}
@@ -140,13 +153,35 @@ OrderBookUtils.convertOfferQualityToHex = function(quality) {
return so.to_hex();
};
/**
* Formats an offer quality amount to a hex that can be parsed by
* Amount.parse_quality
*
* @param {String} quality
*
* @return {String}
*/
OrderBookUtils.convertOfferQualityToHexFromText = function(quality) {
const so = new SerializedObject();
Types.Quality.serialize(so, quality);
return so.to_hex();
};
OrderBookUtils.CURRENCY_ONE = Currency.from_json(1);
OrderBookUtils.ISSUER_ONE = UInt160.from_json(1);
/**
*
*/
OrderBookUtils.normalizeAmount = function(value) {
return Amount.from_number(value);
return Amount.from_components_unsafe(new IOUValue(value),
OrderBookUtils.CURRENCY_ONE, OrderBookUtils.ISSUER_ONE, false);
};
module.exports = OrderBookUtils;

View File

@@ -224,14 +224,16 @@ Remote.flags = {
Passive: 0x00010000,
Sell: 0x00020000 // offer was placed as a sell
},
// Ripple tate
// Ripple state
state: {
LowReserve: 0x00010000, // entry counts toward reserve
HighReserve: 0x00020000,
LowAuth: 0x00040000,
HighAuth: 0x00080000,
LowNoRipple: 0x00100000,
HighNoRipple: 0x00200000
HighNoRipple: 0x00200000,
LowFreeze: 0x00400000,
HighFreeze: 0x00800000
}
};
@@ -2275,7 +2277,11 @@ Remote.prototype.createTransaction = function(type, options = {}) {
TrustSet: transaction.trustSet,
OfferCreate: transaction.offerCreate,
OfferCancel: transaction.offerCancel,
SetRegularKey: transaction.setRegularKey
SetRegularKey: transaction.setRegularKey,
SignerListSet: transaction.setSignerList,
SuspendedPaymentCreate: transaction.suspendedPaymentCreate,
SuspendedPaymentFinish: transaction.suspendedPaymentFinish,
SuspendedPaymentCancel: transaction.suspendedPaymentCancel
};
const transactionConstructor = constructorMap[type];

View File

@@ -39,7 +39,10 @@ function Request(remote, command) {
util.inherits(Request, EventEmitter);
// Send the request to a remote.
Request.prototype.request = function(servers, callback) {
Request.prototype.request = function(servers, callback_) {
const self = this;
const callback = typeof servers === 'function' ? servers : callback_;
this.emit('before');
this.callback(callback);
@@ -51,15 +54,32 @@ Request.prototype.request = function(servers, callback) {
this.on('error', function() {});
this.emit('request', this.remote);
if (Array.isArray(servers)) {
servers.forEach(function(server) {
this.setServer(server);
this.remote.request(this);
}, this);
} else {
this.remote.request(this);
function doRequest() {
if (Array.isArray(servers)) {
servers.forEach(function(server) {
self.setServer(server);
self.remote.request(self);
}, self);
} else {
self.remote.request(self);
}
}
function onReconnect() {
doRequest();
}
function onResponse() {
self.remote.removeListener('connected', onReconnect);
}
if (this.remote.isConnected()) {
this.remote.on('connected', onReconnect);
}
this.once('response', onResponse);
doRequest();
return this;
};
@@ -228,7 +248,9 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
this.once(this.successEvent, requestSuccess);
this.once(this.errorEvent, requestError);
this.request();
if (!this.requested) {
this.request();
}
return this;
};

View File

@@ -19,7 +19,7 @@ const Seed = extend(function() {
}, UInt);
Seed.width = 16;
Seed.prototype = extend({}, UInt.prototype);
Seed.prototype = Object.create(extend({}, UInt.prototype));
Seed.prototype.constructor = Seed;
// value = NaN on error.

View File

@@ -4,7 +4,6 @@ const _ = require('lodash');
const assert = require('assert');
const util = require('util');
const url = require('url');
const HttpsProxyAgent = require('https-proxy-agent');
const LRU = require('lru-cache');
const EventEmitter = require('events').EventEmitter;
const RippleError = require('./rippleerror').RippleError;
@@ -447,6 +446,12 @@ Server.prototype.connect = function() {
const parsed = url.parse(this._opts.url);
const opts = url.parse(this._remote.proxy);
opts.secureEndpoint = parsed.protocol === 'wss:';
let HttpsProxyAgent;
try {
HttpsProxyAgent = require('https-proxy-agent');
} catch (error) {
throw new Error('"proxy" option is not supported in the browser');
}
const agent = new HttpsProxyAgent(opts);
this._ws = new WebSocket(this._opts.url, {agent: agent});
@@ -687,6 +692,7 @@ Server.prototype._handleResponse = function(message) {
remote: message
});
}
request.emit('response', message);
};
Server.prototype._handlePathFind = function(message) {

View File

@@ -1,7 +1,8 @@
'use strict';
const assert = require('assert');
const util = require('util');
const lodash = require('lodash');
const _ = require('lodash');
const EventEmitter = require('events').EventEmitter;
const utils = require('./utils');
const sjclcodec = require('sjcl-codec');
@@ -9,6 +10,7 @@ const Amount = require('./amount').Amount;
const Currency = require('./amount').Currency;
const UInt160 = require('./amount').UInt160;
const Seed = require('./seed').Seed;
const KeyPair = require('ripple-keypairs').KeyPair;
const SerializedObject = require('./serializedobject').SerializedObject;
const RippleError = require('./rippleerror').RippleError;
const hashprefixes = require('./hashprefixes');
@@ -199,7 +201,7 @@ Transaction.from_json = function(j) {
Transaction.prototype.setJson =
Transaction.prototype.parseJson = function(v) {
this.tx_json = v;
this.tx_json = _.merge({}, v);
return this;
};
@@ -358,7 +360,6 @@ Transaction.prototype._computeFee = function() {
});
const midInd = Math.floor(fees.length / 2);
const median = fees.length % 2 === 0
? Math.floor(0.5 + (fees[midInd] + fees[midInd - 1]) / 2)
: fees[midInd];
@@ -377,56 +378,86 @@ Transaction.prototype._computeFee = function() {
* return `false`
*/
Transaction.prototype.err = function(error, errorMessage) {
this.emit('error', new RippleError(error, errorMessage));
return false;
};
Transaction.prototype.complete = function() {
if (this.remote) {
if (!this.remote.trusted && !this.remote.local_signing) {
this.emit('error', new RippleError(
'tejServerUntrusted', 'Attempt to give secret to untrusted server'));
return false;
}
// Auto-fill the secret
this._secret = this._secret || this.getSecret();
if (_.isUndefined(this._secret)) {
return this.err('tejSecretUnknown', 'Missing secret');
}
if (!this._secret) {
this._secret = this.getSecret();
if (this.remote && !(this.remote.local_signing || this.remote.trusted)) {
return this.err(
'tejServerUntrusted',
'Attempt to give secret to untrusted server');
}
// Try to auto-fill the secret
if (!this._secret) {
this.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
return false;
}
if (typeof this.tx_json.SigningPubKey === 'undefined') {
if (_.isUndefined(this.tx_json.SigningPubKey)) {
try {
const seed = Seed.from_json(this._secret);
const key = seed.get_key();
this.tx_json.SigningPubKey = key.pubKeyHex();
} catch(e) {
this.emit('error', new RippleError(
'tejSecretInvalid', 'Invalid secret'));
return false;
this.setSigningPubKey(this.getSigningPubKey());
} catch (e) {
return this.err('tejSecretInvalid', 'Invalid secret');
}
}
// If the Fee hasn't been set, one needs to be computed by
// an assigned server
if (this.remote && typeof this.tx_json.Fee === 'undefined') {
if (this.remote.local_fee || !this.remote.trusted) {
this.tx_json.Fee = this._computeFee();
if (!this.tx_json.Fee) {
this.emit('error', new RippleError('tejUnconnected'));
return false;
// Auto-fill transaction Fee
if (_.isUndefined(this.tx_json.Fee)) {
if (this.remote && (this.remote.local_fee || !this.remote.trusted)) {
const computedFee = this._computeFee();
if (!computedFee) {
// Unable to compute fee due to no connected servers
return this.err('tejUnconnected');
}
this.tx_json.Fee = computedFee;
}
}
if (Number(this.tx_json.Fee) > this._maxFee) {
this.emit('error', new RippleError(
'tejMaxFeeExceeded', 'Max fee exceeded'));
return false;
return this.err('tejMaxFeeExceeded', 'Max fee exceeded');
}
// Set canonical flag - this enables canonicalized signature checking
this.setCanonicalFlag();
return this.tx_json;
};
Transaction.prototype.getKeyPair = function(secret_) {
if (this._keyPair) {
return this._keyPair;
}
const secret = secret_ || this._secret;
assert(secret, 'Secret missing');
const keyPair = Seed.from_json(secret).get_key();
this._keyPair = keyPair;
return keyPair;
};
Transaction.prototype.getSigningPubKey = function(secret) {
return this.getKeyPair(secret).pubKeyHex();
};
Transaction.prototype.setSigningPubKey = function(key) {
if (_.isString(key)) {
this.tx_json.SigningPubKey = key;
} else if (key instanceof KeyPair) {
this.tx_json.SigningPubKey = key.pubKeyHex();
}
return this;
};
Transaction.prototype.setCanonicalFlag = function() {
if (this.remote && this.remote.local_signing && this.canonical) {
this.tx_json.Flags |= Transaction.flags.Universal.FullyCanonicalSig;
@@ -435,7 +466,7 @@ Transaction.prototype.complete = function() {
this.tx_json.Flags = this.tx_json.Flags >>> 0;
}
return this.tx_json;
return this;
};
Transaction.prototype.serialize = function() {
@@ -453,6 +484,14 @@ Transaction.prototype.signingData = function() {
return so;
};
Transaction.prototype.multiSigningData = function(account) {
const so = new SerializedObject();
so.append(hashprefixes.HASH_TX_MULTISIGN_BYTES);
so.parse_json(this.tx_json);
so.append(UInt160.from_json(account).to_bytes());
return so;
};
Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
let prefix;
@@ -470,7 +509,11 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
};
Transaction.prototype.sign = function() {
const seed = Seed.from_json(this._secret);
if (this.hasMultiSigners()) {
return this;
}
const keyPair = this.getKeyPair();
const prev_sig = this.tx_json.TxnSignature;
delete this.tx_json.TxnSignature;
@@ -483,9 +526,7 @@ Transaction.prototype.sign = function() {
return this;
}
const key = seed.get_key();
const hex = key.signHex(this.signingData().buffer);
this.tx_json.TxnSignature = hex;
this.tx_json.TxnSignature = keyPair.signHex(this.signingData().buffer);
this.previousSigningHash = hash;
return this;
@@ -499,7 +540,7 @@ Transaction.prototype.sign = function() {
*/
Transaction.prototype.addId = function(id) {
if (!lodash.contains(this.submittedIDs, id)) {
if (!_.contains(this.submittedIDs, id)) {
this.submittedIDs.unshift(id);
}
};
@@ -515,7 +556,7 @@ Transaction.prototype.addId = function(id) {
*/
Transaction.prototype.findId = function(cache) {
const cachedTransactionID = lodash.detect(this.submittedIDs, function(id) {
const cachedTransactionID = _.detect(this.submittedIDs, function(id) {
return cache.hasOwnProperty(id);
});
return cache[cachedTransactionID];
@@ -582,9 +623,24 @@ Transaction.prototype.maxFee = function(fee) {
* @returns {Transaction} calling instance for chaining
*/
Transaction.prototype.setFixedFee = function(fee) {
if (typeof fee === 'number' && fee >= 0) {
this._setFixedFee = true;
return this.setFee(fee, {fixed: true});
};
Transaction.prototype.setFee = function(fee, options = {}) {
if (_.isNumber(fee) && fee >= 0) {
this.tx_json.Fee = String(fee);
if (options.fixed) {
this._setFixedFee = true;
}
}
return this;
};
Transaction.prototype.setSequence = function(sequence) {
if (_.isNumber(sequence)) {
this._setUInt32('Sequence', sequence);
this._setSequence = true;
}
return this;
@@ -606,7 +662,7 @@ Transaction.prototype.secret = function(secret) {
};
Transaction.prototype.setType = function(type) {
if (lodash.isUndefined(Transaction.formats, type)) {
if (_.isUndefined(Transaction.formats, type)) {
throw new Error('TransactionType must be a valid transaction type');
}
@@ -616,14 +672,14 @@ Transaction.prototype.setType = function(type) {
};
Transaction.prototype._setUInt32 = function(name, value, options_) {
const options = lodash.merge({}, options_);
const options = _.merge({}, options_);
const isValidUInt32 = typeof value === 'number'
&& value >= 0 && value < Math.pow(256, 4);
if (!isValidUInt32) {
throw new Error(name + ' must be a valid UInt32');
}
if (!lodash.isUndefined(options.min_value) && value < options.min_value) {
if (!_.isUndefined(options.min_value) && value < options.min_value) {
throw new Error(name + ' must be >= ' + options.min_value);
}
@@ -660,7 +716,7 @@ Transaction.prototype.setAccount = function(account) {
};
Transaction.prototype._setAmount = function(name, amount, options_) {
const options = lodash.merge({no_native: false}, options_);
const options = _.merge({no_native: false}, options_);
const parsedAmount = Amount.from_json(amount);
if (parsedAmount.is_negative()) {
@@ -689,7 +745,7 @@ Transaction.prototype._setHash256 = function(name, value, options_) {
throw new Error(name + ' must be a valid Hash256');
}
const options = lodash.merge({pad: false}, options_);
const options = _.merge({pad: false}, options_);
let hash256 = value;
if (options.pad) {
@@ -752,6 +808,11 @@ Transaction.prototype.setFlags = function(flags) {
return this;
};
function convertStringToHex(string) {
const utf8String = sjclcodec.utf8String.toBits(string);
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
}
/**
* Add a Memo to transaction.
*
@@ -769,7 +830,7 @@ Transaction.prototype.addMemo = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
} else {
options = {
memoType: arguments[0],
@@ -778,11 +839,6 @@ Transaction.prototype.addMemo = function(options_) {
};
}
function convertStringToHex(string) {
const utf8String = sjclcodec.utf8String.toBits(string);
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
}
const memo = {};
const memoRegex = Transaction.MEMO_REGEX;
let memoType = options.memoType;
@@ -790,7 +846,7 @@ Transaction.prototype.addMemo = function(options_) {
let memoData = options.memoData;
if (memoType) {
if (!(lodash.isString(memoType) && memoRegex.test(memoType))) {
if (!(_.isString(memoType) && memoRegex.test(memoType))) {
throw new Error(
'MemoType must be a string containing only valid URL characters');
}
@@ -803,7 +859,7 @@ Transaction.prototype.addMemo = function(options_) {
}
if (memoFormat) {
if (!(lodash.isString(memoFormat) && memoRegex.test(memoFormat))) {
if (!(_.isString(memoFormat) && memoRegex.test(memoFormat))) {
throw new Error(
'MemoFormat must be a string containing only valid URL characters');
}
@@ -857,15 +913,15 @@ Transaction.prototype.accountSet = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src;
}
if (lodash.isUndefined(options.set_flag)) {
if (_.isUndefined(options.set_flag)) {
options.set_flag = options.set;
}
if (lodash.isUndefined(options.clear_flag)) {
if (_.isUndefined(options.clear_flag)) {
options.clear_flag = options.clear;
}
} else {
@@ -879,10 +935,10 @@ Transaction.prototype.accountSet = function(options_) {
this.setType('AccountSet');
this.setAccount(options.account);
if (!lodash.isUndefined(options.set_flag)) {
if (!_.isUndefined(options.set_flag)) {
this.setSetFlag(options.set_flag);
}
if (!lodash.isUndefined(options.clear_flag)) {
if (!_.isUndefined(options.clear_flag)) {
this.setClearFlag(options.clear_flag);
}
@@ -899,7 +955,7 @@ Transaction.prototype.setAccountSetFlag = function(name, value) {
: accountSetFlags['asf' + flagValue];
}
if (!lodash.contains(lodash.values(accountSetFlags), flagValue)) {
if (!_.contains(_.values(accountSetFlags), flagValue)) {
throw new Error(name + ' must be a valid AccountSet flag');
}
@@ -956,9 +1012,9 @@ Transaction.prototype.setRegularKey = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src;
}
} else {
@@ -971,7 +1027,7 @@ Transaction.prototype.setRegularKey = function(options_) {
this.setType('SetRegularKey');
this.setAccount(options.account);
if (!lodash.isUndefined(options.regular_key)) {
if (!_.isUndefined(options.regular_key)) {
this._setAccount('RegularKey', options.regular_key);
}
@@ -992,9 +1048,9 @@ Transaction.prototype.rippleLineSet = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src;
}
} else {
@@ -1009,13 +1065,13 @@ Transaction.prototype.rippleLineSet = function(options_) {
this.setType('TrustSet');
this.setAccount(options.account);
if (!lodash.isUndefined(options.limit)) {
if (!_.isUndefined(options.limit)) {
this.setLimit(options.limit);
}
if (!lodash.isUndefined(options.quality_in)) {
if (!_.isUndefined(options.quality_in)) {
this.setQualityIn(options.quality_in);
}
if (!lodash.isUndefined(options.quality_out)) {
if (!_.isUndefined(options.quality_out)) {
this.setQualityOut(options.quality_out);
}
@@ -1057,12 +1113,12 @@ Transaction.prototype.payment = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src || options.from;
}
if (lodash.isUndefined(options.destination)) {
if (_.isUndefined(options.destination)) {
options.destination = options.dst || options.to;
}
} else {
@@ -1241,18 +1297,18 @@ Transaction.prototype.offerCreate = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src;
}
if (lodash.isUndefined(options.taker_pays)) {
if (_.isUndefined(options.taker_pays)) {
options.taker_pays = options.buy;
}
if (lodash.isUndefined(options.taker_gets)) {
if (_.isUndefined(options.taker_gets)) {
options.taker_gets = options.sell;
}
if (lodash.isUndefined(options.offer_sequence)) {
if (_.isUndefined(options.offer_sequence)) {
options.offer_sequence = options.cancel_sequence || options.sequence;
}
} else {
@@ -1270,10 +1326,10 @@ Transaction.prototype.offerCreate = function(options_) {
this.setTakerGets(options.taker_gets);
this.setTakerPays(options.taker_pays);
if (!lodash.isUndefined(options.expiration)) {
if (!_.isUndefined(options.expiration)) {
this.setExpiration(options.expiration);
}
if (!lodash.isUndefined(options.offer_sequence)) {
if (!_.isUndefined(options.offer_sequence)) {
this.setOfferSequence(options.offer_sequence);
}
@@ -1311,12 +1367,12 @@ Transaction.prototype.offerCancel = function(options_) {
let options;
if (typeof options_ === 'object') {
options = lodash.merge({}, options_);
options = _.merge({}, options_);
if (lodash.isUndefined(options.account)) {
if (_.isUndefined(options.account)) {
options.account = options.src;
}
if (lodash.isUndefined(options.offer_sequence)) {
if (_.isUndefined(options.offer_sequence)) {
options.offer_sequence = options.sequence || options.cancel_sequence;
}
} else {
@@ -1333,16 +1389,48 @@ Transaction.prototype.offerCancel = function(options_) {
return this;
};
Transaction._prepareSignerEntry = function(signer) {
const {account, weight} = signer;
assert(UInt160.is_valid(account), 'Signer account invalid');
assert(_.isNumber(weight), 'Signer weight missing');
assert(weight > 0 && weight <= 65535, 'Signer weight must be 1-65535');
return {
SignerEntry: {
Account: account,
SignerWeight: weight
}
};
};
Transaction.prototype.setSignerList = function(options = {}) {
this.setType('SignerListSet');
this.setAccount(options.account);
this.setSignerQuorum(options.signerQuorum);
if (!_.isEmpty(options.signers)) {
this.tx_json.SignerEntries =
options.signers.map(Transaction._prepareSignerEntry);
}
return this;
};
Transaction.prototype.setSignerQuorum = function(quorum) {
this._setUInt32('SignerQuorum', quorum);
};
/**
* Submit transaction to the network
*
* @param [Function] callback
*/
Transaction.prototype.submit = function(callback) {
Transaction.prototype.submit = function(callback = function() {}) {
const self = this;
this.callback = (typeof callback === 'function') ? callback : function() {};
this.callback = callback;
this._errorHandler = function transactionError(error_, message) {
let error = error_;
@@ -1413,6 +1501,163 @@ Transaction.prototype.summary = function() {
return txSummary;
};
exports.Transaction = Transaction;
/**
* Construct a 'SuspendedPaymentCreate' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setDigest()
* - setAllowCancelAfter()
* - setAllowExecuteAfter()
*
* @param {String} options.account source account
* @param {String} options.destination account
* @param {Amount} options.amount payment amount
*/
// vim:sw=2:sts=2:ts=8:et
Transaction.prototype.suspendedPaymentCreate = function(options) {
this.setType('SuspendedPaymentCreate');
this.setAccount(options.account);
this.setDestination(options.destination);
this.setAmount(options.amount);
return this;
};
/**
* Construct a 'SuspendedPaymentFinish' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setOwner()
* - setOfferSequence()
* - setMethod()
* - setDigest()
* - setProof()
*
* @param {String} options.account source account
* @param {String} options.owner SuspendedPaymentCreate's Account
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
*/
Transaction.prototype.suspendedPaymentFinish = function(options) {
this.setType('SuspendedPaymentFinish');
this.setAccount(options.account);
this.setOwner(options.owner);
this.setOfferSequence(options.paymentSequence);
return this;
};
/**
* Construct a 'SuspendedPaymentCancel' transaction
*
* Relevant setters:
* - setSourceTag()
* - setFlags()
* - setOwner()
* - setOfferSequence()
*
* @param {String} options.account source account
* @param {String} options.owner SuspendedPaymentCreate's Account
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
*/
Transaction.prototype.suspendedPaymentCancel = function(options) {
this.setType('SuspendedPaymentCancel');
this.setAccount(options.account);
this.setOwner(options.owner);
this.setOfferSequence(options.paymentSequence);
return this;
};
Transaction.prototype.setDigest = function(digest) {
return this._setHash256('Digest', digest);
};
Transaction.prototype.setAllowCancelAfter = function(after) {
return this._setUInt32('CancelAfter', utils.time.toRipple(after));
};
Transaction.prototype.setAllowExecuteAfter = function(after) {
return this._setUInt32('FinishAfter', utils.time.toRipple(after));
};
Transaction.prototype.setOwner = function(owner) {
return this._setAccount('Owner', owner);
};
Transaction.prototype.setMethod = function(method) {
return this._setUInt8('Method', method);
};
Transaction.prototype.setProof = function(proof) {
this.tx_json.Proof = convertStringToHex(proof);
return this;
};
Transaction.prototype._setUInt8 = function(name, value) {
const isValidUInt8 = typeof value === 'number' && value >= 0 && value < 256;
if (!isValidUInt8) {
throw new Error(name + ' must be a valid UInt8');
}
this.tx_json[name] = value;
return this;
};
Transaction.prototype.setSigners = function(signers) {
if (_.isArray(signers)) {
this.tx_json.Signers = signers;
}
return this;
};
Transaction.prototype.addMultiSigner = function(signer) {
assert(UInt160.is_valid(signer.Account), 'Signer must have a valid Account');
if (_.isUndefined(this.multi_signers)) {
this.multi_signers = [];
}
this.multi_signers.push({Signer: signer});
this.multi_signers.sort((a, b) => {
return UInt160.from_json(a.Signer.Account)
.cmp(UInt160.from_json(b.Signer.Account));
});
};
Transaction.prototype.hasMultiSigners = function() {
return !_.isEmpty(this.multi_signers);
};
Transaction.prototype.getMultiSigners = function() {
return this.multi_signers;
};
Transaction.prototype.getMultiSigningJson = function() {
assert(this.tx_json.Sequence, 'Sequence must be set before multi-signing');
assert(this.tx_json.Fee, 'Fee must be set before multi-signing');
const signingTx = Transaction.from_json(this.tx_json);
signingTx.remote = this.remote;
signingTx.setSigningPubKey('');
signingTx.setCanonicalFlag();
return signingTx.tx_json;
};
Transaction.prototype.multiSign = function(account, secret) {
const signingData = this.multiSigningData(account);
const keyPair = Seed.from_json(secret).get_key();
const signer = {
Account: account,
TxnSignature: keyPair.signHex(signingData.buffer),
SigningPubKey: keyPair.pubKeyHex()
};
return signer;
};
exports.Transaction = Transaction;

View File

@@ -1,5 +1,6 @@
'use strict';
const _ = require('lodash');
const util = require('util');
const assert = require('assert');
const async = require('async');
@@ -520,6 +521,17 @@ TransactionManager.prototype._resubmit = function(ledgers_, pending_) {
TransactionManager.prototype._prepareRequest = function(tx) {
const submitRequest = this._remote.requestSubmit();
if (tx.hasMultiSigners()) {
tx.setSigningPubKey('');
if (this._remote.local_signing) {
tx.setSigners(tx.getMultiSigners());
} else {
submitRequest.message.command = 'submit_multisigned';
submitRequest.message.Signers = tx.getMultiSigners();
}
}
if (this._remote.local_signing) {
tx.sign();
@@ -678,7 +690,7 @@ TransactionManager.prototype._request = function(tx) {
tx.initialSubmitIndex = tx.submitIndex;
}
if (!tx._setLastLedger) {
if (!tx._setLastLedger && !tx.hasMultiSigners()) {
// Honor LastLedgerSequence set with tx.lastLedger()
tx.tx_json.LastLedgerSequence = tx.initialSubmitIndex
+ this._lastLedgerOffset;
@@ -726,9 +738,13 @@ TransactionManager.prototype.submit = function(tx) {
return;
}
if (typeof tx.tx_json.Sequence !== 'number') {
if (!_.isNumber(tx.tx_json.Sequence)) {
// Honor manually-set sequences
tx.tx_json.Sequence = this._nextSequence++;
tx.setSequence(this._nextSequence++);
}
if (tx.hasMultiSigners()) {
tx.setResubmittable(false);
}
tx.once('cleanup', function() {

View File

@@ -1,23 +1,23 @@
'use strict';
var utils = require('./utils');
var extend = require('extend');
var UInt = require('./uint').UInt;
const utils = require('./utils');
const extend = require('extend');
const UInt = require('./uint').UInt;
//
// UInt128 support
//
var UInt128 = extend(function() {
const UInt128 = extend(function() {
this._value = NaN;
}, UInt);
UInt128.width = 16;
UInt128.prototype = extend({}, UInt.prototype);
UInt128.prototype = Object.create(extend({}, UInt.prototype));
UInt128.prototype.constructor = UInt128;
var HEX_ZERO = UInt128.HEX_ZERO = '00000000000000000000000000000000';
var HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
const HEX_ZERO = UInt128.HEX_ZERO = '00000000000000000000000000000000';
const HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
UInt128.STR_ZERO = utils.hexToString(HEX_ZERO);
UInt128.STR_ONE = utils.hexToString(HEX_ONE);

View File

@@ -1,27 +1,27 @@
'use strict';
var utils = require('./utils');
var extend = require('extend');
const utils = require('./utils');
const extend = require('extend');
var UInt = require('./uint').UInt;
var Base = require('./base').Base;
const UInt = require('./uint').UInt;
const Base = require('./base').Base;
//
// UInt160 support
//
var UInt160 = extend(function() {
const UInt160 = extend(function() {
this._value = NaN;
this._version_byte = undefined;
this._update();
}, UInt);
UInt160.width = 20;
UInt160.prototype = extend({}, UInt.prototype);
UInt160.prototype = Object.create(extend({}, UInt.prototype));
UInt160.prototype.constructor = UInt160;
var HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
const HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
const HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
UInt160.ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
UInt160.ACCOUNT_ONE = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
@@ -74,13 +74,12 @@ UInt160.prototype.parse_generic = function(j) {
};
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
UInt160.prototype.to_json = function(opts) {
opts = opts || {};
UInt160.prototype.to_json = function(opts = {}) {
if (this.is_valid()) {
// If this value has a type, return a Base58 encoded string.
if (typeof this._version_byte === 'number') {
var output = Base.encode_check(this._version_byte, this.to_bytes());
let output = Base.encode_check(this._version_byte, this.to_bytes());
if (opts.gateways && output in opts.gateways) {
output = opts.gateways[output];

View File

@@ -1,25 +1,25 @@
'use strict';
var utils = require('./utils');
var extend = require('extend');
var UInt = require('./uint').UInt;
const utils = require('./utils');
const extend = require('extend');
const UInt = require('./uint').UInt;
//
// UInt256 support
//
var UInt256 = extend(function() {
const UInt256 = extend(function() {
this._value = NaN;
}, UInt);
UInt256.width = 32;
UInt256.prototype = extend({}, UInt.prototype);
UInt256.prototype = Object.create(extend({}, UInt.prototype));
UInt256.prototype.constructor = UInt256;
var HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' +
const HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' +
'00000000000000000000000000000000';
var HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' +
const HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' +
'00000000000000000000000000000001';
UInt256.STR_ZERO = utils.hexToString(HEX_ZERO);

46
test/amount-test-error.js Normal file
View File

@@ -0,0 +1,46 @@
'use strict';
const assert = require('assert');
const Amount = require('ripple-lib').Amount;
const Remote = require('ripple-lib').Remote;
const data = require('./fixtures/negative-error');
describe.skip('Amount ', function() {
it('Show "Offer total cannot be negative" error', function() {
const a1 = {
currency: 'JPY',
issuer: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
value: '66436.33517689175'
};
const a2 = {
currency: 'JPY',
issuer: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
value: '66435.49665972557'
};
const a1a = Amount.from_json(a1);
const res = a1a.add(a2).subtract(a2).subtract(a1);
console.log(res.to_human());
assert(!res.is_negative(), 'Offer total cannot be negative');
});
it('Show Details of "Offer total cannot be negative" error', function() {
const book = new Remote().createOrderBook({
currency_gets: 'JPY',
issuer_gets: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
currency_pays: 'XRP'
});
book._subscribed = true;
book._synced = true;
book._offers = data._offers;
book._offerCounts = data._offerCounts;
book._ownerFundsUnadjusted = data._ownerFundsUnadjusted;
book._ownerFunds = data._ownerFunds;
book._ownerOffersTotal = data._ownerOffersTotal;
book._issuerTransferRate = 1000000000;
book._remote._handleTransaction(data.message1);
book._remote._handleTransaction(data.lastMessage);
});
});

View File

@@ -2,7 +2,6 @@
'use strict';
const _ = require('lodash');
const assert = require('assert-diff');
const path = require('path');
const setupAPI = require('./setup-api');
const RippleAPI = require('ripple-api').RippleAPI;
const common = RippleAPI._PRIVATE.common;
@@ -16,6 +15,7 @@ const validate = common.validate;
const utils = RippleAPI._PRIVATE.ledgerUtils;
const ledgerClosed = require('./fixtures/api/rippled/ledger-close-newer');
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
const ledgerHashSchema = require('./fixtures/schemas/ledgerhash.json');
const orderbook = {
base: {
@@ -143,6 +143,31 @@ describe('RippleAPI', function() {
'prepare'));
});
it('prepareSuspendedPaymentCreation', function() {
const localInstructions = _.defaults({
maxFee: '0.000012'
}, instructions);
return this.api.prepareSuspendedPaymentCreation(
address, requests.prepareSuspendedPaymentCreation,
localInstructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentCreation,
'prepare'));
});
it('prepareSuspendedPaymentExecution', function() {
return this.api.prepareSuspendedPaymentExecution(
address, requests.prepareSuspendedPaymentExecution, instructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentExecution,
'prepare'));
});
it('prepareSuspendedPaymentCancellation', function() {
return this.api.prepareSuspendedPaymentCancellation(
address, requests.prepareSuspendedPaymentCancellation, instructions).then(
_.partial(checkResult, responses.prepareSuspendedPaymentCancellation,
'prepare'));
});
it('sign', function() {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
const result = this.api.sign(requests.sign.txJSON, secret);
@@ -155,160 +180,198 @@ describe('RippleAPI', function() {
_.partial(checkResult, responses.submit, 'submit'));
});
it('submit - failure', function() {
return this.api.submit('BAD').then(() => {
assert(false, 'Should throw RippleError');
}).catch(error => {
assert(error instanceof this.api.errors.RippleError);
assert(error.data);
});
});
it('getBalances', function() {
return this.api.getBalances(address).then(
_.partial(checkResult, responses.getBalances, 'getBalances'));
});
it('getTransaction - payment', function() {
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
_.partial(checkResult, responses.getTransaction.payment,
'getTransaction'));
});
it('getTransaction - settings', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.settings,
'getTransaction'));
});
it('getTransaction - order', function() {
const hash =
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.order,
'getTransaction'));
});
it('getTransaction - order cancellation', function() {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.orderCancellation,
'getTransaction'));
});
it('getTransaction - trustline set', function() {
const hash =
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustline,
'getTransaction'));
});
it('getTransaction - trustline frozen off', function() {
const hash =
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustlineFrozenOff,
'getTransaction'));
});
it('getTransaction - trustline no quality', function() {
const hash =
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
'getTransaction'));
});
it('getTransaction - not validated', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.notValidated,
'getTransaction'));
});
it('getTransaction - tracking on', function() {
const hash =
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trackingOn,
'getTransaction'));
});
it('getTransaction - tracking off', function() {
const hash =
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trackingOff,
'getTransaction'));
});
it('getTransaction - set regular key', function() {
const hash =
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.setRegularKey,
'getTransaction'));
});
it('getTransaction - not found in range', function() {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
const options = {
minLedgerVersion: 32570,
maxLedgerVersion: 32571
};
return this.api.getTransaction(hash, options).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
describe('getTransaction', () => {
it('getTransaction - payment', function() {
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
_.partial(checkResult, responses.getTransaction.payment,
'getTransaction'));
});
});
it('getTransaction - not found by hash', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
it('getTransaction - settings', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.settings,
'getTransaction'));
});
});
it('getTransaction - missing ledger history', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
// make gaps in history
this.api.remote.getServer().emit('message', ledgerClosed);
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw MissingLedgerHistoryError');
}).catch(error => {
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
it('getTransaction - order', function() {
const hash =
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.order,
'getTransaction'));
});
});
it('getTransaction - ledger_index not found', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
assert(error.message.indexOf('ledger_index') !== -1);
it('getTransaction - order cancellation', function() {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.orderCancellation,
'getTransaction'));
});
});
it('getTransaction - transaction ledger not found', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
assert(error.message.indexOf('ledger not found') !== -1);
it('getTransaction - trustline set', function() {
const hash =
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustline,
'getTransaction'));
});
});
it('getTransaction - ledger missing close time', function() {
const hash =
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw ApiError');
}).catch(error => {
assert(error instanceof this.api.errors.ApiError);
it('getTransaction - trustline frozen off', function() {
const hash =
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustlineFrozenOff,
'getTransaction'));
});
it('getTransaction - trustline no quality', function() {
const hash =
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
'getTransaction'));
});
it('getTransaction - not validated', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
});
});
it('getTransaction - tracking on', function() {
const hash =
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trackingOn,
'getTransaction'));
});
it('getTransaction - tracking off', function() {
const hash =
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.trackingOff,
'getTransaction'));
});
it('getTransaction - set regular key', function() {
const hash =
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60';
return this.api.getTransaction(hash).then(
_.partial(checkResult, responses.getTransaction.setRegularKey,
'getTransaction'));
});
it('getTransaction - not found in range', function() {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
const options = {
minLedgerVersion: 32570,
maxLedgerVersion: 32571
};
return this.api.getTransaction(hash, options).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
});
});
it('getTransaction - not found by hash', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
});
});
it('getTransaction - missing ledger history', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
// make gaps in history
this.api.remote.getServer().emit('message', ledgerClosed);
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw MissingLedgerHistoryError');
}).catch(error => {
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
});
});
it('getTransaction - missing ledger history with ledger range', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
const options = {
minLedgerVersion: 32569,
maxLedgerVersion: 32571
};
return this.api.getTransaction(hash, options).then(() => {
assert(false, 'Should throw MissingLedgerHistoryError');
}).catch(error => {
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
});
});
it('getTransaction - not found - future maxLedgerVersion', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
const options = {
maxLedgerVersion: 99999999999
};
return this.api.getTransaction(hash, options).then(() => {
assert(false, 'Should throw PendingLedgerVersionError');
}).catch(error => {
assert(error instanceof this.api.errors.PendingLedgerVersionError);
});
});
it('getTransaction - ledger_index not found', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
assert(error.message.indexOf('ledger_index') !== -1);
});
});
it('getTransaction - transaction ledger not found', function() {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw NotFoundError');
}).catch(error => {
assert(error instanceof this.api.errors.NotFoundError);
assert(error.message.indexOf('ledger not found') !== -1);
});
});
it('getTransaction - ledger missing close time', function() {
const hash =
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04';
return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw ApiError');
}).catch(error => {
assert(error instanceof this.api.errors.ApiError);
});
});
});
@@ -628,9 +691,7 @@ describe('RippleAPI', function() {
describe('schema-validator', function() {
beforeEach(function() {
const schema = schemaValidator.loadSchema(path.join(__dirname,
'./fixtures/schemas/ledgerhash.json'));
schemaValidator.SCHEMAS.ledgerhash = schema;
schemaValidator.SCHEMAS.ledgerhash = ledgerHashSchema;
});
it('valid', function() {
@@ -652,12 +713,6 @@ describe('RippleAPI', function() {
}, this.api.errors.ValidationError);
});
it('load schema error', function() {
assert.throws(function() {
schemaValidator.loadSchema('/bad/file/name');
}, Error);
});
it('schema not found error', function() {
assert.throws(function() {
schemaValidator.schemaValidate('unexisting', 'anything');
@@ -765,6 +820,13 @@ describe('RippleAPI', function() {
});
it('ledgerClosed', function(done) {
this.api.on('ledgerClosed', message => {
checkResult(responses.ledgerClosed, 'ledgerClosed', message);
done();
});
this.api.remote.getServer().emit('message', ledgerClosed);
});
});
describe('RippleAPI - offline', function() {

View File

@@ -7,6 +7,12 @@ module.exports = {
preparePaymentAllOptions: require('./prepare-payment-all-options'),
preparePaymentNoCounterparty: require('./prepare-payment-no-counterparty'),
prepareSettings: require('./prepare-settings'),
prepareSuspendedPaymentCreation:
require('./prepare-suspended-payment-creation'),
prepareSuspendedPaymentExecution:
require('./prepare-suspended-payment-execution'),
prepareSuspendedPaymentCancellation:
require('./prepare-suspended-payment-cancellation'),
prepareTrustline: {
simple: require('./prepare-trustline-simple'),
complex: require('./prepare-trustline')

View File

@@ -0,0 +1,4 @@
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"paymentSequence": 1234
}

View File

@@ -0,0 +1,19 @@
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"allowCancelAfter": 1440694329002
}

View File

@@ -0,0 +1,7 @@
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"paymentSequence": 1234,
"method": 1,
"digest": "8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4",
"proof": "whatever"
}

View File

@@ -47,10 +47,17 @@ module.exports = {
setTransferRate: require('./prepare-settings-set-transfer-rate.json'),
fieldClear: require('./prepare-settings-field-clear.json')
},
prepareSuspendedPaymentCreation:
require('./prepare-suspended-payment-creation'),
prepareSuspendedPaymentExecution:
require('./prepare-suspended-payment-execution'),
prepareSuspendedPaymentCancellation:
require('./prepare-suspended-payment-cancellation'),
prepareTrustline: {
simple: require('./prepare-trustline-simple.json'),
complex: require('./prepare-trustline.json')
},
sign: require('./sign.json'),
submit: require('./submit.json')
submit: require('./submit.json'),
ledgerClosed: require('./ledger-closed.json')
};

View File

@@ -0,0 +1,11 @@
{
"feeBase": 10,
"feeReference": 10,
"ledgerVersion": 14804627,
"ledgerHash": "9141FA171F2C0CE63E609466AF728FF66C12F7ACD4B4B50B0947A7F3409D593A",
"ledgerTimestamp": "2015-07-23T05:50:40.000Z",
"reserveBase": 20000000,
"reserveIncrement": 5000000,
"transactionCount": 19,
"validatedLedgerVersions": "13983423-14804627"
}

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentCancel\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"CancelAfter\":494009529,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -0,0 +1,8 @@
{
"txJSON": "{\"Flags\":0,\"TransactionType\":\"SuspendedPaymentFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Method\":1,\"Digest\":\"8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4\",\"Proof\":\"7768617465766572\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "12",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}

View File

@@ -1,7 +1,10 @@
'use strict';
module.exports = {
submit: require('./submit'),
submit: {
success: require('./submit'),
failure: require('./submit-failed')
},
ledger: require('./ledger'),
ledgerNotFound: require('./ledger-not-found'),
ledgerWithoutCloseTime: require('./ledger-without-close-time'),

View File

@@ -0,0 +1,13 @@
{
"id": 0,
"status": "success",
"type": "response",
"result": {
"success": false,
"engine_result": "temBAD_FEE",
"engine_result_code": 1,
"engine_result_message": "",
"tx_blob": "12000322000000002400000017201B0086955468400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402207660BDEF67105CE1EBA9AD35DC7156BAB43FF1D47633199EE257D70B6B9AAFBF02207F5517BC8AEF2ADC1325897ECDBA8C673838048BCA62F4E98B252F19BE88796D770A726970706C652E636F6D81144FBFF73DA4ECF9B701940F27341FA8020C313443",
"tx_json": {}
}
}

View File

@@ -40,6 +40,7 @@
],
"TransactionIndex": 5,
"TransactionResult": "tesSUCCESS"
}
},
"validated": false
}
}

View File

@@ -181,6 +181,7 @@
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
}
},
"validated": true
}
}

718
test/fixtures/negative-error.json vendored Normal file
View File

@@ -0,0 +1,718 @@
{
"_offers":
[
{
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "8FB8D385FF07349C022524BBD2AC693B38751880CE123505E558ED18FA1043C1",
"PreviousTxnLgrSeq": 15658981,
"Sequence": 3511992,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66435.49665972557"
},
"TakerPays": "71365305157",
"Flags": 0,
"BookNode": "0000000000000000",
"LedgerEntryType": "Offer",
"index": "64DDB33BF3AF700BF8DBD66DDBD7F43495C20B41E55420F5F865538A956999B2",
"quality": "1074203.813756165",
"owner_funds": "770539.7390873457",
"is_fully_funded": true,
"taker_gets_funded": "66436.33517689175",
"taker_pays_funded": "71366164619"
}
],
"_offerCounts":
{
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5": 1
},
"_ownerFundsUnadjusted":
{
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5": "770539.7390873457"
},
"_ownerFunds":
{
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5": "770539.7390873457"
},
"_ownerOffersTotal":
{
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5": {
"_value": "66436.33517689173",
"_is_native": false,
"_currency": {
"_value": {
"limbs": [
0,
3145728,
12336,
0,
0,
0,
0
]
},
"_native": false,
"_type": 0,
"_interest_start": null,
"_interest_period": null,
"_iso_code": "000"
},
"_issuer": {
"_value": {
"limbs": [
0,
0,
0,
0,
0,
0,
0
]
},
"_version_byte": 0
}
}
},
"message1": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_hash": "12E6264EB6D9287171C904DDEF494C4EE0A7D6B4200C1AC9683C45B349B82622",
"ledger_index": 15658982,
"meta": {
"AffectedNodes": [
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "1E5B1F64A949775AC3236139AD28452AF0D32F28D43DBEB0BFEF85D942E69E5A",
"NewFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"OwnerNode": "000000000000000F",
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
}
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "8FB8D385FF07349C022524BBD2AC693B38751880CE123505E558ED18FA1043C1",
"PreviousTxnLgrSeq": 15658981,
"Sequence": 3511992,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66435.49665972557"
},
"TakerPays": "71365305157"
},
"LedgerEntryType": "Offer",
"LedgerIndex": "64DDB33BF3AF700BF8DBD66DDBD7F43495C20B41E55420F5F865538A956999B2"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "669421A08DBE33B9F510ED2AA0C32A71445AA95613BB0DC87DB2A4E6DBF45ED1"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Balance": "1045829766700",
"Flags": 0,
"OwnerCount": 33,
"Sequence": 3512004
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "6FA3F5B750FF159267BA377112BA021DD4941543FEDFB73CDE2EEC1E4E5B17FE",
"PreviousFields": {
"Balance": "1045829776700",
"Sequence": 3512003
},
"PreviousTxnID": "71CB0DAC1149EB3301A45F96D3CB124B2EF911CFBD5F62A209DC9350EE251560",
"PreviousTxnLgrSeq": 15658982
}
},
{
"CreatedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"NewFields": {
"ExchangeRate": "5B03D0FB90BC3D05",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
}
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "5B03D0FBB5C48403",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403"
}
}
],
"TransactionIndex": 16,
"TransactionResult": "tesSUCCESS"
},
"status": "closed",
"transaction": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Fee": "10000",
"Flags": 2147483648,
"LastLedgerSequence": 15658983,
"Memos": [
{
"Memo": {
"MemoType": "3031"
}
}
],
"OfferSequence": 3511992,
"Sequence": 3512003,
"SigningPubKey": "023104AE68E6E6FA6987345A37B8A651E867356947E101E7BFB278541836277D48",
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619",
"TransactionType": "OfferCreate",
"TxnSignature": "304402204B6A793273487D1811D9479B8408A3A0752EAAF0A6F2BC385F275AD2167BCE0402201F6BBD07407499D839F9834662087F508AC06B67C5E049D72A3FF3A55E89829C",
"date": 494604970,
"hash": "F8F042903D4A2AE18F407D2B277EB75FC1C7ED115401ACA54D9A26D96D7F9A98",
"owner_funds": "770539.7390873457"
},
"type": "transaction",
"validated": true,
"mmeta": {
"nodes": [
{
"nodeType": "CreatedNode",
"diffType": "CreatedNode",
"entryType": "Offer",
"ledgerIndex": "1E5B1F64A949775AC3236139AD28452AF0D32F28D43DBEB0BFEF85D942E69E5A",
"fields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"OwnerNode": "000000000000000F",
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
},
"fieldsPrev": {},
"fieldsNew": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"OwnerNode": "000000000000000F",
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
},
"fieldsFinal": {},
"bookKey": "JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
},
{
"nodeType": "DeletedNode",
"diffType": "DeletedNode",
"entryType": "Offer",
"ledgerIndex": "64DDB33BF3AF700BF8DBD66DDBD7F43495C20B41E55420F5F865538A956999B2",
"fields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "8FB8D385FF07349C022524BBD2AC693B38751880CE123505E558ED18FA1043C1",
"PreviousTxnLgrSeq": 15658981,
"Sequence": 3511992,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66435.49665972557"
},
"TakerPays": "71365305157"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "8FB8D385FF07349C022524BBD2AC693B38751880CE123505E558ED18FA1043C1",
"PreviousTxnLgrSeq": 15658981,
"Sequence": 3511992,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66435.49665972557"
},
"TakerPays": "71365305157"
},
"bookKey": "JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
},
{
"nodeType": "ModifiedNode",
"diffType": "ModifiedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "669421A08DBE33B9F510ED2AA0C32A71445AA95613BB0DC87DB2A4E6DBF45ED1",
"fields": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
}
},
{
"nodeType": "ModifiedNode",
"diffType": "ModifiedNode",
"entryType": "AccountRoot",
"ledgerIndex": "6FA3F5B750FF159267BA377112BA021DD4941543FEDFB73CDE2EEC1E4E5B17FE",
"fields": {
"Balance": "1045829766700",
"Sequence": 3512004,
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Flags": 0,
"OwnerCount": 33
},
"fieldsPrev": {
"Balance": "1045829776700",
"Sequence": 3512003
},
"fieldsNew": {},
"fieldsFinal": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Balance": "1045829766700",
"Flags": 0,
"OwnerCount": 33,
"Sequence": 3512004
}
},
{
"nodeType": "CreatedNode",
"diffType": "CreatedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"fields": {
"ExchangeRate": "5B03D0FB90BC3D05",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
},
"fieldsPrev": {},
"fieldsNew": {
"ExchangeRate": "5B03D0FB90BC3D05",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
},
"fieldsFinal": {}
},
{
"nodeType": "DeletedNode",
"diffType": "DeletedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"fields": {
"ExchangeRate": "5B03D0FBB5C48403",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"ExchangeRate": "5B03D0FBB5C48403",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FBB5C48403",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
}
}
],
"_affectedAccounts": [
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN"
],
"_affectedBooks": [
"JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
]
}
},
"lastMessage": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_hash": "91A484E043A0AD506BF84D3FC733B3F1886831F65E23866B15B356392B714261",
"ledger_index": 15658984,
"meta": {
"AffectedNodes": [
{
"DeletedNode": {
"FinalFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "F8F042903D4A2AE18F407D2B277EB75FC1C7ED115401ACA54D9A26D96D7F9A98",
"PreviousTxnLgrSeq": 15658982,
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
},
"LedgerEntryType": "Offer",
"LedgerIndex": "1E5B1F64A949775AC3236139AD28452AF0D32F28D43DBEB0BFEF85D942E69E5A"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "669421A08DBE33B9F510ED2AA0C32A71445AA95613BB0DC87DB2A4E6DBF45ED1"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Balance": "1045829696700",
"Flags": 0,
"OwnerCount": 33,
"Sequence": 3512011
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "6FA3F5B750FF159267BA377112BA021DD4941543FEDFB73CDE2EEC1E4E5B17FE",
"PreviousFields": {
"Balance": "1045829706700",
"Sequence": 3512010
},
"PreviousTxnID": "D8518B78A0C6643A79283247BF09DB85F428D80FCF0268242A899482E23F11CE",
"PreviousTxnLgrSeq": 15658984
}
},
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "709FAE8F56B15C9C3326D8D5D0DF461C17BD5E97C909D46CE366DEE2BC227F0F",
"NewFields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"OwnerNode": "000000000000000F",
"Sequence": 3512010,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66493.18081187701"
},
"TakerPays": "71366164172"
}
}
},
{
"CreatedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"NewFields": {
"ExchangeRate": "5B03D025BE99FECC",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
}
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "5B03D0FB90BC3D05",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05"
}
}
],
"TransactionIndex": 9,
"TransactionResult": "tesSUCCESS"
},
"status": "closed",
"transaction": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Fee": "10000",
"Flags": 2147483648,
"LastLedgerSequence": 15658985,
"Memos": [
{
"Memo": {
"MemoType": "3031"
}
}
],
"OfferSequence": 3512003,
"Sequence": 3512010,
"SigningPubKey": "023104AE68E6E6FA6987345A37B8A651E867356947E101E7BFB278541836277D48",
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66493.18081187701"
},
"TakerPays": "71366164172",
"TransactionType": "OfferCreate",
"TxnSignature": "304402201DE5CFA82F4CCBF1A987EDCB63EC95EFCC4FC7F167B942FC78CA68C459252D6B02205690058F976A49EF7034FD6958CA02889288782C81A8FEE83A791BA1A974336E",
"date": 494604980,
"hash": "68C33D8465B2F7942D118679CC73976988725CC057F6D0E22413B4E5A0A64087",
"owner_funds": "770539.7390873457"
},
"type": "transaction",
"validated": true,
"mmeta": {
"nodes": [
{
"nodeType": "DeletedNode",
"diffType": "DeletedNode",
"entryType": "Offer",
"ledgerIndex": "1E5B1F64A949775AC3236139AD28452AF0D32F28D43DBEB0BFEF85D942E69E5A",
"fields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "F8F042903D4A2AE18F407D2B277EB75FC1C7ED115401ACA54D9A26D96D7F9A98",
"PreviousTxnLgrSeq": 15658982,
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "000000000000000F",
"PreviousTxnID": "F8F042903D4A2AE18F407D2B277EB75FC1C7ED115401ACA54D9A26D96D7F9A98",
"PreviousTxnLgrSeq": 15658982,
"Sequence": 3512003,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66436.33517689175"
},
"TakerPays": "71366164619"
},
"bookKey": "JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
},
{
"nodeType": "ModifiedNode",
"diffType": "ModifiedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "669421A08DBE33B9F510ED2AA0C32A71445AA95613BB0DC87DB2A4E6DBF45ED1",
"fields": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"Flags": 0,
"IndexPrevious": "0000000000000001",
"Owner": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"RootIndex": "77FF082487FAF8E65296292EBD5779AC4283909E2E171DFB1BE69F09B765D882"
}
},
{
"nodeType": "ModifiedNode",
"diffType": "ModifiedNode",
"entryType": "AccountRoot",
"ledgerIndex": "6FA3F5B750FF159267BA377112BA021DD4941543FEDFB73CDE2EEC1E4E5B17FE",
"fields": {
"Balance": "1045829696700",
"Sequence": 3512011,
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Flags": 0,
"OwnerCount": 33
},
"fieldsPrev": {
"Balance": "1045829706700",
"Sequence": 3512010
},
"fieldsNew": {},
"fieldsFinal": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"Balance": "1045829696700",
"Flags": 0,
"OwnerCount": 33,
"Sequence": 3512011
}
},
{
"nodeType": "CreatedNode",
"diffType": "CreatedNode",
"entryType": "Offer",
"ledgerIndex": "709FAE8F56B15C9C3326D8D5D0DF461C17BD5E97C909D46CE366DEE2BC227F0F",
"fields": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"OwnerNode": "000000000000000F",
"Sequence": 3512010,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66493.18081187701"
},
"TakerPays": "71366164172"
},
"fieldsPrev": {},
"fieldsNew": {
"Account": "rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"BookDirectory": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"OwnerNode": "000000000000000F",
"Sequence": 3512010,
"TakerGets": {
"currency": "JPY",
"issuer": "r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN",
"value": "66493.18081187701"
},
"TakerPays": "71366164172"
},
"fieldsFinal": {},
"bookKey": "JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
},
{
"nodeType": "CreatedNode",
"diffType": "CreatedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"fields": {
"ExchangeRate": "5B03D025BE99FECC",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
},
"fieldsPrev": {},
"fieldsNew": {
"ExchangeRate": "5B03D025BE99FECC",
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D025BE99FECC",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098"
},
"fieldsFinal": {}
},
{
"nodeType": "DeletedNode",
"diffType": "DeletedNode",
"entryType": "DirectoryNode",
"ledgerIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"fields": {
"ExchangeRate": "5B03D0FB90BC3D05",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
},
"fieldsPrev": {},
"fieldsNew": {},
"fieldsFinal": {
"ExchangeRate": "5B03D0FB90BC3D05",
"Flags": 0,
"RootIndex": "9F72CA02AB7CBA0FD97EA5F245C03EDC555C3FE97749CD425B03D0FB90BC3D05",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "5BBC0F22F61D9224A110650CFE21CC0C4BE13098",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000"
}
}
],
"_affectedAccounts": [
"rBztfz5wmDXXgB3KQd5LgtbHZz28KGpYP5",
"r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN"
],
"_affectedBooks": [
"JPY/r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN:XRP"
]
}
}
}

View File

@@ -1,10 +1,13 @@
/*eslint-disable max-len, no-param-reassign*/
/* eslint-disable max-len, no-param-reassign */
'use strict';
const _ = require('lodash');
const addresses = require('./addresses');
const Meta = require('ripple-lib').Meta;
const SerializedObject = require('ripple-lib').SerializedObject;
const Types = require('ripple-lib').types;
const IOUValue = require('ripple-lib')._test.IOUValue;
module.exports.FIAT_BALANCE = '10';
module.exports.NATIVE_BALANCE = '55';
@@ -809,6 +812,7 @@ module.exports.transactionWithInvalidAccountRoot = function(options) {
};
};
module.exports.transactionWithCreatedOffer = function(options) {
options = options || {};
_.defaults(options, {
@@ -816,6 +820,15 @@ module.exports.transactionWithCreatedOffer = function(options) {
amount: '1.9951'
});
const takerGets = new IOUValue(options.amount);
const takerPays = new IOUValue(module.exports.TAKER_PAYS);
const quality = takerPays.divide(takerGets);
const so = new SerializedObject();
Types.Quality.serialize(so, quality);
const BookDirectory = so.to_hex();
const meta = new Meta({
AffectedNodes: [
{
@@ -824,7 +837,7 @@ module.exports.transactionWithCreatedOffer = function(options) {
LedgerIndex: 'AF3C702057C9C47DB9E809FD8C76CD22521012C5CC7AE95D914EC9E226F1D7E5',
NewFields: {
Account: options.account,
BookDirectory: '7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F211CEE1E0697A0',
BookDirectory: BookDirectory,
Flags: 131072,
Sequence: 1404,
TakerGets: {

View File

@@ -205,7 +205,11 @@ module.exports = function(port) {
mock.on('request_submit', function(request, conn) {
assert.strictEqual(request.command, 'submit');
conn.send(createResponse(request, fixtures.submit));
if (request.tx_blob === 'BAD') {
conn.send(createResponse(request, fixtures.submit.failure));
} else {
conn.send(createResponse(request, fixtures.submit.success));
}
});
mock.on('request_account_lines', function(request, conn) {

View File

@@ -1,19 +1,30 @@
/*eslint-disable max-len */
/* eslint-disable max-len */
'use strict';
var _ = require('lodash');
var assert = require('assert-diff');
var Remote = require('ripple-lib').Remote;
var Currency = require('ripple-lib').Currency;
var addresses = require('./fixtures/addresses');
var fixtures = require('./fixtures/orderbook');
const _ = require('lodash');
const assert = require('assert-diff');
const Remote = require('ripple-lib').Remote;
const Currency = require('ripple-lib').Currency;
const addresses = require('./fixtures/addresses');
const fixtures = require('./fixtures/orderbook');
const IOUValue = require('ripple-lib')._test.IOUValue;
describe('OrderBook Autobridging', function() {
this.timeout(0);
function createRemote() {
const remote = new Remote();
remote.isConnected = function() {
return true;
};
return remote;
}
it('Initialize IOU/IOU', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
@@ -27,23 +38,24 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -58,25 +70,26 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg one partially funded', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -88,25 +101,26 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg two partially funded', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].owner_funds = '10';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -118,25 +132,26 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg two transfer rate', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1002000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].owner_funds = '10';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
@@ -146,19 +161,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - taker funds < leg two in', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '33461561812';
@@ -169,6 +184,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -180,19 +196,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg one partially funded - owners equal', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
@@ -201,6 +217,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -212,19 +229,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
@@ -236,6 +253,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -247,23 +265,24 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg one consumes leg two fully', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
@@ -280,19 +299,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - leg two consumes first leg one offer fully', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 2));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 2));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].TakerGets.value = '170.7639524223001';
legTwoOffers[0].TakerPays = '49439476610';
@@ -301,6 +320,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
@@ -317,19 +337,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - owners equal', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1002000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
legOneOffers[0].owner_funds = '2105863129';
legTwoOffers[1].owner_funds = '19.32660005780981';
@@ -339,6 +359,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
@@ -355,19 +376,19 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - owners equal - leg one overfunded', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1002000000);
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
const legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
const legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
legOneOffers[0].owner_funds = '41461561812';
@@ -378,6 +399,7 @@ describe('OrderBook Autobridging', function() {
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
@@ -394,20 +416,21 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '75',
TakerPays: {
value: '50',
@@ -422,6 +445,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
@@ -433,6 +457,7 @@ describe('OrderBook Autobridging', function() {
}
]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
@@ -444,20 +469,21 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - update funded amount', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '100',
TakerPays: {
value: '100',
@@ -469,6 +495,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '50',
TakerPays: {
value: '100',
@@ -482,6 +509,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
@@ -493,6 +521,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
@@ -504,6 +533,7 @@ describe('OrderBook Autobridging', function() {
}
]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 3);
@@ -525,20 +555,21 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - update funded amount - owners equal', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '100',
TakerPays: {
value: '100',
@@ -550,6 +581,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '20',
TakerPays: {
value: '100',
@@ -563,6 +595,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
@@ -574,6 +607,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
@@ -585,6 +619,7 @@ describe('OrderBook Autobridging', function() {
}
]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 3);
@@ -606,20 +641,21 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - update funded amount - first two owners equal', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '100',
TakerPays: {
value: '100',
@@ -631,6 +667,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '100',
TakerPays: {
value: '200',
@@ -644,6 +681,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
@@ -655,6 +693,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
@@ -665,6 +704,7 @@ describe('OrderBook Autobridging', function() {
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '20',
issuer: addresses.ISSUER,
@@ -676,6 +716,7 @@ describe('OrderBook Autobridging', function() {
}
]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 4);
@@ -702,20 +743,21 @@ describe('OrderBook Autobridging', function() {
});
it('Compute autobridged offers - unfunded offer - owners equal', function() {
var book = new Remote().createOrderBook({
const book = createRemote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook._issuerTransferRate = new IOUValue(1000000000);
book._legTwoBook._issuerTransferRate = new IOUValue(1000000000);
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: '75',
TakerPays: {
value: '75',
@@ -730,6 +772,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
@@ -741,6 +784,7 @@ describe('OrderBook Autobridging', function() {
}
]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* eslint-disable no-new, max-len, no-comma-dangle, indent */
/* eslint-disable no-new, max-len, no-comma-dangle, indent, max-nested-callbacks */
'use strict';
@@ -14,7 +14,9 @@ const Amount = require('ripple-lib').Amount;
const PathFind = require('ripple-lib')._test.PathFind;
const Log = require('ripple-lib')._test.Log;
let options, remote, callback;
let options;
let remote;
let callback;
const ADDRESS = 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS';
const LEDGER_INDEX = 9592219;
@@ -2057,4 +2059,88 @@ describe('Remote', function() {
RegularKey: TX_JSON.Destination
});
});
it('Construct SignerListSet transaction', function() {
const tx = remote.createTransaction('SignerListSet', {
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
signerQuorum: 3,
signers: [
{
account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
weight: 1
},
{
account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
weight: 2
}
]
});
assert(tx instanceof Transaction);
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SignerListSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
SignerQuorum: 3,
SignerEntries: [
{
SignerEntry: {
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
SignerWeight: 1
}
},
{
SignerEntry: {
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
SignerWeight: 2
}
}
]
});
});
it('Construct SuspendedPaymentCreate transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentCreate', {
account: TX_JSON.Account,
destination: TX_JSON.Destination,
amount: TX_JSON.Amount
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCreate',
Account: TX_JSON.Account,
Destination: TX_JSON.Destination,
Amount: TX_JSON.Amount
});
});
it('Construct SuspendedPaymentFinish transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentFinish', {
account: TX_JSON.Account,
owner: TX_JSON.Account,
paymentSequence: 1234
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentFinish',
Account: TX_JSON.Account,
Owner: TX_JSON.Account,
OfferSequence: 1234
});
});
it('Construct SuspendedPaymentCancel transaction', function() {
const tx = remote.createTransaction('SuspendedPaymentCancel', {
account: TX_JSON.Account,
owner: TX_JSON.Account,
paymentSequence: 1234
});
assert.deepEqual(tx.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCancel',
Account: TX_JSON.Account,
Owner: TX_JSON.Account,
OfferSequence: 1234
});
});
});

View File

@@ -47,6 +47,11 @@ describe('Request', function() {
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'server_info');
done();
},
on: function() {
},
isConnected: function() {
return true;
}
};

View File

@@ -1,9 +1,10 @@
/* eslint-disable max-len */
/* eslint-disable max-len, max-nested-callbacks */
'use strict';
const assert = require('assert');
const lodash = require('lodash');
const ripple = require('ripple-lib');
const Transaction = require('ripple-lib').Transaction;
const TransactionQueue = require('ripple-lib').TransactionQueue;
const Remote = require('ripple-lib').Remote;
@@ -412,6 +413,7 @@ describe('Transaction', function() {
const remote = new Remote();
const transaction = new Transaction(remote);
transaction.setSecret('shK5kH88MZPaCpCNmzmzpLY58xKS9');
remote.trusted = false;
remote.local_signing = false;
@@ -445,7 +447,6 @@ describe('Transaction', function() {
remote.trusted = true;
remote.local_signing = true;
transaction.SigningPubKey = undefined;
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoijX';
transaction.once('error', function(err) {
@@ -571,8 +572,8 @@ describe('Transaction', function() {
tx.complete();
tx.sign();
assert.strictEqual(tx_json.SigningPubKey, expectedPub);
assert.strictEqual(tx_json.TxnSignature, expectedSig);
assert.strictEqual(tx.tx_json.SigningPubKey, expectedPub);
assert.strictEqual(tx.tx_json.TxnSignature, expectedSig);
});
});
@@ -1903,6 +1904,45 @@ describe('Transaction', function() {
});
});
it('Construct SignerListSet transaction', function() {
const transaction = new Transaction().setSignerList({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
signerQuorum: 3,
signers: [
{
account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
weight: 1
},
{
account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
weight: 2
}
]
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SignerListSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
SignerQuorum: 3,
SignerEntries: [
{
SignerEntry: {
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
SignerWeight: 1
}
},
{
SignerEntry: {
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
SignerWeight: 2
}
}
]
});
});
it('Submit transaction', function(done) {
const remote = new Remote();
const transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
@@ -2066,4 +2106,192 @@ describe('Transaction', function() {
queue.remove(tx);
});
});
it('Add multisigner', function() {
const transaction = new Transaction();
const s1 = {
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
TxnSignature: '304402203020865BDC995431325C371E6A3CE89BFC40597D9CFAF77DBB16E9D159824EA402203645A6462A6DCEC7B5D0811882DC54CEA66258A227A2762BE6EFCD9EB62C27BF',
SigningPubKey: '02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE'
};
const s2 = {
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
TxnSignature: '30450221009C84E455DC199A7DB4B800D68C92269D60972E8850AFC0D50B1AE6B08BBB02EA02206FA93A560BE96844DF7D96D07F6400EF9534A32FBA352DD10E855DA8923A3AF8',
SigningPubKey: '028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EAAFEC466F5C095407BF'
};
transaction.addMultiSigner(s1);
transaction.addMultiSigner(s2);
assert.deepEqual(transaction.getMultiSigners(), [
{Signer: s2}, {Signer: s1}]);
});
it('Get multisign data', function() {
const transaction = Transaction.from_json({
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
Sequence: 1,
Fee: '100',
TransactionType: 'AccountSet',
Flags: 0
});
transaction.setSigningPubKey('');
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
const d1 = transaction.multiSigningData(a1);
const tbytes = ripple.SerializedObject.from_json(
lodash.merge(transaction.tx_json, {SigningPubKey: ''})).buffer;
const abytes = ripple.UInt160.from_json(a1).to_bytes();
const prefix = require('ripple-lib')._test.HashPrefixes.HASH_TX_MULTISIGN_BYTES;
assert.deepEqual(d1.buffer, prefix.concat(tbytes, abytes));
});
it('Multisign', function() {
const transaction = Transaction.from_json({
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
Sequence: 1,
Fee: '100',
TransactionType: 'AccountSet',
Flags: 0
});
const multiSigningJson = transaction.getMultiSigningJson();
const t1 = Transaction.from_json(multiSigningJson);
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
const a2 = 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW';
const s1 = t1.multiSign(a1, 'alice');
assert.strictEqual(s1.Account, a1);
assert.strictEqual(s1.SigningPubKey, '0388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97C052');
assert.strictEqual(s1.TxnSignature, '30440220611256E46B2946152695FFEF34D5C71BB3AE569C3D919A270BFBCA9ADF260D9202202FAE24FC8A575FE3265A6D7CFA596094A7950E0011706431A11C2A9ABEF60B3B');
const s2 = t1.multiSign(a2, 'bob');
assert.strictEqual(s2.Account, a2);
assert.strictEqual(s2.SigningPubKey, '02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE');
assert.strictEqual(s2.TxnSignature, '3044022067F769BE0A4CC2B4F26E7B52B366F861FED02DA0F564F98B44009C8181A9655702206D882919139DF8E9D7F2FC1DD54D8B4FEAC40203349AE21519FD388925A4DE83');
transaction.addMultiSigner(s1);
transaction.addMultiSigner(s2);
assert.deepEqual(transaction.getMultiSigners(), [
{Signer: s2},
{Signer: s1}
]);
});
it('Construct SuspendedPaymentCreate transaction', function() {
const transaction = new Transaction().suspendedPaymentCreate({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
destination: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
amount: '1/USD/r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCreate',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Destination: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
Amount: {
value: '1',
currency: 'USD',
issuer: 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
}
});
});
it('Set Digest', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Digest, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setDigest('foo');
}, /Error: Digest must be a valid Hash256/);
const hash = '8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4';
transaction.setDigest(hash);
assert.strictEqual(transaction.tx_json.Digest, hash);
});
it('Set CancelAfter', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.CancelAfter, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setAllowCancelAfter('foo');
}, /Error: CancelAfter must be a valid UInt32/);
transaction.setAllowCancelAfter(1441043377523);
assert.strictEqual(transaction.tx_json.CancelAfter, 494358578);
});
it('Set FinishAfter', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.FinishAfter, undefined);
transaction.setType('SuspendedPaymentCreate');
assert.throws(function() {
transaction.setAllowExecuteAfter('foo');
}, /Error: FinishAfter must be a valid UInt32/);
transaction.setAllowExecuteAfter(1441043377523);
assert.strictEqual(transaction.tx_json.FinishAfter, 494358578);
});
it('Construct SuspendedPaymentFinish transaction', function() {
const transaction = new Transaction().suspendedPaymentFinish({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
paymentSequence: 1234
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentFinish',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
OfferSequence: 1234
});
});
it('Set Method', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Method, undefined);
transaction.setType('SuspendedPaymentFinish');
assert.throws(function() {
transaction.setMethod('foo');
}, /Error: Method must be a valid UInt8/);
transaction.setMethod(1);
assert.strictEqual(transaction.tx_json.Method, 1);
});
it('Set Proof', function() {
const transaction = new Transaction();
assert.strictEqual(transaction.tx_json.Proof, undefined);
transaction.setType('SuspendedPaymentFinish');
transaction.setProof('foo');
assert.strictEqual(transaction.tx_json.Proof, '666F6F');
});
it('Construct SuspendedPaymentCancel transaction', function() {
const transaction = new Transaction().suspendedPaymentCancel({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
paymentSequence: 1234
});
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'SuspendedPaymentCancel',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
Owner: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
OfferSequence: 1234
});
});
});