Compare commits

...

81 Commits
0.9.1 ... 0.9.4

Author SHA1 Message Date
Geert Weening
12e428733a [TASK] bump version to 0.9.4 2014-12-05 15:48:24 -08:00
Geert Weening
9cc6ad09a9 [DOC] update release notes 2014-12-05 15:47:46 -08:00
Geert Weening
84abb5962e [TASK] bump ripple-lib to 0.9.4-rc2 2014-12-04 12:56:17 -08:00
Geert Weening
4bba55d2dc Revert "[FEATURE] improve memo support"
This reverts commit 89adcf4f4e.
2014-12-04 12:54:48 -08:00
Geert Weening
b4cabad44e [TASK] bump version to 0.9.4-rc1 2014-12-04 11:03:01 -08:00
Geert Weening
28cc0f9e3b [DOC] update release notes 2014-12-04 11:02:20 -08:00
wltsmrz
95a2cc18fe Merge pull request #213 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-02 00:00:10 -08:00
Geert Weening
8e315a9859 [DOC] update generate wallet example
to take advantage of randomness collected from a rippled
2014-12-01 17:54:34 -08:00
Geert Weening
89adcf4f4e [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat
2014-12-01 09:48:56 -08:00
Geert Weening
3a6c5e41c9 Merge pull request #217 from ripple/orderbook-cleanup
Cleanup, normalize offers from book_offers and transaction stream
2014-11-30 14:14:01 -08:00
wltsmrz
86ed24b94c Cleanup, normalize offers from book_offers and transaction stream 2014-11-29 15:24:15 -08:00
wltsmrz
c792c471c3 Merge pull request #215 from ripple/fix-precision-rounding
Fix to_human precision rounding
2014-11-26 18:31:29 -08:00
wltsmrz
e371cc2c3c Fix to_human precision rounding 2014-11-26 11:32:15 -08:00
Geert Weening
ccf218c8f0 Merge pull request #214 from ripple/fix-fractional-drops
Fix fractional drops in funded taker_pays setter
2014-11-26 09:16:55 -08:00
wltsmrz
0d7fc0a573 Fix fractional drops in funded taker_pays setter 2014-11-25 21:10:57 -08:00
Geert Weening
74cacd5209 [DOC] update offer example 2014-11-19 18:04:45 -08:00
Geert Weening
bb79cf2a87 [TASK] bump version to 0.9.3 2014-11-19 12:09:18 -08:00
Geert Weening
28451df1a8 Merge branch 'develop' into release 2014-11-19 11:32:19 -08:00
Geert Weening
38e288f62a [TASK] bump version to 0.9.2-rc3 2014-11-19 11:31:19 -08:00
Geert Weening
905f908450 [DOC] update release notes 2014-11-19 11:31:19 -08:00
wltsmrz
672171fd0c Merge pull request #211 from jks-liu/fix-link
Fix link in README
2014-11-19 05:24:58 -08:00
Jks Liu
520660ecbc Fix link in README 2014-11-19 16:42:25 +08:00
Geert Weening
06acb5faf2 [TASK] bump version to 0.9.2-rc3 2014-11-18 11:58:41 -08:00
Geert Weening
d43fa03f05 [DOC] update release notes 2014-11-18 11:57:49 -08:00
Geert Weening
baed1aaf92 Merge branch 'release' into develop 2014-11-18 11:54:17 -08:00
wltsmrz
cc229e803c Merge pull request #210 from geertweening/fix/max_fee
[TASK] change default `max_fee` for Remote to 1 XRP
2014-11-18 11:48:37 -08:00
Geert Weening
d6b1728c23 [TASK] change default max_fee for Remote to 1 XRP 2014-11-18 10:47:28 -08:00
wltsmrz
bc5dcc359c Merge pull request #209 from ximinez/ledger_accept
Request ledger_accept returns the Request, not Remote
2014-11-17 12:08:38 -08:00
Edward Hennis
ced07e1d6b Request ledger_accept returns the Request, not Remote 2014-11-17 15:02:45 -05:00
Geert Weening
cffffd9591 [TASK] bump version to 0.9.3-rc2 2014-11-14 10:24:44 -08:00
Geert Weening
b8766e263f [DOC] update release notes 2014-11-14 10:24:00 -08:00
Geert Weening
fc426d5764 Merge branch 'release' into develop 2014-11-14 10:16:34 -08:00
Geert Weening
056d2381cd Merge pull request #208 from ripple/relocate-presubmit
Relocate presubmit emission to immediately before transaction submit
2014-11-14 10:14:02 -08:00
Geert Weening
2932a0ec5f [DOC] add doc that accountRequest throws
if a marker is provided, but no ledger_index or ledger_hash
2014-11-14 10:11:26 -08:00
Geert Weening
d3d85a3fcf [DOC] add doc that accountRequest throws
if a marker is provided, but no ledger_index or ledger_hash
2014-11-14 10:11:13 -08:00
wltsmrz
7a1feaa897 Relocate presubmit emission to immediately before transaction submit 2014-11-13 21:44:20 -08:00
Geert Weening
5f3cf72cc6 Merge pull request #207 from shekenahglory/develop
[TASK] binformat: update fields to match rippled
2014-11-13 14:13:52 -08:00
Matthew Fettig
cae980788e binformat: update fields to match rippled 2014-11-13 11:14:46 -08:00
Geert Weening
df763b8765 Merge pull request #205 from ripple/core-build
Add core build
2014-11-12 10:51:45 -08:00
wltsmrz
365085809e Add note on restricted browser builds 2014-11-12 03:04:51 -08:00
Geert Weening
3ee7998261 [TASK] bump version to 0.9.3-rc1 2014-11-11 17:52:30 -08:00
Geert Weening
6fb9ed8312 [DOC] update release notes 2014-11-11 17:52:13 -08:00
Geert Weening
89f79c35f5 Merge pull request #206 from geertweening/fix/tec_wait_for_validated
[TASK] wait for validation before returning tec error
2014-11-11 17:49:38 -08:00
Geert Weening
6bdd4b2670 [TASK] wait for validation before returning tec error 2014-11-11 16:54:25 -08:00
Geert Weening
acd79d19e2 [TASK] bump version to 0.9.2 2014-11-11 12:14:04 -08:00
Geert Weening
674d4a957d [TASK] bump version to 0.9.2-rc6 2014-11-11 11:51:54 -08:00
Geert Weening
bdbf264771 [FIX] support string '0' being interpreted as XRP 2014-11-11 11:51:12 -08:00
wltsmrz
8f17873da2 Remove server._computeFee(Transaction), require fee units argument 2014-11-11 06:24:34 -08:00
wltsmrz
b0cac776ee Throw an error when trying to use unavailable class in WebPack build 2014-11-11 06:23:37 -08:00
wltsmrz
625dba4d85 Add build-core gulp task 2014-11-11 05:30:50 -08:00
Geert Weening
261b72d0fc [DOC] update API reference 2014-11-10 14:32:35 -08:00
Geert Weening
b5b167ef6d [DOC] update README and GUIDES
to match current API's
2014-11-10 10:14:11 -08:00
Geert Weening
66d21b24cd [TASK] bump ripple-lib to 0.9.2-rc5 2014-11-07 18:22:23 -08:00
Geert Weening
5a084ea3cc [TEST] fix broken tests
as a result of updating account_info request in account.js
2014-11-07 18:22:05 -08:00
Geert Weening
486944fa4c [FIX] request account info in account.js 2014-11-07 18:04:04 -08:00
Geert Weening
b63a76d298 [TASK] bump version to 0.9.2-rc4 2014-11-07 17:45:17 -08:00
Geert Weening
31045039c0 [DOC] update release notes 2014-11-07 17:44:30 -08:00
Geert Weening
6f5d1104aa [TASK] change accountRequest method signature
`(type, options, callback)` where the last argument will be considered for callback if it's a function
2014-11-07 17:08:01 -08:00
Geert Weening
3c9660203b Merge pull request #203 from geertweening/feature/marker_valid_ledger
[FEATURE] check for valid ledger when using marker
2014-11-07 12:57:57 -08:00
Geert Weening
29e1423f84 [FEATURE] check for valid ledger when using marker
when using a marker on an account request, a valid ledger_index or ledger_hash is required, otherwise the results can't be guaranteed to be reliable

consolidated test values for addresses, indexes and hashes
2014-11-07 11:18:24 -08:00
Geert Weening
e42e67e259 [TASK] consolidate accountRequest and pagingAccountRequest
update `requestAccountCurrencies` and  `requestAccountInfo`
2014-11-07 09:52:17 -08:00
Geert Weening
ed018282c4 [TASK] remove clone in favor of copy and update
see 'object' case in `parse_json`
2014-11-06 22:58:04 -08:00
Geert Weening
fbe015758c [TASK] bump version to 0.9.2-rc3 2014-11-06 21:48:01 -08:00
Geert Weening
7e24a81764 [DOC] update release notes 2014-11-06 21:47:44 -08:00
Geert Weening
9ab77e90fe Merge pull request #202 from geertweening/feature/paging_account_request
[FEATURE] add paging account request
2014-11-06 21:41:44 -08:00
Geert Weening
ae3ed699db Merge pull request #201 from geertweening/fix/from_human_full_name_native
[FIX] from_human 'XRP' with full name
2014-11-06 21:41:30 -08:00
Geert Weening
0c22a9753e Merge pull request #199 from geertweening/fix/amount_cap
[FIX] cap IOU Amounts to their max and min value
2014-11-06 21:41:08 -08:00
Geert Weening
a447f6b723 [TEST] add missing testcases for interest bearing currencies
- `get_interest_at` by providing a Date object
- `get_interest_at` for a Currency without interest
2014-11-06 21:38:00 -08:00
Geert Weening
a8ef614b81 [FIX] Currency constructor with Currency object
clone the given Currency instance in `parse_json`
2014-11-06 21:36:57 -08:00
Geert Weening
9025e8bfa8 [FIX] get interest for currency without interest
- `get_interest_at` for a currency without interest would not hit the intended check since the function was used as variable instead of calling the method
2014-11-06 21:35:41 -08:00
Geert Weening
722f4e175d [FEATURE] add paging account request
some requests return results that can be paged through, e.g. `account_lines`
use `limit` and `marker` options to specify results per response and position

change `requestAccountLines` and `requestAccountOffers` to use the `pagingAccountRequest`
2014-11-06 18:40:45 -08:00
Geert Weening
1ad6e5a15f [FIX] from_human 'XRP' with full name
from_human 'XRP - Ripples' should result in native XRP
2014-11-05 16:54:45 -08:00
Geert Weening
3554572db7 [TASK] bump version to 0.9.2-rc2 2014-11-05 16:32:37 -08:00
Geert Weening
f1abff962f Merge pull request #200 from boxbag/fix-max-fee
[FIX] fix test and do not set tx_json.Fee in maxFee method
2014-11-05 16:07:01 -08:00
Geert Weening
f05941fbc4 [FIX] cap IOU Amounts to their max and min value
respect rippled's limits
2014-11-05 16:04:02 -08:00
Bo Chen
237c46d5a0 [FIX] fix test and do not set tx_json.Fee in maxFee method 2014-11-05 15:59:57 -08:00
Geert Weening
76cfb69d9f [TASK] bump version to 0.9.2-rc1 2014-11-05 14:38:47 -08:00
Geert Weening
7610df0fbb [DOC] update release notes 2014-11-05 14:38:35 -08:00
Geert Weening
8bc935aa62 Merge pull request #198 from boxbag/transaction-max-fee
[FEATURE] set max fee the submitter of a transaction is willing to pay
2014-11-05 09:27:26 -08:00
Bo Chen
24587fab9c [FEATURE] set max fee the submitter of a transaction is willing to pay 2014-11-04 14:13:42 -08:00
Geert Weening
0248475473 [DOC] update description in package.json 2014-10-31 18:52:09 -07:00
24 changed files with 1227 additions and 539 deletions

View File

@@ -68,45 +68,6 @@ gulp.task('build', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({version: pkg.version}))
.pipe(gulp.dest('./dist/'));
});
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.src('./package.json')
.pipe(bump({type:argv.type}))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({version: pkg.version+'-beta'}))
.pipe(gulp.dest('./'));
});
gulp.task('build-min', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(uglify())
@@ -128,6 +89,66 @@ gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
/**
* Generate a WebPack external for a given unavailable module which replaces
* that module's constructor with an error-thrower
*/
function buildUseError(cons) {
return 'var {<CONS>:function(){throw new Error("Class is unavailable in this build: <CONS>")}}'
.replace(new RegExp('<CONS>', 'g'), cons);
};
gulp.task('build-core', [ 'concat-sjcl' ], function(callback) {
webpack({
entry: [
'./src/js/ripple/remote.js'
],
externals: [
{
'./transaction': buildUseError('Transaction'),
'./orderbook': buildUseError('OrderBook'),
'./account': buildUseError('Account'),
'./serializedobject': buildUseError('SerializedObject')
}
],
output: {
library: 'ripple',
path: './build/',
filename: [ 'ripple-', '-core.js' ].join(pkg.version)
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({ version: pkg.version }))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.task('lint', function() {
gulp.src('src/js/ripple/*.js')
.pipe(jshint())
@@ -158,6 +179,20 @@ gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.src('./package.json')
.pipe(bump({ type: argv.type }))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({ version: pkg.version + '-beta' }))
.pipe(gulp.dest('./'));
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);

View File

@@ -1,3 +1,57 @@
##0.9.4
+ [Normalize offers from book_offers and transaction stream](https://github.com/ripple/ripple-lib/commit/86ed24b94cf7c8929c87db3a63e9bbea7f767e9c)
+ [Fix: Amount.to_human() precision rounding](https://github.com/ripple/ripple-lib/commit/e371cc2c3ceccb3c1cfdf18b98d80093147dd8b2)
+ [Fix: fractional drops in funded taker_pays setter](https://github.com/ripple/ripple-lib/commit/0d7fc0a573a144caac15dd13798b23eeb1f95fb4)
##0.9.3
+ [Change `presubmit` to emit immediately before transaction submit](https://github.com/ripple/ripple-lib/commit/7a1feaa89701bf861ab31ebd8ffdc8d8d1474e29)
+ [Add a "core" browser build of ripple-lib which has a subset of features and smaller file size](https://github.com/ripple/ripple-lib/pull/205)
+ [Update binformat with missing fields from rippled](https://github.com/ripple/ripple-lib/commit/cae980788efb00191bfd0988ed836d60cdf7a9a2)
+ [Wait for transaction validation before returning `tec` error](https://github.com/ripple/ripple-lib/commit/6bdd4b2670906588852fc4dda457607b4aac08e4)
+ [Change default `max_fee` on `Remote` to `1 XRP`](https://github.com/ripple/ripple-lib/commit/d6b1728c23ff85c3cc791bed6982a750641fd95f)
+ [Fix: Request ledger_accept should return the Remote](https://github.com/ripple/ripple-lib/pull/209)
##0.9.2
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add max_fee setter to transactions to set max fee the submitter is willing to pay] (https://github.com/ripple/ripple-lib/commit/24587fab9c8ad3840d7aa345a7037b48839e09d7)
+ [Fix: cap IOU Amounts to their max and min value] (https://github.com/ripple/ripple-lib/commit/f05941fbc46fdb7c6fe7ad72927af02d527ffeed)
Example on how to use paging with `account_offers`:
```
// A valid `ledger_index` or `ledger_hash` is required to provide a reliable result.
// Results can change between ledger closes, so the provided ledger will be used as base.
var options = {
account: < rippleAccount >,
limit: < Number between 10 and 400 >,
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;
}
var request = remote.requestAccountOffers(options);
```
[Full working example](https://github.com/geertweening/ripple-lib-scripts/blob/master/account_offers_paging.js)
##0.9.1
+ Switch account requests to use ledgerSelect rather than ledgerChoose ([278df90](https://github.com/ripple/ripple-lib/commit/278df9025a20228de22379a53c76ca12d40fa591))

View File

@@ -1,6 +1,6 @@
#ripple-lib
JavaScript client for [rippled](https://github.com/ripple/rippled)
A JavaScript API for interacting with Ripple in Node.js and the browser
[![Build Status](https://travis-ci.org/ripple/ripple-lib.svg?branch=develop)](https://travis-ci.org/ripple/ripple-lib) [![Coverage Status](https://coveralls.io/repos/ripple/ripple-lib/badge.png?branch=develop)](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
@@ -15,9 +15,9 @@ JavaScript client for [rippled](https://github.com/ripple/rippled)
###In this file
1. [Installation](README.md#installation)
2. [Quickstart](README.md#quickstart)
3. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
1. [Installation](#installation)
2. [Quick start](#quick-start)
3. [Running tests](#running-tests)
###Additional documentation
@@ -47,7 +47,9 @@ JavaScript client for [rippled](https://github.com/ripple/rippled)
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions
**Building ripple-lib from github**
**Building ripple-lib for browser environments**
ripple-lib uses Gulp to generate browser builds. These steps will generate minified and non-minified builds of ripple-lib in the `build/` directory.
```
$ git clone https://github.com/ripple/ripple-lib
@@ -55,9 +57,13 @@ See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for addition
$ npm run build
```
Then use the minified `build/ripple-*-min.js`
**Restricted browser builds**
##Quickstart
You may generate browser builds that contain a subset of features. To do this, run `./node_modules/.bin/gulp build-<name>`
+ `build-core` Contains the functionality to make requests and listen for events such as `ledgerClose`. Only `ripple.Remote` is currently exposed. Advanced features like transaction submission and orderbook tracking are excluded from this build.
##Quick start
`Remote.js` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the point of entry for interacting with rippled
@@ -75,8 +81,8 @@ var remote = new Remote({
remote.connect(function() {
/* remote connected */
remote.request('server_info', function(err, info) {
remote.requestServerInfo(function(err, info) {
// process err and info
});
});
```
@@ -87,7 +93,7 @@ remote.connect(function() {
2. `cd` into the repository and install dependencies with `npm install`
3. `npm test` or `node_modules/.bin/mocha test/*-test.js`
3. `npm test`
**Generating code coverage**

View File

@@ -16,17 +16,6 @@ This file provides step-by-step walkthroughs for some of the most common usages
1. [The ripple-lib README](../README.md)
2. [The ripple-lib API Reference](REFERENCE.md)
##Generating a new Ripple Wallet
```js
var Wallet = require('ripple-lib').Wallet;
var wallet = Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
```
##Connecting to the Ripple network
1. [Get ripple-lib](README.md#getting-ripple-lib)
@@ -40,9 +29,19 @@ This file provides step-by-step walkthroughs for some of the most common usages
```
3. Create a new `Remote` and connect to the network:
```js
var options = {
trace : false,
trusted: true,
local_signing: true,
servers: [
{ host: 's-west.ripple.com', port: 443, secure: true }
]
}
var remote = new Remote({options});
remote.connect(function() {
remote.connect(function(err, res) {
/* remote connected, use some remote functions here */
});
```
@@ -50,18 +49,45 @@ This file provides step-by-step walkthroughs for some of the most common usages
4. You're connected! Read on to see what to do now.
##Generating a new Ripple Wallet
```js
var ripple = require('ripple-lib');
// subscribing to a server allows for more entropy
var remote = new ripple.Remote({
servers: [
{ host: 's1.ripple.com', port: 443, secure: true }
]
});
remote.connect(function(err, res) {
/* remote connected */
});
// Wait for randomness to have been added.
// The entropy of the random generator is increased
// by random data received from a rippled
remote.once('random', function(err, info) {
var wallet = ripple.Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
});
```
##Sending rippled API requests
`Remote` contains functions for constructing a `Request` object.
`Remote` contains functions for constructing a `Request` object.
A `Request` is an `EventEmitter` so you can listen for success or failure events -- or, instead, you can provide a callback.
Here is an example, using [request_server_info](https://ripple.com/wiki/JSON_Messages#server_info).
Here is an example, using [requestServerInfo](https://ripple.com/wiki/JSON_Messages#server_info).
+ Constructing a `Request` with event listeners
```js
var request = remote.request('server_info');
var request = remote.requestServerInfo();
request.on('success', function onSuccess(res) {
//handle success
@@ -102,23 +128,43 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
var remote = new Remote({options});
remote.connect(function() {
var request = remote.request('subscribe');
request.addStream('ledger'); //remote will emit `ledger_closed`
request.addStream('transactions'); //remote will emit `transaction`
request.on('ledger_closed', function onLedgerClosed(ledgerData) {
//handle ledger
var remote = new Remote({
// see the API Reference for available options
servers: [ 'wss://s1.ripple.com:443' ]
});
request.on('transaction', function onTransacstion(transaction) {
//handle transaction
});
remote.connect(function() {
console.log('Remote connected');
request.request(function(err) {
if (err) {
} else {
}
var streams = [
'ledger',
'transactions'
];
var request = remote.requestSubscribe(streams);
request.on('error', function(error) {
console.log('request error: ', error);
});
// the `ledger_closed` and `transaction` will come in on the remote
// since the request for subscribe is finalized after the success return
// the streaming events will still come in, but not on the initial request
remote.on('ledger_closed', function(ledger) {
console.log('ledger_closed: ', JSON.stringify(ledger, null, 2));
});
remote.on('transaction', function(transaction) {
console.log('transaction: ', JSON.stringify(transaction, null, 2));
});
remote.on('error', function(error) {
console.log('remote error: ', error);
});
// fire the request
request.request();
});
});
```
@@ -130,7 +176,7 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
Submitting a payment transaction to the Ripple network involves connecting to a `Remote`, creating a transaction, signing it with the user's secret, and submitting it to the `rippled` server. Note that the `Amount` module is used to convert human-readable amounts like '1XRP' or '10.50USD' to the type of Amount object used by the Ripple network.
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -149,8 +195,8 @@ remote.connect(function() {
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: RECIPIENT,
account: MY_ADDRESS,
destination: RECIPIENT,
amount: AMOUNT
});
@@ -171,12 +217,12 @@ Since the fee required for a transaction may change between the time when the or
The [`max_fee`](REFERENCE.md#1-remote-options) option can be used to avoid submitting a transaction to a server that is charging unreasonably high fees.
##4. Submitting a trade offer to the network
##Submitting a trade offer to the network
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example for a trade that expires in 24 hours where you are offering to sell 1 USD in exchange for 100 XRP:
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example offering to sell 1 USD in exchange for 100 XRP:
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -195,7 +241,7 @@ remote.connect(function() {
var transaction = remote.createTransaction('OfferCreate', {
account: MY_ADDRESS,
taker_pays: '1',
taker_pays: '100',
taker_gets: '1/USD/' + GATEWAY
});

View File

@@ -18,7 +18,7 @@ __(More examples coming soon!)__
###Also see:
1. [The ripple-lib README](../README.md)
2. [The ripple-lib GUIDES](GUIDES.md)
2. [The ripple-lib GUIDES](GUIDES.md)a
#Remote options
@@ -61,14 +61,34 @@ or
#Request constructor functions
Some requests have helper methods to construct the requests object and set properties on the message object. These will often be the more used requests and the helper methods is the preferred way of constructing these requests.
Other request can still be made, but the type will have to be passed in directly to request constructor. See examples below.
If the method is camelCased and starts with `request`, it's a helper method that wraps the request constructor.
##Server requests
**[server_info([callback])](https://ripple.com/wiki/JSON_Messages#server_info)**
**[requestServerInfo([callback])](https://ripple.com/wiki/JSON_Messages#server_info)**
Returns information about the state of the server. If you are connected to multiple servers and want to select by a particular host, use `request.setServer`. Example:
```js
var request = remote.request('server_info');
var request = remote.requestServerInfo();
request.setServer('wss://s1.ripple.com');
request.request(function(err, res) {
});
```
**[requestPeers([callback])](https://ripple.com/wiki/JSON_Messages#peers)**
**[requestConnect(ip, port, [callback])](https://ripple.com/wiki/JSON_Messages#connect)**
**[unl_list([callback])](https://ripple.com/wiki/JSON_Messages#unl_list)**
```js
var request = remote.request('un_list');
request.setServer('wss://s1.ripple.com');
@@ -77,42 +97,48 @@ request.request(function(err, res) {
});
```
**[unl_list([callback])](https://ripple.com/wiki/JSON_Messages#unl_list)**
**[unl_add(addr, comment, [callback])](https://ripple.com/wiki/JSON_Messages#unl_add)**
**[unl_delete(node, [callback])](https://ripple.com/wiki/JSON_Messages#unl_delete)**
**[requestPeers([callback])](https://ripple.com/wiki/JSON_Messages#peers)**
**[connect(ip, port, [callback])](https://ripple.com/wiki/JSON_Messages#connect)**
##Ledger requests
**[ledger(ledger, [opts], [callback])](https://ripple.com/wiki/JSON_Messages#ledger)**
**[requestLedger([opts], [callback])](https://ripple.com/wiki/JSON_Messages#ledger)**
**ledger_header([callback])**
**[requestLedgerHeader([callback])](https://wiki.ripple.com/JSON_Messages#ledger_data)**
**[ledger_current([callback])](https://ripple.com/wiki/JSON_Messages#ledger_current)**
**[requestLedgerCurrent([callback])](https://ripple.com/wiki/JSON_Messages#ledger_current)**
**[ledger_entry(type, [callback])](https://ripple.com/wiki/JSON_Messages#ledger_entry)**
**[requestLedgerEntry(type, [callback])](https://ripple.com/wiki/JSON_Messages#ledger_entry)**
**[subscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#subscribe)**
**[requestSubscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#subscribe)**
Start receiving selected streams from the server.
**[unsubscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#unsubscribe)**
**[requestUnsubscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#unsubscribe)**
Stop receiving selected streams from the server.
##Account requests
**[account_info(account, [callback])](https://ripple.com/wiki/JSON_Messages#account_info)**
**[requestAccountInfo(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_info)**
Return information about the specified account.
```
var options = {
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
ledger: 'validated'
};
var request = remote.requestAccountInfo(options, function(err, info) {
/* process info */
});
// response
{
ledger_current_index: <number>,
account_data: {
@@ -129,13 +155,35 @@ Return information about the specified account.
}
```
**[account_lines(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_lines)**
**[requestAccountLines(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_lines)**
**[account_offers(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_offers)**
**[requestAccountOffers(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_offers)**
Return the specified account's outstanding offers.
**[account_tx(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_tx)**
Requests for both `account_lines` and `account_offers` support paging. The amount of results per response can be configured with the `limit`.
The responses can be paged through by using the `marker`.
```
// A valid `ledger_index` or `ledger_hash` is required to provide a reliable result.
// Results can change between ledger closes, so the provided ledger will be used as base.
var options = {
account: < rippleAccount >,
limit: < Number between 10 and 400 >,
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;
}
var request = remote.requestAccountOffers(options);
```
**[requestAccountTransactions(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_tx)**
Fetch a list of transactions that applied to this account.
@@ -153,42 +201,46 @@ Options:
+ `fwd_marker`
+ `rev_marker`
**[wallet_accounts(seed, [callback])](https://ripple.com/wiki/JSON_Messages#wallet_accounts)**
**[requestWalletAccounts(seed, [callback])](https://ripple.com/wiki/JSON_Messages#wallet_accounts)**
Return a list of accounts for a wallet. *Requires trusted remote*
**account_balance(account, [ledger], [callback])**
**requestAccountBalance(account, [ledger], [callback])**
Get the balance for an account. Returns an [Amount](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/amount.js) object.
**account_flags(account, [ledger], [callback])**
**requestAccountFlags(account, [ledger], [callback])**
Return the flags for an account.
**owner_count(account, [ledger], [callback])**
**requestOwnerCount(account, [ledger], [callback])**
Return the owner count for an account.
**ripple_balance(account, issuer, currency, [ledger], [callback])**
**requestRippleBalance(account, issuer, currency, [ledger], [callback])**
Return a request to get a ripple balance
##Orderbook requests
**[book_offers(options, [callback])](https://ripple.com/wiki/JSON_Messages#book_offers)**
**[requestBookOffers(options, [callback])](https://ripple.com/wiki/JSON_Messages#book_offers)**
Return the offers for an order book, also called a *snapshot*
```js
var request = remote.request('book_offers', {
taker_gets: {
'currency':'XRP'
var options = {
gets: {
issuer: < issuer >,
currency: < currency >
},
taker_pays: {
'currency':'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
});
pays: {
issuer: < issuer >,
currency: < currency >
},
limit: < limit >
};
var request = remote.requestBookOffers(options);
request.request(function(err, offers) {
//handle offers
@@ -197,23 +249,23 @@ request.request(function(err, offers) {
##Transaction requests
**[transaction_entry(hash, [ledger_hash], [callback])](https://ripple.com/wiki/JSON_Messages#transaction_entry)**
**[requestTransactionEntry(hash, [ledger_hash], [callback])](https://ripple.com/wiki/JSON_Messages#transaction_entry)**
Searches a particular ledger for a transaction hash. Default ledger is the open ledger.
**[tx(hash, [callback])](https://ripple.com/wiki/JSON_Messages#tx)**
**[requestTransaction(hash, [callback])](https://ripple.com/wiki/JSON_Messages#tx)**
Searches ledger history for validated transaction hashes.
**[sign(secret, tx_json, [callback])](https://ripple.com/wiki/JSON_Messages#sign)**
**[requestSign(secret, tx_json, [callback])](https://ripple.com/wiki/JSON_Messages#sign)**
Sign a transaction. *Requires trusted remote*
**[submit([callback])](https://ripple.com/wiki/JSON_Messages#submit)**
**[requestSubmit([callback])](https://ripple.com/wiki/JSON_Messages#submit)**
Submit a transaction to the network. This command is used internally to submit transactions with a greater degree of reliability. See [Submitting a payment to the network](GUIDES.md#3-submitting-a-payment-to-the-network) for details.
**[ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])](https://ripple.com/wiki/JSON_Messages#path_find)**
**[pathFind(src_account, dst_account, dst_amount, src_currencies)](https://ripple.com/wiki/JSON_Messages#path_find)**
#Transaction constructors

2
npm-shrinkwrap.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.9.0-rc5",
"version": "0.9.4",
"dependencies": {
"async": {
"version": "0.8.0",

View File

@@ -1,7 +1,7 @@
{
"name": "ripple-lib",
"version": "0.9.1",
"description": "Ripple JavaScript client library",
"version": "0.9.4",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
"bin/*",

View File

@@ -132,7 +132,7 @@ Account.prototype.isValid = function() {
*/
Account.prototype.getInfo = function(callback) {
return this._remote.request_account_info(this._account_id, callback);
return this._remote.requestAccountInfo({account: this._account_id}, callback);
};
/**
@@ -211,7 +211,7 @@ Account.prototype.lines = function(callback) {
}
}
this._remote.requestAccountLines(this._account_id, accountLines);
this._remote.requestAccountLines({account: this._account_id}, accountLines);
return this;
};

View File

@@ -54,7 +54,9 @@ var consts = {
// Maximum possible amount for non-XRP currencies using the maximum mantissa
// with maximum exponent. Corresponds to hex 0xEC6386F26FC0FFFF.
max_value: '9999999999999999e80'
max_value: '9999999999999999e80',
// Minimum possible amount for non-XRP currencies.
min_value: '-1000000000000000e-96'
};
// Add constants to Amount class
@@ -424,6 +426,33 @@ Amount.prototype.invert = function() {
return this.copy()._invert();
};
/**
* Canonicalize amount value
*
* Mirrors rippled's internal Amount representation
* From https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol/STAmount.h#L31-L40
*
* Internal form:
* 1: If amount is zero, then value is zero and offset is -100
* 2: Otherwise:
* legal offset range is -96 to +80 inclusive
* value range is 10^15 to (10^16 - 1) inclusive
* amount = value * [10 ^ offset]
*
* -------------------
*
* The amount can be epxresses as A x 10^B
* Where:
* - A must be an integer between 10^15 and (10^16)-1 inclusive
* - B must be between -96 and 80 inclusive
*
* This results
* - minumum: 10^15 x 10^-96 -> 10^-81 -> -1e-81
* - maximum: (10^16)-1 x 10^80 -> 9999999999999999e80
*
* @returns {Amount}
* @throws {Error} if offset exceeds legal ranges, meaning the amount value is bigger than supported
*/
Amount.prototype.canonicalize = function() {
if (!(this._value instanceof BigInteger)) {
// NaN.
@@ -447,9 +476,8 @@ Amount.prototype.canonicalize = function() {
}
}
// XXX Make sure not bigger than supported. Throw if so.
} else if (this.is_zero()) {
this._offset = -100;
this._offset = Amount.cMinOffset;
this._is_negative = false;
} else {
// Normalize mantissa to valid range.
@@ -465,6 +493,16 @@ Amount.prototype.canonicalize = function() {
}
}
// Make sure not bigger than supported. Throw if so.
if (this.is_negative() && this._offset < Amount.cMinOffset) {
throw new Error('Exceeding min value of ' + Amount.min_value);
}
// Make sure not smaller than supported. Throw if so.
if (!this.is_negative() && this._offset > Amount.cMaxOffset) {
throw new Error('Exceeding max value of ' + Amount.max_value);
}
return this;
};
@@ -539,9 +577,7 @@ Amount.prototype.equals = function(d, ignore_issuer) {
return this.equals(Amount.from_json(d));
}
var result = true;
result = !((!this.is_valid() || !d.is_valid())
var result = !((!this.is_valid() || !d.is_valid())
|| (this._is_native !== d._is_native)
|| (!this._value.equals(d._value) || this._offset !== d._offset)
|| (this._is_negative !== d._is_negative)
@@ -1109,25 +1145,21 @@ Amount.prototype.to_human = function(opts) {
fraction_part = fraction_part.replace(/0*$/, '');
if (fraction_part.length || !opts.skip_empty_fraction) {
// Enforce the maximum number of decimal digits (precision)
if (typeof opts.precision === 'number') {
if (opts.precision <= 0) {
var precision = Math.max(0, opts.precision);
precision = Math.min(precision, fraction_part.length);
var rounded = Number('0.' + fraction_part).toFixed(precision);
// increment the int_part if the first decimal is 5 or higher
if (fraction_part.charCodeAt(0) >= 53) {
int_part = (Number(int_part) + 1).toString();
}
fraction_part = '';
if (rounded < 1) {
fraction_part = rounded.substring(2);
} else {
var precision = Math.min(opts.precision, fraction_part.length);
fraction_part = Math.round(fraction_part / Math.pow(10, fraction_part.length - precision)).toString();
int_part = (Number(int_part) + 1).toString();
fraction_part = '';
}
// because the division above will cut off the leading 0's we have to add them back again
// XXX look for a more elegant alternative
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
}
@@ -1135,7 +1167,7 @@ Amount.prototype.to_human = function(opts) {
if (typeof opts.max_sig_digits === 'number') {
// First, we count the significant digits we have.
// A zero in the integer part does not count.
var int_is_zero = +int_part === 0;
var int_is_zero = Number(int_part) === 0;
var digits = int_is_zero ? 0 : int_part.length;
// Don't count leading zeros in the fractional part if the integer part is
@@ -1161,6 +1193,7 @@ Amount.prototype.to_human = function(opts) {
// Enforce the minimum number of decimal digits (min_precision)
if (typeof opts.min_precision === 'number') {
opts.min_precision = Math.max(0, opts.min_precision);
while (fraction_part.length < opts.min_precision) {
fraction_part += '0';
}

View File

@@ -106,7 +106,8 @@ var FIELDS_MAP = exports.fields = {
16: 'BookDirectory',
17: 'InvoiceID',
18: 'Nickname',
19: 'Feature'
19: 'Amendment',
20: 'TicketID'
},
6: { // Amount
1: 'Amount',
@@ -135,7 +136,8 @@ var FIELDS_MAP = exports.fields = {
10: 'ExpireCode',
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData'
13: 'MemoData',
14: 'MemoFormat'
},
8: { // Account
1: 'Account',
@@ -187,7 +189,7 @@ var FIELDS_MAP = exports.fields = {
19: { // Vector256
1: 'Indexes',
2: 'Hashes',
3: 'Features'
3: 'Amendments'
}
};

View File

@@ -53,11 +53,7 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_json(j, shouldInterpretXrpAsIou);
}
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
};
Currency.from_human = function(j, opts) {
@@ -71,12 +67,9 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
switch (typeof j) {
case 'string':
if (!j || /^(0|XRP)$/.test(j)) {
if (shouldInterpretXrpAsIou) {
this.parse_hex(Currency.HEX_CURRENCY_BAD);
} else {
this.parse_hex(Currency.HEX_ZERO);
}
// if an empty string is given, fall back to XRP
if (!j || j === '0') {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
break;
}
@@ -86,6 +79,17 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
if (matches) {
var currencyCode = matches[1];
// for the currency 'XRP' case
// we drop everything else that could have been provided
// e.g. 'XRP - Ripple'
if (!currencyCode || /^(0|XRP)$/.test(currencyCode)) {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
// early break, we can't have interest on XRP
break;
}
// 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] || '';
@@ -270,7 +274,7 @@ Currency.prototype.has_interest = function() {
* @returns {number} - interest for provided interval, can be negative for demurred currencies
*/
Currency.prototype.get_interest_at = function(referenceDate, decimals) {
if (!this.has_interest) {
if (!this.has_interest()) {
return 0;
}

View File

@@ -77,10 +77,15 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
listenersModified('remove', event);
});
function updateFundedAmounts(transaction) {
self.updateFundedAmounts(transaction);
};
this._remote.on('transaction', updateFundedAmounts);
this.on('unsubscribe', function() {
self.resetCache();
self._remote.removeListener('transaction', updateFundedAmounts);
self._remote.removeListener('transaction', updateTransferRate);
});
this._remote.once('prepare_subscribe', function() {
@@ -94,18 +99,6 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
});
});
function updateFundedAmounts(message) {
self.updateFundedAmounts(message);
};
this._remote.on('transaction', updateFundedAmounts);
function updateTransferRate(message) {
self.updateTransferRate(message);
};
this._remote.on('transaction', updateTransferRate);
return this;
};
@@ -115,30 +108,15 @@ util.inherits(OrderBook, EventEmitter);
* Events emitted from OrderBook
*/
OrderBook.EVENTS = [ 'transaction', 'model', 'trade', 'offer' ];
OrderBook.EVENTS = [
'transaction', 'model', 'trade',
'offer_added', 'offer_removed',
'offer_changed', 'offer_funds_changed'
];
OrderBook.DEFAULT_TRANSFER_RATE = 1000000000;
/**
* Whether the OrderBook is valid.
*
* Note: This only checks whether the parameters (currencies and issuer) are
* syntactically valid. It does not check anything against the ledger.
*
* @return {Boolean} is valid
*/
OrderBook.prototype.isValid =
OrderBook.prototype.is_valid = function() {
// XXX Should check for same currency (non-native) && same issuer
return (
this._currencyPays && this._currencyPays.is_valid() &&
(this._currencyPays.is_native() || UInt160.is_valid(this._issuerPays)) &&
this._currencyGets && this._currencyGets.is_valid() &&
(this._currencyGets.is_native() || UInt160.is_valid(this._issuerGets)) &&
!(this._currencyPays.is_native() && this._currencyGets.is_native())
);
};
OrderBook.IOU_SUFFIX = '/000/rrrrrrrrrrrrrrrrrrrrrhoLvTp';
/**
* Initialize orderbook. Get orderbook offers and subscribe to transactions
@@ -167,7 +145,7 @@ OrderBook.prototype.subscribe = function() {
}
];
async.series(steps, function(err) {
async.series(steps, function(err, res) {
//XXX What now?
});
};
@@ -308,8 +286,7 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
return balance;
}
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
var adjustedBalance = Amount.from_json(balance + iouSuffix)
var adjustedBalance = Amount.from_json(balance + OrderBook.IOU_SUFFIX)
.divide(transferRate)
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
.to_json()
@@ -321,40 +298,41 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
/**
* Request transfer rate for this orderbook's issuer
*
* @param [Function] calback
* @param {Function} callback
*/
OrderBook.prototype.requestTransferRate = function(callback) {
assert.strictEqual(typeof callback, 'function');
var self = this;
var issuer = this._issuerGets;
this.once('transfer_rate', function(rate) {
if (typeof callback === 'function') {
callback(null, rate);
}
});
if (this._currencyGets.is_native()) {
// Transfer rate is default
return this.emit('transfer_rate', OrderBook.DEFAULT_TRANSFER_RATE);
// Transfer rate is default (native currency)
callback(null, OrderBook.DEFAULT_TRANSFER_RATE);
return;
}
if (this._issuerTransferRate) {
// Transfer rate has been cached
return this.emit('transfer_rate', this._issuerTransferRate);
// Transfer rate has already been cached
callback(null, this._issuerTransferRate);
return;
}
this._remote.requestAccountInfo(issuer, function(err, info) {
this._remote.requestAccountInfo({ account: issuer }, function(err, info) {
if (err) {
// XXX What now?
return callback(err);
}
var transferRate = info.account_data.TransferRate
|| OrderBook.DEFAULT_TRANSFER_RATE;
var transferRate = info.account_data.TransferRate;
if (!transferRate) {
transferRate = OrderBook.DEFAULT_TRANSFER_RATE;
}
self._issuerTransferRate = transferRate;
self.emit('transfer_rate', transferRate);
callback(null, transferRate);
});
};
@@ -380,11 +358,10 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
return offer;
}
var iouSuffix = '/' + this._currencyGets.to_json()
+ '/' + this._issuerGets;
offer.is_fully_funded = Amount.from_json(
this._currencyGets.is_native() ? fundedAmount : fundedAmount + iouSuffix
this._currencyGets.is_native()
? fundedAmount
: fundedAmount + OrderBook.IOU_SUFFIX
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
if (offer.is_fully_funded) {
@@ -395,40 +372,40 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
offer.taker_gets_funded = fundedAmount;
var takerPaysValue = typeof offer.TakerPays === 'object'
var takerPaysValue = (typeof offer.TakerPays === 'object')
? offer.TakerPays.value
: offer.TakerPays;
var takerGetsValue = typeof offer.TakerGets === 'object'
var takerGetsValue = (typeof offer.TakerGets === 'object')
? offer.TakerGets.value
: offer.TakerGets;
var takerPays = Amount.from_json(
takerPaysValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var takerGets = Amount.from_json(
takerGetsValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var fundedPays = Amount.from_json(
fundedAmount + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var takerPays = Amount.from_json(takerPaysValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(takerGetsValue + OrderBook.IOU_SUFFIX);
var fundedPays = Amount.from_json(fundedAmount + OrderBook.IOU_SUFFIX);
var rate = takerPays.divide(takerGets);
fundedPays = fundedPays.multiply(rate);
if (fundedPays.compareTo(takerPays) < 0) {
offer.taker_pays_funded = fundedPays.to_json().value;
if (this._currencyPays.is_native()) {
fundedPays = String(parseInt(fundedPays.to_json().value, 10));
} else {
fundedPays = fundedPays.to_json().value;
}
} else {
offer.taker_pays_funded = takerPays.to_json().value;
fundedPays = takerPays.to_json().value;
}
offer.taker_pays_funded = fundedPays;
return offer;
};
/**
* DEPRECATED:
* Should only be called for old versions of rippled
*
* Determine what an account is funded to offer for orderbook's
* currency/issuer
*
@@ -447,7 +424,7 @@ OrderBook.prototype.requestFundedAmount = function(account, callback) {
}
function requestNativeBalance(callback) {
self._remote.requestAccountInfo(account, function(err, info) {
self._remote.requestAccountInfo({ account: account }, function(err, info) {
if (err) {
callback(err);
} else {
@@ -457,12 +434,11 @@ OrderBook.prototype.requestFundedAmount = function(account, callback) {
};
function requestLineBalance(callback) {
var request = self._remote.requestAccountLines(
account, // account
void(0), // account index
'VALIDATED', // ledger
self._issuerGets //peer
);
var request = self._remote.requestAccountLines({
account: account,
ledger: 'validated',
peer: self._issuerGets
});
request.request(function(err, res) {
if (err) {
@@ -601,10 +577,10 @@ OrderBook.prototype.isBalanceChange = function(node) {
* @param {Object} transaction
*/
OrderBook.prototype.updateFundedAmounts = function(message) {
OrderBook.prototype.updateFundedAmounts = function(transaction) {
var self = this;
var affectedAccounts = message.mmeta.getAffectedAccounts();
var affectedAccounts = transaction.mmeta.getAffectedAccounts();
var isOwnerAffected = affectedAccounts.some(function(account) {
return self.hasCachedFunds(account);
@@ -616,25 +592,23 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
if (!this._currencyGets.is_native() && !this._issuerTransferRate) {
// Defer until transfer rate is requested
if (self._remote.trace) {
if (this._remote.trace) {
log.info('waiting for transfer rate');
}
this.once('transfer_rate', function() {
self.updateFundedAmounts(message);
this.requestTransferRate(function() {
self.updateFundedAmounts(transaction);
});
this.requestTransferRate();
return;
}
var nodes = message.mmeta.getNodes({
var affectedNodes = transaction.mmeta.getNodes({
nodeType: 'ModifiedNode',
entryType: this._currencyGets.is_native() ? 'AccountRoot' : 'RippleState'
});
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
for (var i=0, l=affectedNodes.length; i<l; i++) {
var node = affectedNodes[i];
if (!this.isBalanceChange(node)) {
continue;
@@ -642,39 +616,72 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
var result = this.getBalanceChange(node);
if (result.isValid) {
if (this.hasCachedFunds(result.account)) {
this.updateOfferFunds(result.account, result.balance);
}
if (result.isValid && this.hasCachedFunds(result.account)) {
this.updateAccountFunds(result.account, result.balance);
}
}
};
/**
* Update issuer's TransferRate as it changes
* Normalize offers from book_offers and transaction stream
*
* @param {Object} transaction
* @param {Object} offer
* @return {Object} normalized
*/
OrderBook.prototype.updateTransferRate = function(message) {
var self = this;
OrderBook.offerRewrite = function(offer) {
var result = { };
var keys = Object.keys(offer);
var affectedAccounts = message.mmeta.getAffectedAccounts();
var isIssuerAffected = affectedAccounts.some(function(account) {
return account === self._issuerGets;
});
if (!isIssuerAffected) {
return;
for (var i=0, l=keys.length; i<l; i++) {
var key = keys[i];
switch (key) {
case 'PreviousTxnID':
case 'PreviousTxnLgrSeq':
case 'quality':
break;
default:
result[key] = offer[key];
}
}
// XXX Update transfer rate
//
// var nodes = message.mmeta.getNodes({
// nodeType: 'ModifiedNode',
// entryType: 'AccountRoot'
// });
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');
return result;
};
/**
* Reset internal offers cache from book_offers request
*
* @param {Array} offers
* @api private
*/
OrderBook.prototype.setOffers = function(offers) {
assert(Array.isArray(offers));
var newOffers = [ ];
for (var i=0, l=offers.length; i<l; i++) {
var offer = OrderBook.offerRewrite(offers[i]);
var fundedAmount;
if (this.hasCachedFunds(offer.Account)) {
fundedAmount = this.getCachedFunds(offer.Account);
} else if (offer.hasOwnProperty('owner_funds')) {
fundedAmount = this.applyTransferRate(offer.owner_funds);
this.addCachedFunds(offer.Account, fundedAmount);
}
this.setFundedAmount(offer, fundedAmount);
this.incrementOfferCount(offer.Account);
newOffers.push(offer);
}
this._offers = newOffers;
};
/**
@@ -706,27 +713,8 @@ OrderBook.prototype.requestOffers = function(callback) {
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
}
// Reset offers
self._offers = [ ];
for (var i=0, l=res.offers.length; i<l; i++) {
var offer = res.offers[i];
var fundedAmount;
if (self.hasCachedFunds(offer.Account)) {
fundedAmount = self.getCachedFunds(offer.Account);
} else if (offer.hasOwnProperty('owner_funds')) {
fundedAmount = self.applyTransferRate(offer.owner_funds);
self.addCachedFunds(offer.Account, fundedAmount);
}
self.setFundedAmount(offer, fundedAmount);
self.incrementOfferCount(offer.Account);
self._offers.push(offer);
}
self.setOffers(res.offers);
self._synchronized = true;
self.emit('model', self._offers);
callback(null, self._offers);
@@ -838,6 +826,57 @@ OrderBook.prototype.getOffersSync = function() {
return this._offers;
};
/**
* Update offers whose account's funds have changed
*
* @param {String} account address
* @param {String|Object} offer funds
*/
OrderBook.prototype.updateAccountFunds = function(account, balance) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(balance), 'Funded amount is invalid');
if (this._remote.trace) {
log.info('updating offer funds', this._key, account, fundedAmount);
}
var fundedAmount = this.applyTransferRate(balance);
// Update cached account funds
this.addCachedFunds(account, fundedAmount);
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
if (offer.Account !== account) {
continue;
}
var previousOffer = extend({ }, offer);
var previousFundedGets = Amount.from_json(
offer.taker_gets_funded + OrderBook.IOU_SUFFIX
);
offer.owner_funds = balance;
this.setFundedAmount(offer, fundedAmount);
var hasChangedFunds = !previousFundedGets.equals(
Amount.from_json(offer.taker_gets_funded + OrderBook.IOU_SUFFIX)
);
if (!hasChangedFunds) {
continue;
}
this.emit('offer_changed', previousOffer, offer);
this.emit('offer_funds_changed', offer,
previousOffer.taker_gets_funded,
offer.taker_gets_funded
);
}
};
/**
* Insert an offer into the orderbook
*
@@ -849,8 +888,8 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
log.info('inserting offer', this._key, node.fields);
}
var nodeFields = node.fields;
var nodeFields = OrderBook.offerRewrite(node.fields);
nodeFields.LedgerEntryType = node.entryType;
nodeFields.index = node.ledgerIndex;
if (!isNaN(fundedAmount)) {
@@ -901,7 +940,7 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
}
}
for (var i=0; i<this._offers.length; i++) {
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
if (offer.index === node.ledgerIndex) {
if (isDeletedNode) {
@@ -920,57 +959,6 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
}
};
/**
* Update funded status on offers whose account's balance has changed
*
* Update cached account funds
*
* @param {String} account address
* @param {String|Object} offer funds
*/
OrderBook.prototype.updateOfferFunds = function(account, balance) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(balance), 'Funded amount is invalid');
if (this._remote.trace) {
log.info('updating offer funds', this._key, account, fundedAmount);
}
var fundedAmount = this.applyTransferRate(balance);
// Update cached account funds
this.addCachedFunds(account, fundedAmount);
for (var i=0; i<this._offers.length; i++) {
var offer = this._offers[i];
if (offer.Account !== account) {
continue;
}
var suffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
var previousOffer = extend({}, offer);
var previousFundedGets = Amount.from_json(offer.taker_gets_funded + suffix);
offer.owner_funds = balance;
this.setFundedAmount(offer, fundedAmount);
var hasChangedFunds = !previousFundedGets.equals(
Amount.from_json(offer.taker_gets_funded + suffix)
);
if (hasChangedFunds) {
this.emit('offer_changed', previousOffer, offer);
this.emit(
'offer_funds_changed', offer,
previousOffer.taker_gets_funded,
offer.taker_gets_funded
);
}
}
};
/**
* Notify orderbook of a relevant transaction
*
@@ -978,7 +966,7 @@ OrderBook.prototype.updateOfferFunds = function(account, balance) {
* @api private
*/
OrderBook.prototype.notify = function(message) {
OrderBook.prototype.notify = function(transaction) {
var self = this;
// Unsubscribed from OrderBook
@@ -986,7 +974,7 @@ OrderBook.prototype.notify = function(message) {
return;
}
var affectedNodes = message.mmeta.getNodes({
var affectedNodes = transaction.mmeta.getNodes({
entryType: 'Offer',
bookKey: this._key
});
@@ -996,7 +984,7 @@ OrderBook.prototype.notify = function(message) {
}
if (this._remote.trace) {
log.info('notifying', this._key, message.transaction.hash);
log.info('notifying', this._key, transaction.transaction.hash);
}
var tradeGets = Amount.from_json(
@@ -1013,7 +1001,7 @@ OrderBook.prototype.notify = function(message) {
function handleNode(node, callback) {
var isDeletedNode = node.nodeType === 'DeletedNode';
var isOfferCancel = message.transaction.TransactionType === 'OfferCancel';
var isOfferCancel = transaction.transaction.TransactionType === 'OfferCancel';
switch (node.nodeType) {
case 'DeletedNode':
@@ -1040,7 +1028,7 @@ OrderBook.prototype.notify = function(message) {
case 'CreatedNode':
self.incrementOfferCount(node.fields.Account);
var fundedAmount = message.transaction.owner_funds;
var fundedAmount = transaction.transaction.owner_funds;
if (!isNaN(fundedAmount)) {
self.insertOffer(node, fundedAmount);
@@ -1062,7 +1050,7 @@ OrderBook.prototype.notify = function(message) {
};
async.eachSeries(affectedNodes, handleNode, function() {
self.emit('transaction', message);
self.emit('transaction', transaction);
self.emit('model', self._offers);
if (!tradeGets.is_zero()) {
self.emit('trade', tradePays, tradeGets);
@@ -1098,6 +1086,27 @@ OrderBook.prototype.to_json = function() {
return json;
};
/**
* Whether the OrderBook is valid.
*
* Note: This only checks whether the parameters (currencies and issuer) are
* syntactically valid. It does not check anything against the ledger.
*
* @return {Boolean} is valid
*/
OrderBook.prototype.isValid =
OrderBook.prototype.is_valid = function() {
// XXX Should check for same currency (non-native) && same issuer
return (
this._currencyPays && this._currencyPays.is_valid() &&
(this._currencyPays.is_native() || UInt160.is_valid(this._issuerPays)) &&
this._currencyGets && this._currencyGets.is_valid() &&
(this._currencyGets.is_native() || UInt160.is_valid(this._issuerGets)) &&
!(this._currencyPays.is_native() && this._currencyGets.is_native())
);
};
exports.OrderBook = OrderBook;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -23,6 +23,7 @@ var Server = require('./server').Server;
var Amount = require('./amount').Amount;
var Currency = require('./currency').Currency;
var UInt160 = require('./uint160').UInt160;
var UInt256 = require('./uint256').UInt256;
var Transaction = require('./transaction').Transaction;
var Account = require('./account').Account;
var Meta = require('./meta').Meta;
@@ -92,7 +93,7 @@ function Remote(opts, trace) {
this.canonical_signing = (typeof opts.canonical_signing === 'boolean') ? opts.canonical_signing : true;
this.fee_cushion = (typeof opts.fee_cushion === 'number') ? opts.fee_cushion : 1.2;
this.max_fee = (typeof opts.max_fee === 'number') ? opts.max_fee : Infinity;
this.max_fee = (typeof opts.max_fee === 'number') ? opts.max_fee : 1000000; // default max fee is 1 XRP, 10^6 drops
this.max_attempts = (typeof opts.max_attempts === 'number') ? opts.max_attempts : 10;
@@ -1198,19 +1199,43 @@ Remote.prototype.requestTx = function(hash, callback) {
};
/**
* Account request abstraction
* Account Request
*
* @this Remote
* @api private
* Optional paging with limit and marker options
* supported in rippled for 'account_lines' and 'account_offers'
*
* The paged responses aren't guaranteed to be reliable between
* ledger closes. You have to supply a ledger_index or ledger_hash
* when paging to ensure a complete response
*
* @param {String} type - request name, e.g. 'account_lines'
* @param {String} account - ripple address
* @param {Object} options - all optional
* @param {String} peer - ripple address
* @param [String|Number] ledger identifier
* @param [Number] limit - max results per response
* @param {String} marker - start position in response paging
* @param [Function] callback
* @return {Request}
* @throws {Error} if a marker is provided, but no ledger_index or ledger_hash
*/
Remote.accountRequest = function(type, options, callback) {
var account, ledger, peer, limit, marker;
Remote.accountRequest = function(type, account, ledger, peer, callback) {
if (typeof account === 'object') {
var options = account;
callback = ledger;
ledger = options.ledger;
account = options.account || options.accountID;
peer = options.peer;
if (typeof options === 'object') {
account = options.account;
ledger = options.ledger;
peer = options.peer;
limit = options.limit;
marker = options.marker;
}
// if a marker is given, we need a ledger
// check if a valid ledger_index or ledger_hash is provided
if (marker) {
if (!(Number(ledger) > 0) && !UInt256.is_valid(ledger)) {
throw new Error('A ledger_index or ledger_hash must be provided when using a marker');
}
}
var lastArg = arguments[arguments.length - 1];
@@ -1220,30 +1245,55 @@ Remote.accountRequest = function(type, account, ledger, peer, callback) {
}
var request = new Request(this, type);
var account = UInt160.json_rewrite(account);
request.message.account = account;
if (account) {
account = UInt160.json_rewrite(account);
request.message.account = account;
}
request.ledgerSelect(ledger);
if (UInt160.is_valid(peer)) {
request.message.peer = UInt160.json_rewrite(peer);
}
request.callback(callback);
if (!isNaN(Number(limit))) {
limit = Number(limit);
// max for 32-bit unsigned int is 4294967295
// we'll clamp to 1e9
if (limit > 1e9) {
limit = 1e9;
}
// min for 32-bit unsigned int is 0
// we'll clamp to 0
if (limit < 0) {
limit = 0;
}
request.message.limit = limit;
}
if (marker) {
request.message.marker = marker;
}
request.callback(callback);
return request;
};
/**
* Request account_info
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* @param {Object} options
* @param {String} account - ripple address
* @param {String} peer - ripple address
* @param [String|Number] ledger identifier
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountInfo = function(account, callback) {
Remote.prototype.requestAccountInfo = function(options, callback) {
var args = Array.prototype.concat.apply(['account_info'], arguments);
return Remote.accountRequest.apply(this, args);
};
@@ -1251,13 +1301,15 @@ Remote.prototype.requestAccountInfo = function(account, callback) {
/**
* Request account_currencies
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* @param {Object} options
* @param {String} account - ripple address
* @param {String} peer - ripple address
* @param [String|Number] ledger identifier
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountCurrencies = function(account, callback) {
Remote.prototype.requestAccountCurrencies = function(options, callback) {
var args = Array.prototype.concat.apply(['account_currencies'], arguments);
return Remote.accountRequest.apply(this, args);
};
@@ -1265,14 +1317,24 @@ Remote.prototype.requestAccountCurrencies = function(account, callback) {
/**
* Request account_lines
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* @param [String] peer
* Requests for account_lines support paging, provide a limit and marker
* to page through responses.
*
* The paged responses aren't guaranteed to be reliable between
* ledger closes. You have to supply a ledger_index or ledger_hash
* when paging to ensure a complete response
*
* @param {Object} options
* @param {String} account - ripple address
* @param {String} peer - ripple address
* @param [String|Number] ledger identifier
* @param [Number] limit - max results per response
* @param {String} marker - start position in response paging
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountLines = function(account, peer, callback) {
Remote.prototype.requestAccountLines = function(options, callback) {
// XXX Does this require the server to be trusted?
//utils.assert(this.trusted);
var args = Array.prototype.concat.apply(['account_lines'], arguments);
@@ -1282,13 +1344,23 @@ Remote.prototype.requestAccountLines = function(account, peer, callback) {
/**
* Request account_offers
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* Requests for account_offers support paging, provide a limit and marker
* to page through responses.
*
* The paged responses aren't guaranteed to be reliable between
* ledger closes. You have to supply a ledger_index or ledger_hash
* when paging to ensure a complete response
*
* @param {Object} options
* @param {String} account - ripple address
* @param [String|Number] ledger identifier
* @param [Number] limit - max results per response
* @param {String} marker - start position in response paging
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountOffers = function(account, callback) {
Remote.prototype.requestAccountOffers = function(options, callback) {
var args = Array.prototype.concat.apply(['account_offers'], arguments);
return Remote.accountRequest.apply(this, args);
};
@@ -1587,7 +1659,7 @@ Remote.prototype.requestLedgerAccept = function(callback) {
request.callback(callback, 'ledger_closed');
request.request();
return this;
return request;
};
/**

View File

@@ -2,7 +2,6 @@ var util = require('util');
var url = require('url');
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount').Amount;
var Transaction = require('./transaction').Transaction;
var log = require('./log').internal.sub('server');
/**
@@ -564,7 +563,7 @@ Server.prototype._handleServerStatus = function(message) {
this._remote.emit('load', message, this);
var loadChanged = message.load_base !== this._load_base
|| message.load_factor !== this._load_factor
|| message.load_factor !== this._load_factor;
if (loadChanged) {
this._load_base = message.load_base;
@@ -760,22 +759,16 @@ Server.prototype._isConnected = function() {
* Calculate transaction fee
*
* @param {Transaction|Number} Fee units for a provided transaction
* @return {Number} Final fee in XRP for specified number of fee units
* @return {String} Final fee in XRP for specified number of fee units
* @api private
*/
Server.prototype._computeFee = function(transaction) {
var units;
if (transaction instanceof Transaction) {
units = transaction._getFeeUnits();
} else if (typeof transaction === 'number') {
units = transaction;
} else {
Server.prototype._computeFee = function(feeUnits) {
if (isNaN(feeUnits)) {
throw new Error('Invalid argument');
}
return this._feeTx(units).to_json();
return this._feeTx(Number(feeUnits)).to_json();
};
/**

View File

@@ -276,7 +276,7 @@ Transaction.prototype._computeFee = function() {
for (var i=0; i<servers.length; i++) {
var server = servers[i];
if (server._connected) {
fees.push(Number(server._computeFee(this)));
fees.push(Number(server._computeFee(this._getFeeUnits())));
}
}
@@ -491,6 +491,23 @@ Transaction.prototype.lastLedger = function(sequence) {
return this;
};
/*
* Set the transaction's proposed fee. No op when fee parameter
* is not 0 or a positive number
*
* @param {Number} fee The proposed fee
*
* @returns {Transaction} calling instance for chaining
*/
Transaction.prototype.maxFee = function(fee) {
if (typeof fee === 'number' && fee >= 0) {
this._setMaxFee = true;
this._maxFee = fee;
}
return this;
};
Transaction._pathRewrite = function(path) {
if (!Array.isArray(path)) {
return;

View File

@@ -46,8 +46,16 @@ function TransactionManager(account) {
}
if (submission instanceof Transaction) {
// ND: A `success` handler will `finalize` this later
submission.emit('success', transaction);
switch (transaction.engine_result) {
case 'tesSUCCESS':
submission.emit('success', transaction);
break;
default:
submission.emit('error', transaction);
}
} else {
self._pending.addReceivedId(hash, transaction);
}
@@ -357,8 +365,6 @@ TransactionManager.prototype._request = function(tx) {
return tx.emit('error', new RippleError('tejLocalSigningRequired', message));
}
tx.emit('presubmit');
if (tx.finalized) {
return;
}
@@ -413,8 +419,6 @@ TransactionManager.prototype._request = function(tx) {
if (tx.finalized) {
return;
}
tx.emit('error', message);
};
function transactionFailedLocal(message) {
@@ -551,9 +555,11 @@ TransactionManager.prototype._request = function(tx) {
return;
}
submitRequest.timeout(self._submissionTimeout, requestTimeout);
tx.submissions = submitRequest.broadcast();
tx.emit('presubmit');
submitRequest.timeout(self._submissionTimeout, requestTimeout);
tx.submissions = submitRequest.broadcast();
tx.attempts++;
tx.emit('postsubmit');
};

View File

@@ -29,13 +29,15 @@ describe('Account', function(){
});
// XXX: clean up the stubbed out remote methods
describe('#publicKeyIsActive()', function(){
it('should respond true if the public key corresponds to the account address and the master key IS NOT disabled', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
callback(null, { account_data: {
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
@@ -56,7 +58,7 @@ describe('Account', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
callback(null, { account_data: {
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
@@ -77,7 +79,7 @@ describe('Account', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
callback(null, { account_data: {
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
@@ -99,7 +101,7 @@ describe('Account', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
callback(null, { account_data: {
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
@@ -121,7 +123,7 @@ describe('Account', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
callback(null, { account_data: {
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
@@ -142,7 +144,7 @@ describe('Account', function(){
var account = new Account({
on: function(){},
request_account_info: function(address, callback) {
requestAccountInfo: function(address, callback) {
if (address === 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ') {
callback({ error: 'remoteError',
error_message: 'Remote reported an error.',

View File

@@ -87,7 +87,7 @@ describe('Amount', function() {
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
});
it('to human, precision 0, precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:16}), '0.0');
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:16}), '0');
});
it('to human, precision 0, precision 8, min_precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:8, min_precision:16}), '0.0000000000000000');
@@ -101,6 +101,21 @@ describe('Amount', function() {
it('to human, precision 16, min_precision 6, max_sig_digits 20', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision: 16, min_precision: 6, max_sig_digits: 20}), '0.000000');
});
it('to human rounding edge case, precision 2, 1', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:1}), '1.0');
});
it('to human rounding edge case, precision 2, 2', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:2}), '0.99');
});
it('to human rounding edge case, precision 2, 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3}), '0.99');
});
it('to human rounding edge case, precision 2, 3 min precision 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3, min_precision:3}), '0.990');
});
it('to human rounding edge case, precision 3, 2', function() {
assert.strictEqual(Amount.from_human("0.999 XAU").to_human({precision:2}), '1.00');
});
});
describe('from_human', function() {
it('1 XRP', function() {
@@ -1162,4 +1177,80 @@ describe('Amount', function() {
assert.strictEqual(demAmount.to_human_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
describe('amount limits', function() {
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_max.toString(), '9000000000000000000');
});
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_min.toString(), '-9000000000000000000');
});
it('max mantissa value', function() {
assert.strictEqual(Amount.bi_man_max_value.toString(), '9999999999999999');
});
it('min mantissa value', function() {
assert.strictEqual(Amount.bi_man_min_value.toString(), '1000000000000000');
});
it ('from_json minimum XRP', function() {
console.log('max', Amount.bi_xns_max.toString());
var amt = Amount.from_json('-9000000000000000000');
assert.strictEqual(amt.to_json(), '-9000000000000000000');
});
it ('from_json maximum XRP', function() {
var amt = Amount.from_json('-9000000000000000000');
assert.strictEqual(amt.to_json(), '-9000000000000000000');
});
it ('from_json less than minimum XRP', function() {
var amt = Amount.from_json('-9000000000000000001');
assert.strictEqual(amt.to_json(), '0');
});
it ('from_json more than maximum XRP', function() {
var amt = Amount.from_json('9000000000000000001');
assert.strictEqual(amt.to_json(), '0');
});
it ('from_json minimum IOU', function() {
var amt = Amount.from_json('-1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_min_value.toString());
assert.strictEqual(amt.to_text(), '-1000000000000000e-96');
assert.strictEqual(amt.to_text(), Amount.min_value);
});
it('from_json exceed minimum IOU', function() {
assert.throws(function() {
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
}, 'Exceeding min value of ' + Amount.min_value);
});
it ('from_json maximum IOU', function() {
var amt = Amount.from_json('9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_max_value.toString());
assert.strictEqual(amt.to_text(), '9999999999999999e80');
});
it ('from_json exceed maximum IOU', function() {
assert.throws(function() {
Amount.from_json('9999999999999999e81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
}, 'Exceeding max value of ' + Amount.max_value);
});
it ('from_json normalize mantissa to valid max range, lost significant digits', function() {
var amt = Amount.from_json('99999999999999999999999999999999/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_max_value.toString());
assert.strictEqual(amt.to_text(), '9999999999999999e16');
});
it ('from_json normalize mantissa to min valid range, lost significant digits', function() {
var amt = Amount.from_json('-0.0000000000000000000000001/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_min_value.toString());
assert.strictEqual(amt.to_text(), '-1000000000000000e-40');
});
});
});

View File

@@ -1,6 +1,7 @@
var assert = require('assert');
var utils = require('./testutils');
var currency = utils.load_module('currency').Currency;
var timeUtil = utils.load_module('utils').time;
describe('Currency', function() {
describe('json_rewrite', function() {
@@ -16,11 +17,22 @@ describe('Currency', function() {
});
});
describe('from_json', function() {
it('from_json().to_json() == "XRP"', function() {
var r = currency.from_json();
assert(!r.is_valid());
assert.strictEqual('XRP', r.to_json());
});
it('from_json(NaN).to_json() == "XRP"', function() {
var r = currency.from_json(NaN);
assert(!r.is_valid());
assert.strictEqual('XRP', r.to_json());
});
it('from_json().to_json("") == "XRP"', function() {
var r = currency.from_json('');
assert(r.is_valid());
assert(r.is_native());
assert.strictEqual('XRP', r.to_json());
});
it('from_json("XRP").to_json() == "XRP"', function() {
var r = currency.from_json('XRP');
assert(r.is_valid());
@@ -103,6 +115,16 @@ describe('Currency', function() {
var cur = currency.from_human('INR - 30 Indian Rupees');
assert.strictEqual(cur.to_json(), 'INR');
});
it('From human "XRP"', function() {
var cur = currency.from_human('XRP');
assert.strictEqual(cur.to_json(), 'XRP');
assert(cur.is_native(), true);
});
it('From human "XRP - Ripples"', function() {
var cur = currency.from_human('XRP - Ripples');
assert.strictEqual(cur.to_json(), 'XRP');
assert(cur.is_native(), true);
});
});
@@ -162,10 +184,30 @@ describe('Currency', function() {
assert.strictEqual(cur.to_json(), cur.to_human());
});
});
describe('parse_json(currency obj)', function() {
assert.strictEqual('USD', new currency().parse_json(currency.from_json('USD')).to_json());
describe('parse_json', function() {
it('should parse a currency object', function() {
assert.strictEqual('USD', new currency().parse_json(currency.from_json('USD')).to_json());
assert.strictEqual('USD (0.5%pa)', new currency().parse_json(currency.from_json('USD (0.5%pa)')).to_json());
});
it('should clone for parse_json on itself', function() {
var cur = currency.from_json('USD');
var cur2 = currency.from_json(cur);
assert.strictEqual(cur.to_json(), cur2.to_json());
assert.strictEqual('USD (0.5%pa)', new currency().parse_json(currency.from_json('USD (0.5%pa)')).to_json());
cur = currency.from_hex('015841551A748AD2C1F76FF6ECB0CCCD00000000');
cur2 = currency.from_json(cur);
assert.strictEqual(cur.to_json(), cur2.to_json());
});
it('should parse json 0', function() {
var cur = currency.from_json(0);
assert.strictEqual(cur.to_json(), 'XRP');
assert.strictEqual(cur.get_iso(), 'XRP');
});
it('should parse json 0', function() {
var cur = currency.from_json('0');
assert.strictEqual(cur.to_json(), 'XRP');
assert.strictEqual(cur.get_iso(), 'XRP');
});
});
describe('is_valid', function() {
@@ -207,14 +249,16 @@ describe('Currency', function() {
return +(Math.round(num + "e+"+precision) + "e-"+precision);
}
describe('get_interest_at', function() {
it('returns demurred value for demurrage currency', function() {
it('should return demurred value for demurrage currency', function() {
var cur = currency.from_json('015841551A748AD2C1F76FF6ECB0CCCD00000000');
// At start, no demurrage should occur
assert.equal(1, cur.get_interest_at(443845330));
assert.equal(1, precision(cur.get_interest_at(new Date(timeUtil.fromRipple(443845330))), 14));
// After one year, 0.5% should have occurred
assert.equal(0.995, precision(cur.get_interest_at(443845330 + 31536000), 14));
assert.equal(0.995, precision(cur.get_interest_at(new Date(timeUtil.fromRipple(443845330 + 31536000))), 14));
// After one demurrage period, 1/e should have occurred
assert.equal(1/Math.E, cur.get_interest_at(443845330 + 6291418827.05));
@@ -225,6 +269,11 @@ describe('Currency', function() {
// One demurrage period before start, rate should be e
assert.equal(Math.E, cur.get_interest_at(443845330 - 6291418827.05));
});
it('should return 0 for currency without interest', function() {
var cur = currency.from_json('USD - US Dollar');
assert.equal(0, cur.get_interest_at(443845330));
assert.equal(0, cur.get_interest_at(443845330 + 31536000));
});
});
describe('get_iso', function() {
it('should get "XRP" iso_code', function() {

View File

@@ -270,7 +270,8 @@ describe('Message', function(){
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.request_account_info = function(account, callback) {
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {
@@ -306,7 +307,8 @@ describe('Message', function(){
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.request_account_info = function(account, callback) {
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {

View File

@@ -329,12 +329,12 @@ describe('OrderBook', function() {
});
});
it('Set funded amount - funded', function() {
it('Set funded amount - iou/xrp - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'BTC',
currency_pays: 'XRP',
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_gets: 'BTC'
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
var offer = {
@@ -359,7 +359,37 @@ describe('OrderBook', function() {
assert.deepEqual(offer, expected);
});
it('Set funded amount - unfunded', function() {
it('Set funded amount - iou/xrp - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'BTC',
currency_pays: 'XRP',
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
var offer = {
TakerGets: {
value: '100',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
TakerPays: '123456'
};
book.setFundedAmount(offer, '99');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '99',
taker_pays_funded: '122221'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - xrp/iou - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
@@ -370,7 +400,37 @@ describe('OrderBook', function() {
var offer = {
TakerGets: '100',
TakerPays: {
value: '123456',
value: '123.456',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '100.1');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
taker_pays_funded: '123.456'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - xrp/iou - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '123.456',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
@@ -383,67 +443,7 @@ describe('OrderBook', function() {
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '99',
taker_pays_funded: '122221.44'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
taker_pays_funded: '100.1234'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'USD'
});
var offer = {
TakerGets: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
TakerPays: '123'
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '100',
taker_pays_funded: '122.8484050681459'
taker_pays_funded: '122.22144'
};
assert.deepEqual(offer, expected);
@@ -1495,8 +1495,6 @@ describe('OrderBook', function() {
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
PreviousTxnLgrSeq: 8342933,
Sequence: 195,
TakerGets: { currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
@@ -1509,7 +1507,6 @@ describe('OrderBook', function() {
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '0.1129267125000245',
quality: '496.5',
taker_gets_funded: '0.1127013098802639',
taker_pays_funded: '55.95620035555102',
is_fully_funded: false },
@@ -1521,8 +1518,6 @@ describe('OrderBook', function() {
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
@@ -1536,7 +1531,6 @@ describe('OrderBook', function() {
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228',
is_fully_funded: true,
taker_gets_funded: '0.2',
taker_pays_funded: '99.72233516476456'

View File

@@ -5,7 +5,14 @@ var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Request = utils.load_module('request').Request;
var options, spy, mock, stub, remote, callback, database, tx;
var options, remote, callback, database, tx;
var ADDRESS = 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS';
var PEER_ADDRESS = 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX';
var LEDGER_INDEX = 9592219;
var LEDGER_HASH = 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE';
var PAGING_MARKER = '29F992CC252056BF690107D1E8F2D9FBAFF29FF107B62B1D1F4E4E11ADF2CC73';
describe('Remote', function () {
beforeEach(function () {
@@ -28,19 +35,19 @@ describe('Remote', function () {
// 'bitcoin': 'localhost:3000'
// 'bitcoin': 'https://www.bitstamp.net/ripple/bridge/out/bitcoin/'
}
},
}
};
})
});
it('remote server initialization - url object', function() {
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ],
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - no secure property', function() {
var remote = new Remote({
@@ -49,7 +56,7 @@ describe('Remote', function () {
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - secure: false', function() {
var remote = new Remote({
@@ -67,7 +74,7 @@ describe('Remote', function () {
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - invalid host', function() {
assert.throws(
@@ -76,7 +83,7 @@ describe('Remote', function () {
servers: [ { host: '+', port: 443, secure: true } ]
});
}, Error);
})
});
it('remote server initialization - url object - invalid port', function() {
assert.throws(
@@ -144,6 +151,34 @@ describe('Remote', function () {
);
});
it('remote server initialization - set max_fee - number', function() {
var remote = new Remote({
max_fee: 10
});
assert.strictEqual(remote.max_fee, 10);
remote = new Remote({
max_fee: 1234567890
});
assert.strictEqual(remote.max_fee, 1234567890);
});
it('remote server initialization - set max_fee - string fails, should be number', function() {
var remote = new Remote({
max_fee: '1234567890'
});
assert.strictEqual(remote.max_fee, 1e6);
});
it('remote server initialization - max_fee - default', function() {
var remote = new Remote({
max_fee: void(0)
});
assert.strictEqual(remote.max_fee, 1e6);
assert.strictEqual(remote.max_fee, 1000000);
assert.strictEqual((new Remote()).max_fee, 1e6);
});
describe('request constructors', function () {
beforeEach(function () {
callback = function () {}
@@ -185,44 +220,221 @@ describe('Remote', function () {
assert(request instanceof Request);
});
it('request account currencies with ledger index', function() {
var request = remote.requestAccountCurrencies({account: ADDRESS});
assert.strictEqual(request.message.command, 'account_currencies');
assert.strictEqual(request.message.account, ADDRESS);
});
it('request account info with ledger index', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 9592219);
var request = remote.requestAccountInfo({account: ADDRESS, ledger: 9592219});
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.account, ADDRESS);
assert.strictEqual(request.message.ledger_index, 9592219);
});
it('request account info with ledger hash', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
var request = remote.requestAccountInfo({account: ADDRESS, ledger: LEDGER_HASH});
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.account, ADDRESS);
assert.strictEqual(request.message.ledger_hash, LEDGER_HASH);
});
it('request account info with ledger identifier', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'validated');
var request = remote.requestAccountInfo({account: ADDRESS, ledger: 'validated'});
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.account, ADDRESS);
assert.strictEqual(request.message.ledger_index, 'validated');
});
it('request account balance with ledger index', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 9592219);
var request = remote.requestAccountBalance(ADDRESS, 9592219);
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.account_root, ADDRESS);
assert.strictEqual(request.message.ledger_index, 9592219);
});
it('request account balance with ledger hash', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
var request = remote.requestAccountBalance(ADDRESS, LEDGER_HASH);
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.account_root, ADDRESS);
assert.strictEqual(request.message.ledger_hash, LEDGER_HASH);
});
it('request account balance with ledger identifier', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'validated');
var request = remote.requestAccountBalance(ADDRESS, 'validated');
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.account_root, ADDRESS);
assert.strictEqual(request.message.ledger_index, 'validated');
});
})
});
it('pagingAccountRequest', function() {
var request = Remote.accountRequest('account_lines', {account: ADDRESS});
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS
});
});
it('pagingAccountRequest - limit', function() {
var request = Remote.accountRequest('account_lines', {account: ADDRESS, limit: 100});
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS,
limit: 100
});
});
it('pagingAccountRequest - limit, marker', function() {
var request = Remote.accountRequest('account_lines', {account: ADDRESS, limit: 100, marker: PAGING_MARKER, ledger: 9592219});
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS,
limit: 100,
marker: PAGING_MARKER,
ledger_index: 9592219
});
assert(!request.requested);
});
it('accountRequest - limit min', function() {
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: 0}).message.limit, 0);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: -1}).message.limit, 0);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: -1e9}).message.limit, 0);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: -1e24}).message.limit, 0);
});
it('accountRequest - limit max', function() {
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: 1e9}).message.limit, 1e9);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: 1e9+1}).message.limit, 1e9);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: 1e10}).message.limit, 1e9);
assert.strictEqual(Remote.accountRequest('account_lines', {account: ADDRESS, limit: 1e24}).message.limit, 1e9);
});
it('accountRequest - a valid ledger is required when using a marker', function() {
assert.throws(function() {
Remote.accountRequest('account_lines', {account: ADDRESS, marker: PAGING_MARKER})
},'A ledger_index or ledger_hash must be provided when using a marker');
assert.throws(function() {
Remote.accountRequest('account_lines', {account: ADDRESS, marker: PAGING_MARKER, ledger:'validated'})
},'A ledger_index or ledger_hash must be provided when using a marker');
assert.throws(function() {
Remote.accountRequest('account_lines', {account: ADDRESS, marker: PAGING_MARKER, ledger:NaN})
},'A ledger_index or ledger_hash must be provided when using a marker');
assert.throws(function() {
Remote.accountRequest('account_lines', {account: ADDRESS, marker: PAGING_MARKER, ledger:LEDGER_HASH.substr(0,63)})
},'A ledger_index or ledger_hash must be provided when using a marker');
assert.throws(function() {
Remote.accountRequest('account_lines', {account: ADDRESS, marker: PAGING_MARKER, ledger:LEDGER_HASH+'F'})
},'A ledger_index or ledger_hash must be provided when using a marker');
});
it('requestAccountLines, account and callback', function() {
var callback = function() {};
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestAccountLines(
{account: ADDRESS},
callback
);
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS
});
assert(request.requested);
});
it('requestAccountLines, ledger, peer', function() {
var callback = function() {};
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestAccountLines(
{
account: ADDRESS,
ledger: LEDGER_HASH,
peer: PEER_ADDRESS
},
callback
);
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS,
ledger_hash: LEDGER_HASH,
peer: PEER_ADDRESS
});
assert(request.requested);
});
it('requestAccountLines, ledger, peer, limit and marker', function() {
var callback = function() {};
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestAccountLines(
{
account: ADDRESS,
ledger: LEDGER_INDEX,
peer: PEER_ADDRESS,
limit: 200,
marker: PAGING_MARKER
},
callback
);
assert.deepEqual(request.message, {
command: 'account_lines',
id: undefined,
account: ADDRESS,
ledger_index: LEDGER_INDEX,
peer: PEER_ADDRESS,
limit: 200,
marker: PAGING_MARKER
});
assert(request.requested);
});
it('requestAccountOffers, ledger, peer, limit and marker', function() {
var callback = function() {};
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
var request = remote.requestAccountOffers(
{
account: ADDRESS,
ledger: LEDGER_HASH,
peer: PEER_ADDRESS,
limit: 32,
marker: PAGING_MARKER
},
callback
);
assert.deepEqual(request.message, {
command: 'account_offers',
id: undefined,
account: ADDRESS,
ledger_hash: LEDGER_HASH,
peer: PEER_ADDRESS,
limit: 32,
marker: PAGING_MARKER
});
assert(request.requested);
});
it('create remote and get pending transactions', function() {
before(function() {
@@ -282,7 +494,7 @@ describe('Remote', function () {
callback(null, tx);
}
}
})
});
it('should set transaction members correct ', function(done) {
remote = new Remote(options);
@@ -305,9 +517,9 @@ describe('Remote', function () {
},
parseJson: function(json) {}
}
}
};
remote.getPendingTransactions();
})
})
})
});

View File

@@ -1006,12 +1006,6 @@ describe('Server', function() {
assert(server._isConnected());
});
it('Compute fee - transaction', function() {
var server = new Server(new Remote(), 'ws://localhost:5748');
var transaction = new Transaction();
assert.strictEqual(server._computeFee(transaction), '12');
});
it('Compute fee - fee units', function() {
var server = new Server(new Remote(), 'ws://localhost:5748');
var transaction = new Transaction();
@@ -1033,7 +1027,7 @@ describe('Server', function() {
server._load_factor = 256 * 4;
var transaction = new Transaction();
assert.strictEqual(server._computeFee(transaction), '48');
assert.strictEqual(server._computeFee(10), '48');
});
it('Compute reserve', function() {

View File

@@ -835,6 +835,20 @@ describe('Transaction', function() {
assert(transaction._setLastLedger);
});
it('Set Max Fee', function() {
var transaction = new Transaction();
transaction.maxFee('a');
assert(!transaction._setMaxFee);
transaction.maxFee(NaN);
assert(!transaction._setMaxFee);
transaction.maxFee(1000);
assert.strictEqual(transaction._maxFee, 1000);
assert.strictEqual(transaction._setMaxFee, true);
});
it('Rewrite transaction path', function() {
var transaction = new Transaction();
@@ -1546,33 +1560,34 @@ describe('Transaction', function() {
transaction.submit();
});
it.skip('Abort submission', function(done) {
it('Abort submission on presubmit', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
var account = remote.addAccount('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
remote.setSecret('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79', 'snPwFATthTkKnGjEW73q3TL4yci1Q');
var server = new Server(remote, 'wss://s1.ripple.com:443');
server._computeFee = function() { return '12'; };
server._connected = true;
remote._servers.push(server);
remote._connected = true;
remote._ledger_current_index = 1;
var transaction = new Transaction(remote).accountSet('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79');
var account = remote.account('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79');
account._transactionManager._nextSequence = 1;
account._transactionManager._request = function(tx) {
setTimeout(function() {
tx.emit('success', { });
}, 20);
};
transaction.once('presubmit', function() {
transaction.abort();
});
transaction.complete = function() {
return this;
};
function submitCallback(err, res) {
transaction.submit(function(err, res) {
setImmediate(function() {
assert(err);
assert.strictEqual(err.result, 'tejAbort');
done();
});
};
transaction.submit(submitCallback);
transaction.abort();
});
});
});