mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-12 16:45:49 +00:00
Compare commits
77 Commits
0.12.1-rc1
...
0.12.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c3f9ca202 | ||
|
|
f1f0a43f21 | ||
|
|
6b856c3cc5 | ||
|
|
d92888ed73 | ||
|
|
0357840654 | ||
|
|
53cae3a66d | ||
|
|
949a1ca4ae | ||
|
|
e667536a5b | ||
|
|
dde000a4bb | ||
|
|
aa1f5a8e7d | ||
|
|
bfbfcc2894 | ||
|
|
6abfa759aa | ||
|
|
7cbac2e757 | ||
|
|
1012381d3d | ||
|
|
6de96f62df | ||
|
|
e2ed2bdbf6 | ||
|
|
e248c54aa5 | ||
|
|
1c9635edad | ||
|
|
25cf6c52e4 | ||
|
|
7859ef6145 | ||
|
|
6efaa4ac7e | ||
|
|
19e17a8431 | ||
|
|
c865ae9734 | ||
|
|
6959f74073 | ||
|
|
9f4d21e976 | ||
|
|
719f39c01c | ||
|
|
25bb9c7320 | ||
|
|
a160e16abd | ||
|
|
ec31841aa5 | ||
|
|
3e249902c4 | ||
|
|
21bb766f06 | ||
|
|
a883151400 | ||
|
|
3c7fe82cbd | ||
|
|
899fc09704 | ||
|
|
daa45a44b9 | ||
|
|
52494628c3 | ||
|
|
dbf5d21b72 | ||
|
|
441bd4dfbf | ||
|
|
8452f05dda | ||
|
|
0d2325e646 | ||
|
|
90329d3d73 | ||
|
|
ca83a142f8 | ||
|
|
d3b2d3d5c5 | ||
|
|
255177487c | ||
|
|
ed0b75bcde | ||
|
|
06500a7909 | ||
|
|
6e16bf68ae | ||
|
|
ad22480117 | ||
|
|
2fcd09072f | ||
|
|
f0c785b196 | ||
|
|
84fe76bada | ||
|
|
b5ed8f59a7 | ||
|
|
52526f90d7 | ||
|
|
99e6e81e65 | ||
|
|
5af824f5cf | ||
|
|
2166bb2e88 | ||
|
|
ae884c0200 | ||
|
|
423ec7d08a | ||
|
|
914cd6ecb2 | ||
|
|
f221c82859 | ||
|
|
d57be723e6 | ||
|
|
777554809a | ||
|
|
f2b63fa4a8 | ||
|
|
4d06ce7454 | ||
|
|
8da6ec5fa3 | ||
|
|
2a5a8b498d | ||
|
|
a9b7d7d793 | ||
|
|
6578cf5dd7 | ||
|
|
2e21e8a43c | ||
|
|
176e1fd9d4 | ||
|
|
c3b274b18f | ||
|
|
8e134918fb | ||
|
|
2b531d2a1f | ||
|
|
87317dd54a | ||
|
|
618548c88d | ||
|
|
b62f42006c | ||
|
|
c275174f27 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -59,3 +59,5 @@ out/
|
||||
|
||||
# Ignore perf test cache
|
||||
scripts/cache
|
||||
|
||||
eslintrc
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
before_script:
|
||||
- npm install -g eslint
|
||||
- curl 'https://raw.githubusercontent.com/ripple/javascript-style-guide/master/eslintrc' > ./eslintrc
|
||||
- eslint --reset -c ./eslintrc $(git --no-pager diff --name-only --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$")
|
||||
script: MOCHA_REPORTER=tap npm test --coverage
|
||||
after_success:
|
||||
- npm run coveralls
|
||||
|
||||
66
Gulpfile.js
66
Gulpfile.js
@@ -1,3 +1,4 @@
|
||||
'use strict';
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var watch = require('gulp-watch');
|
||||
@@ -8,21 +9,14 @@ var concat = require('gulp-concat');
|
||||
var uglify = require('gulp-uglify');
|
||||
var rename = require('gulp-rename');
|
||||
var webpack = require('webpack');
|
||||
var map = require('map-stream');
|
||||
var bump = require('gulp-bump');
|
||||
var react = require('gulp-react');
|
||||
var flow = require('gulp-flowtype');
|
||||
var argv = require('yargs').argv;
|
||||
//var header = require('gulp-header');
|
||||
// var header = require('gulp-header');
|
||||
|
||||
var pkg = require('./package.json');
|
||||
|
||||
var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> - '
|
||||
+ '<%= new Date().toISOString() %>\n'
|
||||
+ '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>'
|
||||
+ '* Copyright (c) <%= new Date().getFullYear() %> <%= pkg.author.name %>;'
|
||||
+ ' Licensed <%= pkg.license %> */'
|
||||
|
||||
var sjclSrc = [
|
||||
'src/js/sjcl/core/sjcl.js',
|
||||
'src/js/sjcl/core/aes.js',
|
||||
@@ -66,33 +60,33 @@ gulp.task('concat-sjcl', function() {
|
||||
.pipe(gulp.dest('./build/'));
|
||||
});
|
||||
|
||||
gulp.task('build', [ 'concat-sjcl' ], function(callback) {
|
||||
gulp.task('build', ['concat-sjcl'], function(callback) {
|
||||
webpack({
|
||||
cache: true,
|
||||
entry: './src/js/ripple/index.js',
|
||||
output: {
|
||||
library: 'ripple',
|
||||
path: './build/',
|
||||
filename: [ 'ripple-', '.js' ].join(pkg.version)
|
||||
},
|
||||
filename: ['ripple-', '.js'].join(pkg.version)
|
||||
}
|
||||
}, callback);
|
||||
});
|
||||
|
||||
gulp.task('build-min', [ 'build' ], function(callback) {
|
||||
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
|
||||
gulp.task('build-min', ['build'], function() {
|
||||
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
|
||||
.pipe(uglify())
|
||||
.pipe(rename([ 'ripple-', '-min.js' ].join(pkg.version)))
|
||||
.pipe(rename(['ripple-', '-min.js'].join(pkg.version)))
|
||||
.pipe(gulp.dest('./build/'));
|
||||
});
|
||||
|
||||
gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
|
||||
gulp.task('build-debug', ['concat-sjcl'], function(callback) {
|
||||
webpack({
|
||||
cache: true,
|
||||
entry: './src/js/ripple/index.js',
|
||||
output: {
|
||||
library: 'ripple',
|
||||
path: './build/',
|
||||
filename: [ 'ripple-', '-debug.js' ].join(pkg.version)
|
||||
filename: ['ripple-', '-debug.js'].join(pkg.version)
|
||||
},
|
||||
debug: true,
|
||||
devtool: 'eval'
|
||||
@@ -105,11 +99,12 @@ gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
|
||||
*/
|
||||
|
||||
function buildUseError(cons) {
|
||||
return 'var {<CONS>:function(){throw new Error("Class is unavailable in this build: <CONS>")}}'
|
||||
.replace(new RegExp('<CONS>', 'g'), 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) {
|
||||
gulp.task('build-core', ['concat-sjcl'], function(callback) {
|
||||
webpack({
|
||||
entry: [
|
||||
'./src/js/ripple/remote.js'
|
||||
@@ -125,7 +120,7 @@ gulp.task('build-core', [ 'concat-sjcl' ], function(callback) {
|
||||
output: {
|
||||
library: 'ripple',
|
||||
path: './build/',
|
||||
filename: [ 'ripple-', '-core.js' ].join(pkg.version)
|
||||
filename: ['ripple-', '-core.js'].join(pkg.version)
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin()
|
||||
@@ -133,34 +128,35 @@ gulp.task('build-core', [ 'concat-sjcl' ], function(callback) {
|
||||
}, callback);
|
||||
});
|
||||
|
||||
gulp.task('bower-build', [ 'build' ], function(callback) {
|
||||
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
|
||||
gulp.task('bower-build', ['build'], function() {
|
||||
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))
|
||||
gulp.task('bower-build-min', ['build-min'], function() {
|
||||
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))
|
||||
gulp.task('bower-build-debug', ['build-debug'], function() {
|
||||
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(bump({version: pkg.version}))
|
||||
.pipe(gulp.dest('./dist/'));
|
||||
});
|
||||
|
||||
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
|
||||
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug',
|
||||
'bower-version']);
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
|
||||
gulp.watch('src/js/ripple/*', ['build-debug']);
|
||||
});
|
||||
|
||||
// To use this, each javascript file must have /* @flow */ on the first line
|
||||
@@ -177,25 +173,25 @@ gulp.task('strip', function() {
|
||||
.pipe(watch('src/js/ripple/*.js'))
|
||||
.pipe(cleanDest('out')) // delete outdated output file before stripping
|
||||
.pipe(plumber()) // prevent an error in one file from ending build
|
||||
.pipe(react({ stripTypes: true }).on('error', logPluginError))
|
||||
.pipe(react({stripTypes: true}).on('error', logPluginError))
|
||||
.pipe(filelog())
|
||||
.pipe(gulp.dest('out'));
|
||||
});
|
||||
|
||||
gulp.task('version-bump', function() {
|
||||
if (!argv.type) {
|
||||
throw new Error("No type found, pass it in using the --type argument");
|
||||
throw new Error('No type found, pass it in using the --type argument');
|
||||
}
|
||||
|
||||
gulp.src('./package.json')
|
||||
.pipe(bump({ type: argv.type }))
|
||||
.pipe(bump({type: argv.type}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
|
||||
gulp.task('version-beta', function() {
|
||||
gulp.src('./package.json')
|
||||
.pipe(bump({ version: pkg.version + '-beta' }))
|
||||
.pipe(bump({version: pkg.version + '-beta'}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
|
||||
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);
|
||||
gulp.task('default', ['concat-sjcl', 'build', 'build-debug', 'build-min']);
|
||||
|
||||
35
HISTORY.md
35
HISTORY.md
@@ -1,16 +1,47 @@
|
||||
##0.12.2
|
||||
|
||||
+ [Check that stack trace is available, fixes logging in browser](https://github.com/ripple/ripple-lib/commit/53cae3a66d48e88e8a6bbb96d6489ce7b9e22975)
|
||||
|
||||
##0.12.1
|
||||
|
||||
**Breaking Changes**
|
||||
|
||||
+ [Removed support for parsing native amounts in floating point format](https://github.com/ripple/ripple-lib/commit/e80cd1ff55deae9cd5b0ae85be957f86856b887e)
|
||||
|
||||
|
||||
**Changes**
|
||||
|
||||
+ [Fix handling of false parameters in requestLedger](https://github.com/ripple/ripple-lib/commit/6023efed41b7812b3bab660a1c0dc9f0a21000b9)
|
||||
+ [Fix taker pays funded calculation](https://github.com/ripple/ripple-lib/commit/5af824f5cf46c7b9caa58ee0a757bf854d26c8dc)
|
||||
|
||||
+ [Fix order funded amount calculation](https://github.com/ripple/ripple-lib/commit/b2cdb1a6aed968b1f306e8dadbd4b7ca37e5aa03)
|
||||
|
||||
+ [Remove `Features` field requirements from `SetFee` transaction format](https://github.com/ripple/ripple-lib/commit/a20a649013646710c078d4ce1e210f87c7fe74fe)
|
||||
+ [Fix handling of quality in order book](https://github.com/ripple/ripple-lib/commit/2a5a8b498da60df738ba18d5c265f34771e8a1af)
|
||||
|
||||
+ [Fix currency parsing of non-alphanumeric and no-currency currencies](https://github.com/ripple/ripple-lib/commit/2166bb2e88eae8d5f1aba77338f69e8a9edf6a6f)
|
||||
|
||||
+ [Add Amount.strict_mode for toggling range validation](https://github.com/ripple/ripple-lib/commit/b5ed8f59a7dab1a17491618b8d9193646c314fb4)
|
||||
|
||||
+ [Add filename and line number to log, use log.warn() for deprecations](https://github.com/ripple/ripple-lib/commit/90329d3d73f1a76675063655b407513e32dc048b)
|
||||
|
||||
+ [Add GlobalFreeze and NoFreeze flags](https://github.com/ripple/ripple-lib/commit/e2ed2bdbf6f01c7d4d690c2cf0b83fba94558dd7)
|
||||
|
||||
+ [Fix handling of falsy parameters in requestLedger](https://github.com/ripple/ripple-lib/commit/6023efed41b7812b3bab660a1c0dc9f0a21000b9)
|
||||
|
||||
+ [Fix Base:decode](https://github.com/ripple/ripple-lib/commit/719f39c01c6941d9a650aa94f95617793dd53ea0)
|
||||
|
||||
+ [Fix Amount: clone in ratio_human, product_human](https://github.com/ripple/ripple-lib/commit/19e17a8431550cf156b1ad669a19dedfe4e28e4a)
|
||||
|
||||
+ [Fix Amount.to_human for very small numbers](https://github.com/ripple/ripple-lib/commit/6abfa759aa09d68074ac558d96c4b126a7cd1719)
|
||||
|
||||
+ [Refactor base conversion](https://github.com/ripple/ripple-lib/commit/f2b63fa4a80663eb29472bc6bb1aea8159f1f205)
|
||||
|
||||
+ [Update binary transaction format](https://github.com/ripple/ripple-lib/commit/8e134918fb4c22983320a3102f955e4568bb1dfb)
|
||||
|
||||
+ [Add DefaultRipple account flag](https://github.com/ripple/ripple-lib/commit/3e249902c4cf25b4da5e75048c84ae391be83b10)
|
||||
|
||||
+ [Remove `Features` field requirement in `SetFee` transaction format](https://github.com/ripple/ripple-lib/commit/a20a649013646710c078d4ce1e210f87c7fe74fe)
|
||||
|
||||
+ [Remove `RegularKey` field requirement in `SetRegularKey` transaction format](https://github.com/ripple/ripple-lib/commit/c275174f27877ba8f389eb4efe969feb514d6e46)
|
||||
|
||||
|
||||
##0.12.0
|
||||
|
||||
@@ -18,7 +18,7 @@ This file provides step-by-step walkthroughs for some of the most common usages
|
||||
|
||||
##Connecting to the Ripple network
|
||||
|
||||
1. [Get ripple-lib](README.md#getting-ripple-lib)
|
||||
1. [Get ripple-lib](../README.md#installation)
|
||||
2. Load the ripple-lib module into a Node.js file or webpage:
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
|
||||
72
eslint.json
72
eslint.json
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"no-use-before-define": 1,
|
||||
"no-undef": 1,
|
||||
"no-unused-expressions": 1,
|
||||
"no-unused-vars": 1,
|
||||
"no-extend-native": 1,
|
||||
"no-native-reassign": 1,
|
||||
"no-trailing-spaces": 1,
|
||||
"no-empty": 1,
|
||||
"no-inner-declarations": 1,
|
||||
"no-irregular-whitespace": 1,
|
||||
"no-negated-in-lhs": 1,
|
||||
"no-obj-calls": 1,
|
||||
"no-reserved-keys": 1,
|
||||
"no-sparse-arrays": 1,
|
||||
"no-unreachable": 1,
|
||||
"use-isnan": 1,
|
||||
"valid-jsdoc": 1,
|
||||
"valid-typeof": 1,
|
||||
"block-scoped-var": 1,
|
||||
"dot-notation": 1,
|
||||
"semi": 1,
|
||||
"curly": 1,
|
||||
"eqeqeq": 1,
|
||||
"no-else-return": 1,
|
||||
"new-cap": 1,
|
||||
"new-parens": 1,
|
||||
"no-comma-dangle": 1,
|
||||
"no-empty-label": 1,
|
||||
"no-eval": 1,
|
||||
"no-extra-bind": 1,
|
||||
"no-fallthrough": 1,
|
||||
"no-lone-blocks": 1,
|
||||
"no-loop-func": 1,
|
||||
"no-multi-spaces": 1,
|
||||
"no-return-assign": 1,
|
||||
"no-sequences": 1,
|
||||
"no-with": 1,
|
||||
"radix": 1,
|
||||
"yoda": [ 1, "never" ],
|
||||
"no-catch-shadow": 1,
|
||||
"no-shadow-restricted-names": 1,
|
||||
"no-delete-var": 1,
|
||||
"no-undefined": 1,
|
||||
"handle-callback-err": 1,
|
||||
"brace-style": [ 1, "1tbs", { "allowSingleLine": false } ],
|
||||
"comma-spacing": [ 1, { "before": false, "after": true } ],
|
||||
"comma-style": [ 1, "last" ],
|
||||
"consistent-this": [ 1, "self" ],
|
||||
"func-style": [ 1, "declaration" ],
|
||||
"key-spacing": [ 1, { "beforeColon": false, "afterColon": true } ],
|
||||
"max-nested-callbacks": [ 1, 2 ],
|
||||
"no-lonely-if": 1,
|
||||
"no-mixed-spaces-and-tabs": 1,
|
||||
"no-multiple-empty-lines": 1,
|
||||
"no-space-before-semi": 1,
|
||||
"no-spaced-func": 1,
|
||||
"space-after-keywords": [ 1, "always" ],
|
||||
"space-infix-ops": 1,
|
||||
"space-return-throw-case": 1,
|
||||
"spaced-line-comment": 1,
|
||||
"max-params": [ 1, 4 ],
|
||||
"max-depth": [1, 3 ],
|
||||
"max-len": [ 1, 80 ],
|
||||
"quotes": [ 1, "single" ]
|
||||
}
|
||||
}
|
||||
20
npm-shrinkwrap.json
generated
20
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.12.1-rc1",
|
||||
"version": "0.12.2",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.9.0",
|
||||
@@ -18,9 +18,9 @@
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
|
||||
},
|
||||
"lodash": {
|
||||
"version": "3.1.0",
|
||||
"version": "3.5.0",
|
||||
"from": "lodash@>=3.1.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.1.0.tgz"
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz"
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "2.5.0",
|
||||
@@ -28,9 +28,9 @@
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
|
||||
},
|
||||
"ripple-wallet-generator": {
|
||||
"version": "1.0.1",
|
||||
"from": "ripple-wallet-generator@1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.1.tgz"
|
||||
"version": "1.0.2",
|
||||
"from": "ripple-wallet-generator@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.2.tgz"
|
||||
},
|
||||
"superagent": {
|
||||
"version": "0.18.2",
|
||||
@@ -157,9 +157,9 @@
|
||||
"from": "bindings@>=1.2.0 <1.3.0"
|
||||
},
|
||||
"nan": {
|
||||
"version": "1.6.1",
|
||||
"version": "1.6.2",
|
||||
"from": "nan@>=1.6.0 <1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.1.tgz"
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -173,9 +173,9 @@
|
||||
"from": "bindings@>=1.2.0 <1.3.0"
|
||||
},
|
||||
"nan": {
|
||||
"version": "1.6.1",
|
||||
"version": "1.6.2",
|
||||
"from": "nan@>=1.6.0 <1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.1.tgz"
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.12.1-rc1",
|
||||
"version": "0.12.2",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"files": [
|
||||
"src/js/*",
|
||||
@@ -20,12 +20,12 @@
|
||||
"extend": "~1.2.1",
|
||||
"lodash": "^3.1.0",
|
||||
"lru-cache": "~2.5.0",
|
||||
"ripple-wallet-generator": "1.0.1",
|
||||
"ripple-wallet-generator": "1.0.2",
|
||||
"ws": "~0.7.1",
|
||||
"superagent": "^0.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"assert-diff": "0.0.4",
|
||||
"assert-diff": "^1.0.1",
|
||||
"coveralls": "~2.10.0",
|
||||
"eslint": "^0.13.0",
|
||||
"gulp": "~3.8.10",
|
||||
@@ -50,9 +50,9 @@
|
||||
"scripts": {
|
||||
"build": "node_modules/.bin/gulp",
|
||||
"pretest": "node_modules/.bin/gulp concat-sjcl",
|
||||
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/mocha/bin/_mocha -- --reporter ${MOCHA_REPORTER:=spec} test/*-test.js",
|
||||
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/mocha/bin/_mocha -- --reporter ${MOCHA_REPORTER:=spec} --timeout 10000 --slow 500 test/*-test.js",
|
||||
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
|
||||
"lint": "eslint --reset -c eslint.json src/js/ripple/*.js",
|
||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/master/eslintrc'; fi; eslint --reset -c eslintrc src/js/ripple/*.js",
|
||||
"perf": "./scripts/perf_test.sh"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
// Represent Ripple amounts and currencies.
|
||||
// - Numbers in hex are big-endian.
|
||||
|
||||
var assert = require('assert');
|
||||
var extend = require('extend');
|
||||
var utils = require('./utils');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Seed = require('./seed').Seed;
|
||||
var Currency = require('./currency').Currency;
|
||||
var assert = require('assert');
|
||||
var extend = require('extend');
|
||||
var utils = require('./utils');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Seed = require('./seed').Seed;
|
||||
var Currency = require('./currency').Currency;
|
||||
var GlobalBigNumber = require('bignumber.js');
|
||||
|
||||
var BigNumber = GlobalBigNumber.another({
|
||||
@@ -24,40 +26,46 @@ function Amount() {
|
||||
// integer : XRP
|
||||
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
|
||||
|
||||
this._value = new BigNumber(NaN);
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
this._value = new BigNumber(NaN);
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set strict_mode = false to disable amount range checking
|
||||
*/
|
||||
|
||||
Amount.strict_mode = true;
|
||||
|
||||
var consts = {
|
||||
currency_xns: 0,
|
||||
currency_one: 1,
|
||||
xns_precision: 6,
|
||||
currency_xns: 0,
|
||||
currency_one: 1,
|
||||
xns_precision: 6,
|
||||
|
||||
// bi_ prefix refers to "big integer"
|
||||
// TODO: we shouldn't expose our BigNumber library publicly
|
||||
bi_5: new BigNumber(5),
|
||||
bi_7: new BigNumber(7),
|
||||
bi_10: new BigNumber(10),
|
||||
bi_1e14: new BigNumber(1e14),
|
||||
bi_1e16: new BigNumber(1e16),
|
||||
bi_1e17: new BigNumber(1e17),
|
||||
bi_1e32: new BigNumber(1e32),
|
||||
bi_man_max_value: new BigNumber('9999999999999999'),
|
||||
bi_man_min_value: new BigNumber(1e15),
|
||||
bi_xns_max: new BigNumber(1e17),
|
||||
bi_xns_min: new BigNumber(-1e17),
|
||||
bi_xns_unit: new BigNumber(1e6),
|
||||
bi_5: new BigNumber(5),
|
||||
bi_7: new BigNumber(7),
|
||||
bi_10: new BigNumber(10),
|
||||
bi_1e14: new BigNumber(1e14),
|
||||
bi_1e16: new BigNumber(1e16),
|
||||
bi_1e17: new BigNumber(1e17),
|
||||
bi_1e32: new BigNumber(1e32),
|
||||
bi_man_max_value: new BigNumber('9999999999999999'),
|
||||
bi_man_min_value: new BigNumber(1e15),
|
||||
bi_xns_max: new BigNumber(1e17),
|
||||
bi_xns_min: new BigNumber(-1e17),
|
||||
bi_xns_unit: new BigNumber(1e6),
|
||||
|
||||
cMinOffset: -96,
|
||||
cMaxOffset: 80,
|
||||
cMinOffset: -96,
|
||||
cMaxOffset: 80,
|
||||
|
||||
// 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'
|
||||
min_value: '-1000000000000000e-96'
|
||||
};
|
||||
|
||||
var MAX_XRP_VALUE = new BigNumber(1e11);
|
||||
@@ -127,7 +135,7 @@ Amount.prototype.add = function(addend) {
|
||||
var addendAmount = Amount.from_json(addend);
|
||||
|
||||
if (!this.is_comparable(addendAmount)) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
|
||||
return this._copy(this._value.plus(addendAmount._value));
|
||||
@@ -180,24 +188,26 @@ Amount.prototype.divide = function(divisor) {
|
||||
* @this {Amount} The numerator (top half) of the fraction.
|
||||
* @param {Amount} denominator The denominator (bottom half) of the fraction.
|
||||
* @param opts Options for the calculation.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @param opts.reference_date {Date|Number} Date based on which
|
||||
* demurrage/interest should be applied. Can be given as JavaScript Date or int
|
||||
* for Ripple epoch.
|
||||
* @return {Amount} The resulting ratio. Unit will be the same as numerator.
|
||||
*/
|
||||
|
||||
Amount.prototype.ratio_human = function(denominator, opts) {
|
||||
opts = extend({ }, opts);
|
||||
|
||||
var numerator = this;
|
||||
var numerator = this.clone();
|
||||
|
||||
denominator = Amount.from_json(denominator);
|
||||
|
||||
// If either operand is NaN, the result is NaN.
|
||||
if (!numerator.is_valid() || !denominator.is_valid()) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
|
||||
if (denominator.is_zero()) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
|
||||
// Apply interest/demurrage
|
||||
@@ -239,11 +249,11 @@ Amount.prototype.ratio_human = function(denominator, opts) {
|
||||
*
|
||||
* @see Amount#ratio_human
|
||||
*
|
||||
* @this {Amount} The first factor of the product.
|
||||
* @param {Amount} factor The second factor of the product.
|
||||
* @param opts Options for the calculation.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @param {Object} opts Options for the calculation.
|
||||
* @param {Date|Number} opts.reference_date Date based on which
|
||||
* demurrage/interest should be applied. Can be given as JavaScript Date or int
|
||||
* for Ripple epoch.
|
||||
* @return {Amount} The product. Unit will be the same as the first factor.
|
||||
*/
|
||||
Amount.prototype.product_human = function(factor, opts) {
|
||||
@@ -253,7 +263,7 @@ Amount.prototype.product_human = function(factor, opts) {
|
||||
|
||||
// If either operand is NaN, the result is NaN.
|
||||
if (!this.is_valid() || !factor.is_valid()) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
|
||||
// Apply interest/demurrage
|
||||
@@ -280,6 +290,7 @@ Amount.prototype.product_human = function(factor, opts) {
|
||||
/**
|
||||
* Turn this amount into its inverse.
|
||||
*
|
||||
* @return {Amount} self
|
||||
* @private
|
||||
*/
|
||||
Amount.prototype._invert = function() {
|
||||
@@ -301,7 +312,8 @@ Amount.prototype.invert = function() {
|
||||
* 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
|
||||
* 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
|
||||
@@ -322,22 +334,24 @@ Amount.prototype.invert = function() {
|
||||
* - 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
|
||||
* @throws {Error} if offset exceeds legal ranges, meaning the amount value is
|
||||
* bigger than supported
|
||||
*/
|
||||
|
||||
Amount.prototype.canonicalize = function(roundingMode) {
|
||||
if (this._is_native) {
|
||||
this._value = this._value.round(6, BigNumber.ROUND_DOWN);
|
||||
} else if (roundingMode) {
|
||||
this._value = new BigNumber(this._value.toPrecision(16, roundingMode));
|
||||
} else {
|
||||
if (roundingMode) {
|
||||
this._value = new BigNumber(this._value.toPrecision(16, roundingMode));
|
||||
} else {
|
||||
this._value = new BigNumber(this._value.toPrecision(16));
|
||||
}
|
||||
this._value = new BigNumber(this._value.toPrecision(16));
|
||||
}
|
||||
};
|
||||
|
||||
Amount.prototype._check_limits = function() {
|
||||
if (!Amount.strict_mode) {
|
||||
return this;
|
||||
}
|
||||
if (this._value.isNaN() || this._value.isZero()) {
|
||||
return this;
|
||||
}
|
||||
@@ -370,7 +384,7 @@ Amount.prototype._copy = function(value) {
|
||||
Amount.prototype.compareTo = function(to) {
|
||||
var toAmount = Amount.from_json(to);
|
||||
if (!this.is_comparable(toAmount)) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
return this._value.comparedTo(toAmount._value);
|
||||
};
|
||||
@@ -378,10 +392,10 @@ Amount.prototype.compareTo = function(to) {
|
||||
// Make d a copy of this. Returns d.
|
||||
// Modification of objects internally refered to is not allowed.
|
||||
Amount.prototype.copyTo = function(d, negate) {
|
||||
d._value = negate ? this._value.negated() : this._value;
|
||||
d._value = negate ? this._value.negated() : this._value;
|
||||
d._is_native = this._is_native;
|
||||
d._currency = this._currency;
|
||||
d._issuer = this._issuer;
|
||||
d._currency = this._currency;
|
||||
d._issuer = this._issuer;
|
||||
return d;
|
||||
};
|
||||
|
||||
@@ -424,7 +438,9 @@ Amount.prototype.is_valid = function() {
|
||||
};
|
||||
|
||||
Amount.prototype.is_valid_full = function() {
|
||||
return this.is_valid() && this._currency.is_valid() && this._issuer.is_valid();
|
||||
return this.is_valid()
|
||||
&& this._currency.is_valid()
|
||||
&& this._issuer.is_valid();
|
||||
};
|
||||
|
||||
Amount.prototype.is_zero = function() {
|
||||
@@ -451,11 +467,15 @@ Amount.prototype.negate = function() {
|
||||
* 100 => 100000000/XRP
|
||||
*
|
||||
*
|
||||
* The regular expression below matches above cases, broken down for better understanding:
|
||||
* The regular expression below matches above cases, broken down for better
|
||||
* understanding:
|
||||
*
|
||||
* ([A-z]{3}|[0-9]{3}) // either 3 letter alphabetic currency-code or 3 digit numeric currency-code. See ISO 4217
|
||||
* $ // end of string
|
||||
* // either 3 letter alphabetic currency-code or 3 digit numeric currency-code.
|
||||
* // See ISO 4217
|
||||
* ([A-z]{3}|[0-9]{3})
|
||||
*
|
||||
* // end of string
|
||||
* $
|
||||
*/
|
||||
|
||||
Amount.prototype.parse_human = function(j, opts) {
|
||||
@@ -467,7 +487,9 @@ Amount.prototype.parse_human = function(j, opts) {
|
||||
var value;
|
||||
var currency;
|
||||
|
||||
var words = j.split(' ').filter(function(word) { return word !== ''; });
|
||||
var words = j.split(' ').filter(function(word) {
|
||||
return word !== '';
|
||||
});
|
||||
|
||||
function isNumber(s) {
|
||||
return isFinite(s) && s !== '' && s !== null;
|
||||
@@ -481,7 +503,7 @@ Amount.prototype.parse_human = function(j, opts) {
|
||||
value = words[0].slice(0, -3);
|
||||
currency = words[0].slice(-3);
|
||||
if (!(isNumber(value) && currency.match(currency_RE))) {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
}
|
||||
} else if (words.length === 2) {
|
||||
@@ -495,10 +517,10 @@ Amount.prototype.parse_human = function(j, opts) {
|
||||
value = words[0];
|
||||
currency = words[1];
|
||||
} else {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
} else {
|
||||
return Amount.NaN();
|
||||
return new Amount(NaN);
|
||||
}
|
||||
|
||||
currency = currency.toUpperCase();
|
||||
@@ -516,7 +538,7 @@ Amount.prototype.parse_human = function(j, opts) {
|
||||
};
|
||||
|
||||
Amount.prototype.parse_issuer = function(issuer) {
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -533,41 +555,43 @@ Amount.prototype.parse_issuer = function(issuer) {
|
||||
* Prices involving demurraging currencies are tricky, since they depend on the
|
||||
* base and counter currencies.
|
||||
*
|
||||
* @param quality {String} 8 hex bytes quality or 32 hex bytes BookDirectory
|
||||
* @param {String} quality 8 hex bytes quality or 32 hex bytes BookDirectory
|
||||
* index.
|
||||
* @param counterCurrency {Currency|String} Currency of the resulting Amount
|
||||
* @param {Currency|String} counterCurrency currency of the resulting Amount
|
||||
* object.
|
||||
* @param counterIssuer {Issuer|String} Issuer of the resulting Amount object.
|
||||
* @param opts Additional options
|
||||
* @param opts.inverse {Boolean} If true, return the inverse of the price
|
||||
* @param {Issuer|String} counterIssuer Issuer of the resulting Amount object.
|
||||
* @param {Object} opts Additional options
|
||||
* @param {Boolean} opts.inverse If true, return the inverse of the price
|
||||
* encoded in the quality.
|
||||
* @param opts.base_currency {Currency|String} The other currency. This plays a
|
||||
* @param {Currency|String} opts.base_currency The other currency. This plays a
|
||||
* role with interest-bearing or demurrage currencies. In that case the
|
||||
* demurrage has to be applied when the quality is decoded, otherwise the
|
||||
* price will be false.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @param opts.xrp_as_drops {Boolean} Whether XRP amount should be treated as
|
||||
* @param {Date|Number} opts.reference_date Date based on which
|
||||
* demurrage/interest should be applied. Can be given as JavaScript Date or int
|
||||
* for Ripple epoch.
|
||||
* @param {Boolean} opts.xrp_as_drops Whether XRP amount should be treated as
|
||||
* drops. When the base currency is XRP, the quality is calculated in drops.
|
||||
* For human use however, we want to think of 1000000 drops as 1 XRP and
|
||||
* prices as per-XRP instead of per-drop.
|
||||
* @return {Amount} self
|
||||
*/
|
||||
Amount.prototype.parse_quality = function(quality, counterCurrency, counterIssuer, opts)
|
||||
{
|
||||
Amount.prototype.parse_quality =
|
||||
function(quality, counterCurrency, counterIssuer, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var baseCurrency = Currency.from_json(opts.base_currency);
|
||||
|
||||
var mantissa_hex = quality.substring(quality.length-14);
|
||||
var offset_hex = quality.substring(quality.length-16, quality.length-14);
|
||||
var mantissa = new BigNumber(mantissa_hex, 16);
|
||||
var offset = parseInt(offset_hex, 16) - 100;
|
||||
var mantissa_hex = quality.substring(quality.length - 14);
|
||||
var offset_hex = quality.substring(quality.length - 16, quality.length - 14);
|
||||
var mantissa = new BigNumber(mantissa_hex, 16);
|
||||
var offset = parseInt(offset_hex, 16) - 100;
|
||||
|
||||
var value = new BigNumber(mantissa.toString() + 'e' + offset.toString());
|
||||
|
||||
this._currency = Currency.from_json(counterCurrency);
|
||||
this._issuer = UInt160.from_json(counterIssuer);
|
||||
this._is_native = this._currency.is_native();
|
||||
this._currency = Currency.from_json(counterCurrency);
|
||||
this._issuer = UInt160.from_json(counterIssuer);
|
||||
this._is_native = this._currency.is_native();
|
||||
|
||||
if (this._is_native && baseCurrency.is_native()) {
|
||||
throw new Error('XRP/XRP quality is not allowed');
|
||||
@@ -608,7 +632,8 @@ Amount.prototype.parse_quality = function(quality, counterCurrency, counterIssue
|
||||
|
||||
this._set_value(nativeAdjusted);
|
||||
|
||||
if (opts.reference_date && baseCurrency.is_valid() && baseCurrency.has_interest()) {
|
||||
if (opts.reference_date && baseCurrency.is_valid()
|
||||
&& baseCurrency.has_interest()) {
|
||||
var interest = baseCurrency.get_interest_at(opts.reference_date);
|
||||
this._set_value(this._value.dividedBy(interest.toString()));
|
||||
}
|
||||
@@ -617,9 +642,9 @@ Amount.prototype.parse_quality = function(quality, counterCurrency, counterIssue
|
||||
};
|
||||
|
||||
Amount.prototype.parse_number = function(n) {
|
||||
this._is_native = false;
|
||||
this._currency = Currency.from_json(1);
|
||||
this._issuer = UInt160.from_json(1);
|
||||
this._is_native = false;
|
||||
this._currency = Currency.from_json(1);
|
||||
this._issuer = UInt160.from_json(1);
|
||||
this._set_value(new BigNumber(n));
|
||||
return this;
|
||||
};
|
||||
@@ -628,21 +653,22 @@ Amount.prototype.parse_number = function(n) {
|
||||
Amount.prototype.parse_json = function(j) {
|
||||
switch (typeof j) {
|
||||
case 'string':
|
||||
// .../.../... notation is not a wire format. But allowed for easier testing.
|
||||
// .../.../... notation is not a wire format. But allowed for easier
|
||||
// testing.
|
||||
var m = j.match(/^([^/]+)\/([^/]+)(?:\/(.+))?$/);
|
||||
|
||||
if (m) {
|
||||
this._currency = Currency.from_json(m[2]);
|
||||
this._currency = Currency.from_json(m[2]);
|
||||
if (m[3]) {
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
} else {
|
||||
this._issuer = UInt160.from_json('1');
|
||||
this._issuer = UInt160.from_json('1');
|
||||
}
|
||||
this.parse_value(m[1]);
|
||||
} else {
|
||||
this.parse_native(j);
|
||||
this._currency = Currency.from_json('0');
|
||||
this._issuer = UInt160.from_json('0');
|
||||
this._currency = Currency.from_json('0');
|
||||
this._issuer = UInt160.from_json('0');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -681,9 +707,9 @@ Amount.prototype.parse_json = function(j) {
|
||||
// - float = with precision 6
|
||||
// XXX Improvements: disallow leading zeros.
|
||||
Amount.prototype.parse_native = function(j) {
|
||||
if (typeof j === 'string' && !isNaN(parseInt(j))) {
|
||||
if (j && typeof j === 'string' && !isNaN(j)) {
|
||||
if (j.indexOf('.') >= 0) {
|
||||
throw new Error('Native amounts must be specified in integer drops')
|
||||
throw new Error('Native amounts must be specified in integer drops');
|
||||
}
|
||||
var value = new BigNumber(j);
|
||||
this._is_native = true;
|
||||
@@ -704,7 +730,7 @@ Amount.prototype.parse_value = function(j) {
|
||||
};
|
||||
|
||||
Amount.prototype.set_currency = function(c) {
|
||||
this._currency = Currency.from_json(c);
|
||||
this._currency = Currency.from_json(c);
|
||||
this._is_native = this._currency.is_native();
|
||||
return this;
|
||||
};
|
||||
@@ -741,18 +767,18 @@ Amount.prototype.to_text = function() {
|
||||
// Use e notation.
|
||||
// XXX Clamp output.
|
||||
return sign + mantissa.toString() + 'e' + offset.toString();
|
||||
} else {
|
||||
var val = '000000000000000000000000000' + mantissa.toString()
|
||||
+ '00000000000000000000000';
|
||||
var pre = val.substring(0, offset + 43);
|
||||
var post = val.substring(offset + 43);
|
||||
var s_pre = pre.match(/[1-9].*$/); // Everything but leading zeros.
|
||||
var s_post = post.match(/[1-9]0*$/); // Last non-zero plus trailing zeros.
|
||||
|
||||
return sign + (s_pre ? s_pre[0] : '0')
|
||||
+ (s_post ? '.' + post.substring(0, 1 + post.length - s_post[0].length) : '');
|
||||
}
|
||||
|
||||
var val = '000000000000000000000000000'
|
||||
+ mantissa.toString()
|
||||
+ '00000000000000000000000';
|
||||
var pre = val.substring(0, offset + 43);
|
||||
var post = val.substring(offset + 43);
|
||||
var s_pre = pre.match(/[1-9].*$/); // Everything but leading zeros.
|
||||
var s_post = post.match(/[1-9]0*$/); // Last non-zero plus trailing zeros.
|
||||
|
||||
return sign + (s_pre ? s_pre[0] : '0')
|
||||
+ (s_post ? '.' + post.substring(0, 1 + post.length - s_post[0].length) : '');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -763,7 +789,7 @@ Amount.prototype.to_text = function() {
|
||||
* User should not store amount objects after the interest is applied. This is
|
||||
* intended by display functions such as toHuman().
|
||||
*
|
||||
* @param referenceDate {Date|Number} Date based on which demurrage/interest
|
||||
* @param {Date|Number} referenceDate Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @return {Amount} The amount with interest applied.
|
||||
*/
|
||||
@@ -781,21 +807,23 @@ Amount.prototype.applyInterest = function(referenceDate) {
|
||||
* @example
|
||||
* var pretty = amount.to_human({precision: 2});
|
||||
*
|
||||
* @param opts Options for formatter.
|
||||
* @param opts.precision {Number} Max. number of digits after decimal point.
|
||||
* @param opts.min_precision {Number} Min. number of digits after dec. point.
|
||||
* @param opts.skip_empty_fraction {Boolean} Don't show fraction if it is zero,
|
||||
* @param {Object} opts Options for formatter.
|
||||
* @param {Number} opts.precision Max. number of digits after decimal point.
|
||||
* @param {Number} opts.min_precision Min. number of digits after dec. point.
|
||||
* @param {Boolean} opts.skip_empty_fraction Don't show fraction if it is zero,
|
||||
* even if min_precision is set.
|
||||
* @param opts.max_sig_digits {Number} Maximum number of significant digits.
|
||||
* @param {Number} opts.max_sig_digits Maximum number of significant digits.
|
||||
* Will cut fractional part, but never integer part.
|
||||
* @param opts.group_sep {Boolean|String} Whether to show a separator every n
|
||||
* @param {Boolean|String} opts.group_sep Whether to show a separator every n
|
||||
* digits, if a string, that value will be used as the separator. Default: ','
|
||||
* @param opts.group_width {Number} How many numbers will be grouped together,
|
||||
* @param {Number} opts.group_width How many numbers will be grouped together,
|
||||
* default: 3.
|
||||
* @param opts.signed {Boolean|String} Whether negative numbers will have a
|
||||
* @param {Boolean|String} opts.signed Whether negative numbers will have a
|
||||
* prefix. If String, that string will be used as the prefix. Default: '-'
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @param {Date|Number} opts.reference_date Date based on which
|
||||
* demurrage/interest should be applied. Can be given as JavaScript Date or int
|
||||
* for Ripple epoch.
|
||||
* @return {String} amount string
|
||||
*/
|
||||
Amount.prototype.to_human = function(opts) {
|
||||
opts = opts || {};
|
||||
@@ -804,16 +832,19 @@ Amount.prototype.to_human = function(opts) {
|
||||
return 'NaN';
|
||||
}
|
||||
|
||||
/* eslint-disable consistent-this */
|
||||
// Apply demurrage/interest
|
||||
var ref = this;
|
||||
/* eslint-enable consistent-this */
|
||||
|
||||
if (opts.reference_date) {
|
||||
ref = this.applyInterest(opts.reference_date);
|
||||
}
|
||||
|
||||
var isNegative = ref._value.isNegative();
|
||||
var valueString = ref._value.abs().toString();
|
||||
var parts = valueString.split('.');
|
||||
var int_part = parts[0];
|
||||
var isNegative = ref._value.isNegative();
|
||||
var valueString = ref._value.abs().toFixed();
|
||||
var parts = valueString.split('.');
|
||||
var int_part = parts[0];
|
||||
var fraction_part = parts.length === 2 ? parts[1] : '';
|
||||
|
||||
int_part = int_part.replace(/^0*/, '');
|
||||
@@ -847,7 +878,9 @@ Amount.prototype.to_human = function(opts) {
|
||||
|
||||
// Don't count leading zeros in the fractional part if the integer part is
|
||||
// zero.
|
||||
var sig_frac = int_is_zero ? fraction_part.replace(/^0*/, '') : fraction_part;
|
||||
var sig_frac = int_is_zero
|
||||
? fraction_part.replace(/^0*/, '')
|
||||
: fraction_part;
|
||||
digits += sig_frac.length;
|
||||
|
||||
// Now we calculate where we are compared to the maximum
|
||||
@@ -882,7 +915,7 @@ Amount.prototype.to_human = function(opts) {
|
||||
}
|
||||
|
||||
var formatted = '';
|
||||
if(isNegative && opts.signed !== false) {
|
||||
if (isNegative && opts.signed !== false) {
|
||||
formatted += '-';
|
||||
}
|
||||
|
||||
@@ -904,17 +937,19 @@ Amount.prototype.to_human_full = function(opts) {
|
||||
Amount.prototype.to_json = function() {
|
||||
if (this._is_native) {
|
||||
return this.to_text();
|
||||
} else {
|
||||
var amount_json = {
|
||||
value : this.to_text(),
|
||||
currency : this._currency.has_interest() ?
|
||||
this._currency.to_hex() : this._currency.to_json()
|
||||
};
|
||||
if (this._issuer.is_valid()) {
|
||||
amount_json.issuer = this._issuer.to_json();
|
||||
}
|
||||
return amount_json;
|
||||
}
|
||||
|
||||
var amount_json = {
|
||||
value: this.to_text(),
|
||||
currency: this._currency.has_interest() ?
|
||||
this._currency.to_hex() : this._currency.to_json()
|
||||
};
|
||||
|
||||
if (this._issuer.is_valid()) {
|
||||
amount_json.issuer = this._issuer.to_json();
|
||||
}
|
||||
|
||||
return amount_json;
|
||||
};
|
||||
|
||||
Amount.prototype.to_text_full = function(opts) {
|
||||
@@ -954,16 +989,19 @@ Amount.prototype.not_equals_why = function(d, ignore_issuer) {
|
||||
return 'Non-XRP currency differs.';
|
||||
}
|
||||
if (!ignore_issuer && !this._issuer.equals(d._issuer)) {
|
||||
return 'Non-XRP issuer differs: ' + d._issuer.to_json() + '/' + this._issuer.to_json();
|
||||
return 'Non-XRP issuer differs: '
|
||||
+ d._issuer.to_json()
|
||||
+ '/'
|
||||
+ this._issuer.to_json();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.Amount = Amount;
|
||||
exports.Amount = Amount;
|
||||
|
||||
// DEPRECATED: Include the corresponding files instead.
|
||||
exports.Currency = Currency;
|
||||
exports.Seed = Seed;
|
||||
exports.UInt160 = UInt160;
|
||||
exports.Seed = Seed;
|
||||
exports.UInt160 = UInt160;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,142 +1,71 @@
|
||||
var sjcl = require('./utils').sjcl;
|
||||
var utils = require('./utils');
|
||||
var extend = require('extend');
|
||||
'use strict';
|
||||
var _ = require('lodash');
|
||||
var sjcl = require('./utils').sjcl;
|
||||
var utils = require('./utils');
|
||||
var extend = require('extend');
|
||||
var convertBase = require('./baseconverter');
|
||||
|
||||
var Base = {};
|
||||
|
||||
var alphabets = Base.alphabets = {
|
||||
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
||||
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
|
||||
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
||||
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
|
||||
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
};
|
||||
|
||||
extend(Base, {
|
||||
VER_NONE : 1,
|
||||
VER_NODE_PUBLIC : 28,
|
||||
VER_NODE_PRIVATE : 32,
|
||||
VER_ACCOUNT_ID : 0,
|
||||
VER_ACCOUNT_PUBLIC : 35,
|
||||
VER_ACCOUNT_PRIVATE : 34,
|
||||
VER_FAMILY_GENERATOR : 41,
|
||||
VER_FAMILY_SEED : 33
|
||||
VER_NONE: 1,
|
||||
VER_NODE_PUBLIC: 28,
|
||||
VER_NODE_PRIVATE: 32,
|
||||
VER_ACCOUNT_ID: 0,
|
||||
VER_ACCOUNT_PUBLIC: 35,
|
||||
VER_ACCOUNT_PRIVATE: 34,
|
||||
VER_FAMILY_GENERATOR: 41,
|
||||
VER_FAMILY_SEED: 33
|
||||
});
|
||||
|
||||
function sha256(bytes) {
|
||||
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||
return sjcl.codec.bytes.fromBits(
|
||||
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||
}
|
||||
|
||||
function sha256hash(bytes) {
|
||||
return sha256(sha256(bytes));
|
||||
}
|
||||
function encodeString(alphabet, input) {
|
||||
if (input.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function divmod58(number, startAt) {
|
||||
var remainder = 0;
|
||||
for (var i = startAt; i < number.length; i++) {
|
||||
var digit256 = number[i] & 0xFF;
|
||||
var temp = remainder * 256 + digit256;
|
||||
number[i] = (temp / 58);
|
||||
remainder = temp % 58;
|
||||
var leadingZeros = _.takeWhile(input, function(d) {
|
||||
return d === 0;
|
||||
});
|
||||
var out = convertBase(input, 256, 58).map(function(digit) {
|
||||
if (digit < 0 || digit >= alphabet.length) {
|
||||
throw new Error('Value ' + digit + ' is out of bounds for encoding');
|
||||
}
|
||||
return remainder;
|
||||
return alphabet[digit];
|
||||
});
|
||||
var prefix = leadingZeros.map(function() {
|
||||
return alphabet[0];
|
||||
});
|
||||
return prefix.concat(out).join('');
|
||||
}
|
||||
|
||||
function divmod256(number58, startAt) {
|
||||
var remainder = 0;
|
||||
for (var i = startAt; i < number58.length; i++) {
|
||||
var digit58 = number58[i] & 0xFF;
|
||||
var temp = remainder * 58 + digit58;
|
||||
number58[i] = (temp / 256);
|
||||
remainder = temp % 256;
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
function encodeString (alphabet, input) {
|
||||
if (input.length == 0) {
|
||||
function decodeString(indexes, input) {
|
||||
if (input.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// we need to copy the buffer for calc
|
||||
scratch = input.slice();
|
||||
|
||||
// Count leading zeroes.
|
||||
var zeroCount = 0;
|
||||
while (zeroCount < scratch.length &&
|
||||
scratch[zeroCount] == 0)
|
||||
++zeroCount;
|
||||
|
||||
// The actual encoding.
|
||||
var out = new Array(scratch.length * 2);
|
||||
var j = out.length;
|
||||
var startAt = zeroCount;
|
||||
|
||||
while (startAt < scratch.length) {
|
||||
var mod = divmod58(scratch, startAt);
|
||||
if (scratch[startAt] == 0) {
|
||||
++startAt;
|
||||
var input58 = input.split('').map(function(c) {
|
||||
var charCode = c.charCodeAt(0);
|
||||
if (charCode >= indexes.length || indexes[charCode] === -1) {
|
||||
throw new Error('Character ' + c + ' is not valid for encoding');
|
||||
}
|
||||
out[--j] = alphabet[mod];
|
||||
}
|
||||
|
||||
// Strip extra 'r' if there are some after decoding.
|
||||
while (j < out.length && out[j] == alphabet[0]) ++j;
|
||||
// Add as many leading 'r' as there were leading zeros.
|
||||
while (--zeroCount >= 0) out[--j] = alphabet[0];
|
||||
while(j--) out.shift();
|
||||
|
||||
return out.join('');
|
||||
}
|
||||
|
||||
function decodeString(indexes, input) {
|
||||
var isString = typeof input === 'string';
|
||||
|
||||
if (input.length == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
input58 = new Array(input.length);
|
||||
|
||||
// Transform the String to a base58 byte sequence
|
||||
for (var i = 0; i < input.length; ++i) {
|
||||
if (isString) {
|
||||
var c = input.charCodeAt(i);
|
||||
}
|
||||
|
||||
var digit58 = -1;
|
||||
if (c >= 0 && c < 128) {
|
||||
digit58 = indexes[c];
|
||||
}
|
||||
if (digit58 < 0) {
|
||||
throw new Error("Illegal character " + c + " at " + i);
|
||||
}
|
||||
|
||||
input58[i] = digit58;
|
||||
}
|
||||
// Count leading zeroes
|
||||
var zeroCount = 0;
|
||||
while (zeroCount < input58.length && input58[zeroCount] == 0) {
|
||||
++zeroCount;
|
||||
}
|
||||
// The encoding
|
||||
out = utils.arraySet(input.length, 0);
|
||||
var j = out.length;
|
||||
|
||||
var startAt = zeroCount;
|
||||
while (startAt < input58.length) {
|
||||
var mod = divmod256(input58, startAt);
|
||||
if (input58[startAt] == 0) {
|
||||
++startAt;
|
||||
}
|
||||
out[--j] = mod;
|
||||
}
|
||||
|
||||
// Do no add extra leading zeroes, move j to first non null byte.
|
||||
while (j < out.length && (out[j] == 0)) ++j;
|
||||
|
||||
j -= zeroCount;
|
||||
while(j--) out.shift();
|
||||
|
||||
return out;
|
||||
return indexes[charCode];
|
||||
});
|
||||
var leadingZeros = _.takeWhile(input58, function(d) {
|
||||
return d === 0;
|
||||
});
|
||||
var out = convertBase(input58, 58, 256);
|
||||
return leadingZeros.concat(out);
|
||||
}
|
||||
|
||||
function Base58(alphabet) {
|
||||
@@ -151,8 +80,8 @@ function Base58(alphabet) {
|
||||
}
|
||||
|
||||
Base.encoders = {};
|
||||
Object.keys(alphabets).forEach(function(alphabet){
|
||||
Base.encoders[alphabet] = Base58(alphabets[alphabet]);
|
||||
Object.keys(alphabets).forEach(function(alphabet) {
|
||||
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
|
||||
});
|
||||
|
||||
// --> input: big-endian array of bytes.
|
||||
@@ -165,36 +94,26 @@ Base.encode = function(input, alpha) {
|
||||
// <-- array of bytes or undefined.
|
||||
Base.decode = function(input, alpha) {
|
||||
if (typeof input !== 'string') {
|
||||
return void(0);
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return this.encoders[alpha || 'ripple'].decode(input);
|
||||
}
|
||||
catch(e) {
|
||||
return (void 0);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
Base.verify_checksum = function(bytes) {
|
||||
var computed = sha256hash(bytes.slice(0, -4)).slice(0, 4);
|
||||
var computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
|
||||
var checksum = bytes.slice(-4);
|
||||
var result = true;
|
||||
|
||||
for (var i=0; i<4; i++) {
|
||||
if (computed[i] !== checksum[i]) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return _.isEqual(computed, checksum);
|
||||
};
|
||||
|
||||
// --> input: Array
|
||||
// <-- String
|
||||
Base.encode_check = function(version, input, alphabet) {
|
||||
var buffer = [].concat(version, input);
|
||||
var check = sha256(sha256(buffer)).slice(0, 4);
|
||||
var check = sha256(sha256(buffer)).slice(0, 4);
|
||||
|
||||
return Base.encode([].concat(buffer, check), alphabet);
|
||||
};
|
||||
@@ -214,16 +133,10 @@ Base.decode_check = function(version, input, alphabet) {
|
||||
}
|
||||
|
||||
// Multiple allowed versions
|
||||
if (Array.isArray(version)) {
|
||||
var match = false;
|
||||
|
||||
for (var i=0, l=version.length; i<l; i++) {
|
||||
match |= version[i] === buffer[0];
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
return NaN;
|
||||
}
|
||||
if (Array.isArray(version) && _.every(version, function(v) {
|
||||
return v !== buffer[0];
|
||||
})) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
if (!Base.verify_checksum(buffer)) {
|
||||
@@ -234,7 +147,7 @@ Base.decode_check = function(version, input, alphabet) {
|
||||
// intrepret the value as a negative number
|
||||
buffer[0] = 0;
|
||||
|
||||
return sjcl.bn.fromBits (
|
||||
return sjcl.bn.fromBits(
|
||||
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
|
||||
};
|
||||
|
||||
|
||||
32
src/js/ripple/baseconverter.js
Normal file
32
src/js/ripple/baseconverter.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
function normalize(digitArray) {
|
||||
while (digitArray[0] === 0) {
|
||||
digitArray.shift();
|
||||
}
|
||||
return digitArray;
|
||||
}
|
||||
|
||||
function divmod(digitArray, base, divisor) {
|
||||
var remainder = 0;
|
||||
var quotient = [];
|
||||
for (var j = 0; j < digitArray.length; j++) {
|
||||
var temp = remainder * base + parseInt(digitArray[j], 10);
|
||||
quotient.push(Math.floor(temp / divisor));
|
||||
remainder = temp % divisor;
|
||||
}
|
||||
return {quotient: normalize(quotient), remainder: remainder};
|
||||
}
|
||||
|
||||
function convertBase(digitArray, fromBase, toBase) {
|
||||
var result = [];
|
||||
var dividend = digitArray;
|
||||
while (dividend.length > 0) {
|
||||
var qr = divmod(dividend, fromBase, toBase);
|
||||
result.unshift(qr.remainder);
|
||||
dividend = qr.quotient;
|
||||
}
|
||||
return normalize(result);
|
||||
}
|
||||
|
||||
module.exports = convertBase;
|
||||
@@ -1,13 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
|
||||
|
||||
/**
|
||||
* Data type map.
|
||||
*
|
||||
* Mapping of type ids to data types. The type id is specified by the high
|
||||
*
|
||||
* For reference, see rippled's definition:
|
||||
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol/SField.cpp
|
||||
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol
|
||||
* /SField.cpp
|
||||
*/
|
||||
var TYPES_MAP = exports.types = [
|
||||
void(0),
|
||||
|
||||
exports.types = [
|
||||
undefined,
|
||||
|
||||
// Common
|
||||
'Int16', // 1
|
||||
@@ -20,11 +26,11 @@ var TYPES_MAP = exports.types = [
|
||||
'Account', // 8
|
||||
|
||||
// 9-13 reserved
|
||||
void(0), // 9
|
||||
void(0), // 10
|
||||
void(0), // 11
|
||||
void(0), // 12
|
||||
void(0), // 13
|
||||
undefined, // 9
|
||||
undefined, // 10
|
||||
undefined, // 11
|
||||
undefined, // 12
|
||||
undefined, // 13
|
||||
|
||||
'Object', // 14
|
||||
'Array', // 15
|
||||
@@ -151,7 +157,7 @@ var FIELDS_MAP = exports.fields = {
|
||||
8: 'RegularKey'
|
||||
},
|
||||
14: { // Object
|
||||
1: void(0), //end of Object
|
||||
1: undefined, // end of Object
|
||||
2: 'TransactionMetaData',
|
||||
3: 'CreatedNode',
|
||||
4: 'DeletedNode',
|
||||
@@ -163,7 +169,7 @@ var FIELDS_MAP = exports.fields = {
|
||||
10: 'Memo'
|
||||
},
|
||||
15: { // Array
|
||||
1: void(0), //end of Array
|
||||
1: undefined, // end of Array
|
||||
2: 'SigningAccounts',
|
||||
3: 'TxnSignatures',
|
||||
4: 'Signatures',
|
||||
@@ -204,7 +210,6 @@ Object.keys(FIELDS_MAP).forEach(function(k1) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var REQUIRED = exports.REQUIRED = 0,
|
||||
OPTIONAL = exports.OPTIONAL = 1,
|
||||
DEFAULT = exports.DEFAULT = 2;
|
||||
@@ -229,7 +234,9 @@ exports.tx = {
|
||||
[ 'WalletSize' , OPTIONAL ],
|
||||
[ 'MessageKey' , OPTIONAL ],
|
||||
[ 'Domain' , OPTIONAL ],
|
||||
[ 'TransferRate' , OPTIONAL ]
|
||||
[ 'TransferRate' , OPTIONAL ],
|
||||
[ 'SetFlag' , OPTIONAL ],
|
||||
[ 'ClearFlag' , OPTIONAL ]
|
||||
]),
|
||||
TrustSet: [20].concat(base, [
|
||||
[ 'LimitAmount' , OPTIONAL ],
|
||||
@@ -239,13 +246,14 @@ exports.tx = {
|
||||
OfferCreate: [7].concat(base, [
|
||||
[ 'TakerPays' , REQUIRED ],
|
||||
[ 'TakerGets' , REQUIRED ],
|
||||
[ 'Expiration' , OPTIONAL ]
|
||||
[ 'Expiration' , OPTIONAL ],
|
||||
[ 'OfferSequence' , OPTIONAL ]
|
||||
]),
|
||||
OfferCancel: [8].concat(base, [
|
||||
[ 'OfferSequence' , REQUIRED ]
|
||||
]),
|
||||
SetRegularKey: [5].concat(base, [
|
||||
[ 'RegularKey' , REQUIRED ]
|
||||
[ 'RegularKey' , OPTIONAL ]
|
||||
]),
|
||||
Payment: [0].concat(base, [
|
||||
[ 'Destination' , REQUIRED ],
|
||||
@@ -271,11 +279,21 @@ exports.tx = {
|
||||
EnableFeature: [100].concat(base, [
|
||||
[ 'Feature' , REQUIRED ]
|
||||
]),
|
||||
EnableAmendment: [100].concat(base, [
|
||||
[ 'Amendment' , REQUIRED ]
|
||||
]),
|
||||
SetFee: [101].concat(base, [
|
||||
[ 'BaseFee' , REQUIRED ],
|
||||
[ 'ReferenceFeeUnits' , REQUIRED ],
|
||||
[ 'ReserveBase' , REQUIRED ],
|
||||
[ 'ReserveIncrement' , REQUIRED ]
|
||||
]),
|
||||
TicketCreate: [10].concat(base, [
|
||||
[ 'Target' , OPTIONAL ],
|
||||
[ 'Expiration' , OPTIONAL ]
|
||||
]),
|
||||
TicketCancel: [11].concat(base, [
|
||||
[ 'TicketID' , REQUIRED ]
|
||||
])
|
||||
};
|
||||
|
||||
@@ -413,5 +431,8 @@ exports.ter = {
|
||||
tecNO_TARGET : 138,
|
||||
tecNO_PERMISSION : 139,
|
||||
tecNO_ENTRY : 140,
|
||||
tecINSUFFICIENT_RESERVE : 141
|
||||
tecINSUFFICIENT_RESERVE : 141,
|
||||
tecNEED_MASTER_KEY : 142,
|
||||
tecDST_TAG_NEEDED : 143,
|
||||
tecINTERNAL : 144
|
||||
};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
var extend = require('extend');
|
||||
'use strict';
|
||||
|
||||
var extend = require('extend');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var utils = require('./utils');
|
||||
var Float = require('./ieee754').Float;
|
||||
@@ -16,8 +18,7 @@ var Currency = extend(function() {
|
||||
// 3-letter code: ...
|
||||
// XXX Should support hex, C++ doesn't currently allow it.
|
||||
|
||||
this._value = NaN;
|
||||
|
||||
this._value = NaN;
|
||||
this._update();
|
||||
}, UInt160);
|
||||
|
||||
@@ -32,25 +33,37 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
|
||||
* Examples:
|
||||
*
|
||||
* USD => currency
|
||||
* USD - Dollar => currency with optional full currency name
|
||||
* XAU (-0.5%pa) => XAU with 0.5% effective demurrage rate per year
|
||||
* USD - Dollar => currency with optional full currency
|
||||
* name
|
||||
* XAU (-0.5%pa) => XAU with 0.5% effective demurrage rate
|
||||
* per year
|
||||
* XAU - Gold (-0.5%pa) => Optionally allowed full currency name
|
||||
* USD (1%pa) => US dollars with 1% effective interest per year
|
||||
* USD (1%pa) => US dollars with 1% effective interest
|
||||
* per year
|
||||
* INR - Indian Rupees => Optional full currency name with spaces
|
||||
* TYX - 30-Year Treasuries => Optional full currency with numbers and a dash
|
||||
* TYX - 30-Year Treasuries (1.5%pa) => Optional full currency with numbers, dash and interest rate
|
||||
* TYX - 30-Year Treasuries => Optional full currency with numbers
|
||||
* and a dash
|
||||
* TYX - 30-Year Treasuries (1.5%pa) => Optional full currency with numbers,
|
||||
* dash and interest rate
|
||||
*
|
||||
* The regular expression below matches above cases, broken down for better understanding:
|
||||
* The regular expression below matches above cases, broken down for better
|
||||
* understanding:
|
||||
*
|
||||
* ^\s* // start with any amount of whitespace
|
||||
* ([a-zA-Z]{3}|[0-9]{3}) // either 3 letter alphabetic currency-code or 3 digit numeric currency-code. See ISO 4217
|
||||
* (\s*-\s*[- \w]+) // optional full currency name following the dash after currency code,
|
||||
* full currency code can contain letters, numbers and dashes
|
||||
* (\s*\(-?\d+\.?\d*%pa\))? // optional demurrage rate, has optional - and . notation (-0.5%pa)
|
||||
* ([a-zA-Z]{3}|[0-9]{3}) // either 3 letter alphabetic currency-code or 3
|
||||
* digit numeric currency-code. See ISO 4217
|
||||
* (\s*-\s*[- \w]+) // optional full currency name following the dash
|
||||
* after currency code, full currency code can
|
||||
* contain letters, numbers and dashes
|
||||
* (\s*\(-?\d+\.?\d*%pa\))? // optional demurrage rate, has optional - and
|
||||
* . notation (-0.5%pa)
|
||||
* \s*$ // end with any amount of whitespace
|
||||
*
|
||||
*/
|
||||
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
|
||||
|
||||
/*eslint-disable max-len*/
|
||||
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
|
||||
/*eslint-enable max-len*/
|
||||
|
||||
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
|
||||
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
|
||||
@@ -58,39 +71,65 @@ Currency.from_json = function(j, shouldInterpretXrpAsIou) {
|
||||
|
||||
Currency.from_human = function(j, opts) {
|
||||
return (new Currency().parse_human(j, opts));
|
||||
}
|
||||
};
|
||||
|
||||
// this._value = NaN on error.
|
||||
Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
this._value = NaN;
|
||||
|
||||
switch (typeof j) {
|
||||
case 'string':
|
||||
if (j instanceof Currency) {
|
||||
this._value = j.copyTo({})._value;
|
||||
this._update();
|
||||
return this;
|
||||
}
|
||||
|
||||
// if an empty string is given, fall back to XRP
|
||||
switch (typeof j) {
|
||||
case 'number':
|
||||
if (!isNaN(j)) {
|
||||
this.parse_number(j);
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
if (!j || j === '0') {
|
||||
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
|
||||
// Empty string or XRP
|
||||
this.parse_hex(shouldInterpretXrpAsIou
|
||||
? Currency.HEX_CURRENCY_BAD
|
||||
: Currency.HEX_ZERO);
|
||||
break;
|
||||
}
|
||||
|
||||
if (j === '1') {
|
||||
// 'no currency'
|
||||
this.parse_hex(Currency.HEX_ONE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (/^[A-F0-9]{40}$/.test(j)) {
|
||||
// Hex format
|
||||
this.parse_hex(j);
|
||||
break;
|
||||
}
|
||||
|
||||
// match the given string to see if it's in an allowed format
|
||||
var matches = String(j).match(this.human_RE);
|
||||
var matches = j.match(this.human_RE);
|
||||
|
||||
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);
|
||||
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
|
||||
// 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] || '';
|
||||
|
||||
@@ -117,25 +156,28 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
currencyData[2] = currencyCode.charCodeAt(1) & 0xff;
|
||||
currencyData[3] = currencyCode.charCodeAt(2) & 0xff;
|
||||
|
||||
// byte 5-8 are for reference date, but should always be 0 so we won't fill it
|
||||
// byte 5-8 are for reference date, but should always be 0 so we
|
||||
// won't fill it
|
||||
|
||||
// byte 9-16 are for the interest
|
||||
percentage = parseFloat(percentage[0]);
|
||||
|
||||
// the interest or demurrage is expressed as a yearly (per annum) value
|
||||
// the interest or demurrage is expressed as a yearly (per annum)
|
||||
// value
|
||||
var secondsPerYear = 31536000; // 60 * 60 * 24 * 365
|
||||
|
||||
// Calculating the interest e-fold
|
||||
// 0.5% demurrage is expressed 0.995, 0.005 less than 1
|
||||
// 0.5% interest is expressed as 1.005, 0.005 more than 1
|
||||
var interestEfold = secondsPerYear / Math.log(1 + percentage/100);
|
||||
var interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
|
||||
var bytes = Float.toIEEE754Double(interestEfold);
|
||||
|
||||
for (var i=0; i<=bytes.length; i++) {
|
||||
for (var i = 0; i <= bytes.length; i++) {
|
||||
currencyData[8 + i] = bytes[i] & 0xff;
|
||||
}
|
||||
|
||||
// the last 4 bytes are reserved for future use, so we won't fill those
|
||||
// the last 4 bytes are reserved for future use, so we won't fill
|
||||
// those
|
||||
|
||||
} else {
|
||||
currencyData[12] = currencyCode.charCodeAt(0) & 0xff;
|
||||
@@ -144,21 +186,6 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
}
|
||||
|
||||
this.parse_bytes(currencyData);
|
||||
} else {
|
||||
this.parse_hex(j);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
if (!isNaN(j)) {
|
||||
this.parse_number(j);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
if (j instanceof Currency) {
|
||||
this._value = j.copyTo({})._value;
|
||||
this._update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -166,7 +193,6 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
Currency.prototype.parse_human = function(j) {
|
||||
return this.parse_json(j);
|
||||
};
|
||||
@@ -176,6 +202,7 @@ Currency.prototype.parse_human = function(j) {
|
||||
*
|
||||
* You should never need to call this.
|
||||
*/
|
||||
|
||||
Currency.prototype._update = function() {
|
||||
var bytes = this.to_bytes();
|
||||
|
||||
@@ -183,7 +210,7 @@ Currency.prototype._update = function() {
|
||||
var isZeroExceptInStandardPositions = true;
|
||||
|
||||
if (!bytes) {
|
||||
return 'XRP';
|
||||
return;
|
||||
}
|
||||
|
||||
this._native = false;
|
||||
@@ -192,8 +219,9 @@ Currency.prototype._update = function() {
|
||||
this._interest_period = NaN;
|
||||
this._iso_code = '';
|
||||
|
||||
for (var i=0; i<20; i++) {
|
||||
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions && (i===12 || i===13 || i===14 || bytes[i]===0);
|
||||
for (var i = 0; i < 20; i++) {
|
||||
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
|
||||
&& (i === 12 || i === 13 || i === 14 || bytes[i] === 0);
|
||||
}
|
||||
|
||||
if (isZeroExceptInStandardPositions) {
|
||||
@@ -201,7 +229,7 @@ Currency.prototype._update = function() {
|
||||
+ String.fromCharCode(bytes[13])
|
||||
+ String.fromCharCode(bytes[14]);
|
||||
|
||||
if (this._iso_code === '\0\0\0') {
|
||||
if (this._iso_code === '\u0000\u0000\u0000') {
|
||||
this._native = true;
|
||||
this._iso_code = 'XRP';
|
||||
}
|
||||
@@ -215,8 +243,8 @@ Currency.prototype._update = function() {
|
||||
this._type = 1;
|
||||
this._interest_start = (bytes[4] << 24) +
|
||||
(bytes[5] << 16) +
|
||||
(bytes[6] << 8) +
|
||||
(bytes[7] );
|
||||
(bytes[6] << 8) +
|
||||
(bytes[7]);
|
||||
this._interest_period = Float.fromIEEE754Double(bytes.slice(8, 16));
|
||||
}
|
||||
};
|
||||
@@ -230,7 +258,8 @@ Currency.prototype.parse_bytes = function(byte_array) {
|
||||
var isZeroExceptInStandardPositions = true;
|
||||
|
||||
for (var i=0; i<20; i++) {
|
||||
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions && (i===12 || i===13 || i===14 || byte_array[0]===0)
|
||||
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
|
||||
&& (i===12 || i===13 || i===14 || byte_array[0]===0)
|
||||
}
|
||||
|
||||
if (isZeroExceptInStandardPositions) {
|
||||
@@ -260,20 +289,25 @@ Currency.prototype.is_native = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this currency is an interest-bearing/demurring currency.
|
||||
* @return {Boolean} whether this currency is an interest-bearing currency
|
||||
*/
|
||||
|
||||
Currency.prototype.has_interest = function() {
|
||||
return this._type === 1 && !isNaN(this._interest_start) && !isNaN(this._interest_period);
|
||||
return this._type === 1
|
||||
&& !isNaN(this._interest_start)
|
||||
&& !isNaN(this._interest_period);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param referenceDate - number of seconds since the Ripple Epoch (0:00 on January 1, 2000 UTC)
|
||||
* used to calculate the interest over provided interval
|
||||
* pass in one years worth of seconds to ge the yearly interest
|
||||
* @returns {number} - interest for provided interval, can be negative for demurred currencies
|
||||
* @param {number} referenceDate number of seconds since the Ripple Epoch
|
||||
* (0:00 on January 1, 2000 UTC) used to calculate the
|
||||
* interest over provided interval pass in one years
|
||||
* worth of seconds to ge the yearly interest
|
||||
* @returns {number} interest for provided interval, can be negative for
|
||||
* demurred currencies
|
||||
*/
|
||||
Currency.prototype.get_interest_at = function(referenceDate, decimals) {
|
||||
Currency.prototype.get_interest_at = function(referenceDate) {
|
||||
if (!this.has_interest()) {
|
||||
return 0;
|
||||
}
|
||||
@@ -288,18 +322,20 @@ Currency.prototype.get_interest_at = function(referenceDate, decimals) {
|
||||
}
|
||||
|
||||
// calculate interest by e-fold number
|
||||
return Math.exp((referenceDate - this._interest_start) / this._interest_period);
|
||||
return Math.exp((referenceDate - this._interest_start)
|
||||
/ this._interest_period);
|
||||
};
|
||||
|
||||
Currency.prototype.get_interest_percentage_at = function(referenceDate, decimals) {
|
||||
Currency.prototype.get_interest_percentage_at
|
||||
= function(referenceDate, decimals) {
|
||||
var interest = this.get_interest_at(referenceDate, decimals);
|
||||
|
||||
// convert to percentage
|
||||
var interest = (interest*100)-100;
|
||||
var decimalMultiplier = decimals ? Math.pow(10,decimals) : 100;
|
||||
interest = (interest * 100) - 100;
|
||||
var decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
|
||||
|
||||
// round to two decimals behind the dot
|
||||
return Math.round(interest*decimalMultiplier) / decimalMultiplier;
|
||||
return Math.round(interest * decimalMultiplier) / decimalMultiplier;
|
||||
};
|
||||
|
||||
// XXX Currently we inherit UInt.prototype.is_valid, which is mostly fine.
|
||||
@@ -307,9 +343,9 @@ Currency.prototype.get_interest_percentage_at = function(referenceDate, decimals
|
||||
// We could be doing further checks into the internal format of the
|
||||
// currency data, since there are some values that are invalid.
|
||||
//
|
||||
//Currency.prototype.is_valid = function() {
|
||||
// Currency.prototype.is_valid = function() {
|
||||
// return UInt.prototype.is_valid() && ...;
|
||||
//};
|
||||
// };
|
||||
|
||||
Currency.prototype.to_json = function(opts) {
|
||||
if (!this.is_valid()) {
|
||||
@@ -317,28 +353,35 @@ Currency.prototype.to_json = function(opts) {
|
||||
return 'XRP';
|
||||
}
|
||||
|
||||
var opts = opts || {};
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var currency;
|
||||
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
|
||||
opts.show_interest = opts.show_interest !== void(0) ? opts.show_interest : this.has_interest();
|
||||
opts.show_interest = opts.show_interest !== undefined
|
||||
? opts.show_interest
|
||||
: this.has_interest();
|
||||
|
||||
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
|
||||
currency = this._iso_code + fullName;
|
||||
if (opts.show_interest) {
|
||||
var decimals = !isNaN(opts.decimals) ? opts.decimals : void(0);
|
||||
var interestPercentage = this.has_interest() ? this.get_interest_percentage_at(this._interest_start + 3600 * 24 * 365, decimals) : 0;
|
||||
var decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
|
||||
var interestPercentage = this.has_interest()
|
||||
? this.get_interest_percentage_at(
|
||||
this._interest_start + 3600 * 24 * 365, decimals
|
||||
)
|
||||
: 0;
|
||||
currency += ' (' + interestPercentage + '%pa)';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Fallback to returning the raw currency hex
|
||||
currency = this.to_hex();
|
||||
|
||||
// XXX This is to maintain backwards compatibility, but it is very, very odd
|
||||
// behavior, so we should deprecate it and get rid of it as soon as
|
||||
// possible.
|
||||
// XXX This is to maintain backwards compatibility, but it is very, very
|
||||
// odd behavior, so we should deprecate it and get rid of it as soon as
|
||||
// possible.
|
||||
if (currency === Currency.HEX_ONE) {
|
||||
currency = 1;
|
||||
}
|
||||
@@ -357,5 +400,3 @@ Currency.prototype.get_iso = function() {
|
||||
};
|
||||
|
||||
exports.Currency = Currency;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
exports.Remote = require('./remote').Remote;
|
||||
exports.Request = require('./request').Request;
|
||||
exports.Amount = require('./amount').Amount;
|
||||
exports.Account = require('./account').Account;
|
||||
exports.Transaction = require('./transaction').Transaction;
|
||||
exports.Currency = require('./currency').Currency;
|
||||
exports.Base = require('./base').Base;
|
||||
exports.UInt128 = require('./uint128').UInt128;
|
||||
exports.UInt160 = require('./uint160').UInt160;
|
||||
exports.UInt256 = require('./uint256').UInt256;
|
||||
exports.Seed = require('./seed').Seed;
|
||||
exports.Meta = require('./meta').Meta;
|
||||
'use strict';
|
||||
exports.Remote = require('./remote').Remote;
|
||||
exports.Request = require('./request').Request;
|
||||
exports.Amount = require('./amount').Amount;
|
||||
exports.Account = require('./account').Account;
|
||||
exports.Transaction = require('./transaction').Transaction;
|
||||
exports.Currency = require('./currency').Currency;
|
||||
exports.Base = require('./base').Base;
|
||||
exports.UInt128 = require('./uint128').UInt128;
|
||||
exports.UInt160 = require('./uint160').UInt160;
|
||||
exports.UInt256 = require('./uint256').UInt256;
|
||||
exports.Seed = require('./seed').Seed;
|
||||
exports.Meta = require('./meta').Meta;
|
||||
exports.SerializedObject = require('./serializedobject').SerializedObject;
|
||||
exports.RippleError = require('./rippleerror').RippleError;
|
||||
exports.Message = require('./message').Message;
|
||||
exports.binformat = require('./binformat');
|
||||
exports.utils = require('./utils');
|
||||
exports.Server = require('./server').Server;
|
||||
exports.Wallet = require('./wallet');
|
||||
exports.Ledger = require('./ledger').Ledger;
|
||||
exports.RippleError = require('./rippleerror').RippleError;
|
||||
exports.Message = require('./message').Message;
|
||||
exports.binformat = require('./binformat');
|
||||
exports.utils = require('./utils');
|
||||
exports.Server = require('./server').Server;
|
||||
exports.Wallet = require('./wallet');
|
||||
exports.Ledger = require('./ledger').Ledger;
|
||||
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
|
||||
exports.RangeSet = require('./rangeset').RangeSet;
|
||||
exports.RangeSet = require('./rangeset').RangeSet;
|
||||
exports.convertBase = require('./baseconverter');
|
||||
|
||||
// Important: We do not guarantee any specific version of SJCL or for any
|
||||
// specific features to be included. The version and configuration may change at
|
||||
@@ -28,35 +30,35 @@ exports.RangeSet = require('./rangeset').RangeSet;
|
||||
// However, for programs that are tied to a specific version of ripple.js like
|
||||
// the official client, it makes sense to expose the SJCL instance so we don't
|
||||
// have to include it twice.
|
||||
exports.sjcl = require('./utils').sjcl;
|
||||
exports.types = require('./serializedtypes');
|
||||
exports.sjcl = require('./utils').sjcl;
|
||||
exports.types = require('./serializedtypes');
|
||||
|
||||
exports.config = require('./config');
|
||||
|
||||
// camelCase to under_scored API conversion
|
||||
function attachUnderscored(c) {
|
||||
var o = exports[c];
|
||||
function attachUnderscored(name) {
|
||||
var o = exports[name];
|
||||
|
||||
Object.keys(o.prototype).forEach(function(key) {
|
||||
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
|
||||
Object.keys(o.prototype).forEach(function(key) {
|
||||
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
|
||||
|
||||
if (!UPPERCASE.test(key)) {
|
||||
return;
|
||||
}
|
||||
if (!UPPERCASE.test(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var underscored = key.replace(UPPERCASE, function(c) {
|
||||
return '_' + c.toLowerCase();
|
||||
});
|
||||
var underscored = key.replace(UPPERCASE, function(c) {
|
||||
return '_' + c.toLowerCase();
|
||||
});
|
||||
|
||||
o.prototype[underscored] = o.prototype[key];
|
||||
});
|
||||
};
|
||||
o.prototype[underscored] = o.prototype[key];
|
||||
});
|
||||
}
|
||||
|
||||
[ 'Remote',
|
||||
'Request',
|
||||
'Transaction',
|
||||
'Account',
|
||||
'Server'
|
||||
['Remote',
|
||||
'Request',
|
||||
'Transaction',
|
||||
'Account',
|
||||
'Server'
|
||||
].forEach(attachUnderscored);
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Logging functionality for ripple-lib and any applications built on it.
|
||||
*
|
||||
* @param {String} namespace logging prefix
|
||||
* @return {Void} this function does not return...
|
||||
*/
|
||||
function Log(namespace) {
|
||||
if (!namespace) {
|
||||
@@ -7,11 +12,11 @@ function Log(namespace) {
|
||||
} else if (Array.isArray(namespace)) {
|
||||
this._namespace = namespace;
|
||||
} else {
|
||||
this._namespace = [''+namespace];
|
||||
this._namespace = [String(namespace)];
|
||||
}
|
||||
|
||||
this._prefix = this._namespace.concat(['']).join(': ');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sub-logger.
|
||||
@@ -24,6 +29,9 @@ function Log(namespace) {
|
||||
*
|
||||
* log.info('connection successful');
|
||||
* // prints: 'server: connection successful'
|
||||
*
|
||||
* @param {String} namespace logging prefix
|
||||
* @return {Log} sub logger
|
||||
*/
|
||||
Log.prototype.sub = function(namespace) {
|
||||
var subNamespace = this._namespace.slice();
|
||||
@@ -43,17 +51,57 @@ Log.prototype._setParent = function(parentLogger) {
|
||||
|
||||
Log.makeLevel = function(level) {
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
args[0] = this._prefix + args[0];
|
||||
Log.engine.logObject.apply(Log, args);
|
||||
Log.engine.logObject.apply(Log, [level].concat(args[0], [args.slice(2)]));
|
||||
};
|
||||
};
|
||||
|
||||
Log.prototype.debug = Log.makeLevel(1);
|
||||
Log.prototype.info = Log.makeLevel(2);
|
||||
Log.prototype.warn = Log.makeLevel(3);
|
||||
Log.prototype.info = Log.makeLevel(2);
|
||||
Log.prototype.warn = Log.makeLevel(3);
|
||||
Log.prototype.error = Log.makeLevel(4);
|
||||
|
||||
/**
|
||||
* @param {String} message
|
||||
* @param {Array} details
|
||||
* @return {Array} prepared log info
|
||||
*/
|
||||
|
||||
function getLogInfo(message, args) {
|
||||
var stack = new Error().stack;
|
||||
|
||||
return [
|
||||
// Timestamp
|
||||
'[' + new Date().toISOString() + ']',
|
||||
message,
|
||||
'--',
|
||||
// Location
|
||||
(typeof stack === 'string') ? stack.split('\n')[4].replace(/^\s+/, '') : '',
|
||||
'\n'
|
||||
].concat(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} log level
|
||||
* @param {Array} log info
|
||||
*/
|
||||
|
||||
function logMessage(logLevel, args) {
|
||||
switch (logLevel) {
|
||||
case 1:
|
||||
case 2:
|
||||
console.log.apply(console, args);
|
||||
break;
|
||||
case 3:
|
||||
console.warn.apply(console, args);
|
||||
break;
|
||||
case 4:
|
||||
console.error.apply(console, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic logging connector.
|
||||
*
|
||||
@@ -61,20 +109,33 @@ Log.prototype.error = Log.makeLevel(4);
|
||||
* implementations. This is the logging engine used in Node.js.
|
||||
*/
|
||||
var BasicLogEngine = {
|
||||
logObject: function logObject(msg) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
logObject: function logObject(level, message, args) {
|
||||
args = args.map(function(arg) {
|
||||
return JSON.stringify(arg, null, 2);
|
||||
});
|
||||
|
||||
args.unshift(msg);
|
||||
args.unshift('[' + new Date().toISOString() + ']');
|
||||
|
||||
console.log.apply(console, args);
|
||||
logMessage(level, getLogInfo(message, args));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Log engine for browser consoles.
|
||||
*
|
||||
* Browsers tend to have better consoles that support nicely formatted
|
||||
* JavaScript objects. This connector passes objects through to the logging
|
||||
* function without any stringification.
|
||||
*/
|
||||
var InteractiveLogEngine = {
|
||||
logObject: function(level, message, args) {
|
||||
args = args.map(function(arg) {
|
||||
return /MSIE/.test(navigator.userAgent)
|
||||
? JSON.stringify(arg, null, 2)
|
||||
: arg;
|
||||
});
|
||||
|
||||
logMessage(level, getLogInfo(message, args));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Null logging connector.
|
||||
*
|
||||
@@ -85,10 +146,12 @@ var NullLogEngine = {
|
||||
logObject: function() {}
|
||||
};
|
||||
|
||||
Log.engine = NullLogEngine;
|
||||
|
||||
if (console && console.log) {
|
||||
if (typeof window !== 'undefined' && typeof console !== 'undefined') {
|
||||
Log.engine = InteractiveLogEngine;
|
||||
} else if (typeof console !== 'undefined' && console.log) {
|
||||
Log.engine = BasicLogEngine;
|
||||
} else {
|
||||
Log.engine = NullLogEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
var exports = module.exports = require('./log.js');
|
||||
|
||||
/**
|
||||
* Log engine for browser consoles.
|
||||
*
|
||||
* Browsers tend to have better consoles that support nicely formatted
|
||||
* JavaScript objects. This connector passes objects through to the logging
|
||||
* function without any stringification.
|
||||
*/
|
||||
var InteractiveLogEngine = {
|
||||
logObject: function (msg, obj) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
args = args.map(function(arg) {
|
||||
if (/MSIE/.test(navigator.userAgent)) {
|
||||
return JSON.stringify(arg, null, 2);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
});
|
||||
|
||||
args.unshift(msg);
|
||||
args.unshift('[' + new Date().toISOString() + ']');
|
||||
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
};
|
||||
|
||||
if (window.console && window.console.log) {
|
||||
exports.Log.engine = InteractiveLogEngine;
|
||||
}
|
||||
@@ -8,16 +8,22 @@
|
||||
// - trade
|
||||
// - transaction
|
||||
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
var extend = require('extend');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
var extend = require('extend');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
var log = require('./log').internal.sub('orderbook');
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
var log = require('./log').internal.sub('orderbook');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
assert(!_.isNull(number) && !isNaN(number), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor OrderBook
|
||||
@@ -73,11 +79,11 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function updateFundedAmountsWrapper (transaction) {
|
||||
self.updateFundedAmounts(transaction);
|
||||
};
|
||||
}
|
||||
|
||||
this.on('newListener', function(event) {
|
||||
listenersModified('add', event);
|
||||
@@ -107,7 +113,7 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(OrderBook, EventEmitter);
|
||||
|
||||
@@ -136,12 +142,11 @@ OrderBook.offerRewrite = function(offer) {
|
||||
var result = {};
|
||||
var keys = Object.keys(offer);
|
||||
|
||||
for (var i=0, l=keys.length; i<l; i++) {
|
||||
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];
|
||||
@@ -182,9 +187,7 @@ OrderBook.prototype.subscribe = function() {
|
||||
}
|
||||
];
|
||||
|
||||
async.series(steps, function(err, res) {
|
||||
//XXX What now?
|
||||
});
|
||||
async.series(steps);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -221,7 +224,7 @@ OrderBook.prototype.requestOffers = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function(){};
|
||||
callback = function() {};
|
||||
}
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
@@ -247,7 +250,7 @@ OrderBook.prototype.requestOffers = function(callback) {
|
||||
self.emit('model', self._offers);
|
||||
|
||||
callback(null, self._offers);
|
||||
};
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
// XXX What now?
|
||||
@@ -256,7 +259,7 @@ OrderBook.prototype.requestOffers = function(callback) {
|
||||
}
|
||||
|
||||
callback(err);
|
||||
};
|
||||
}
|
||||
|
||||
var request = this._remote.requestBookOffers(this.toJSON());
|
||||
request.once('success', handleOffers);
|
||||
@@ -276,7 +279,7 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function(){};
|
||||
callback = function() {};
|
||||
}
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
@@ -295,7 +298,7 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
self._subscribed = true;
|
||||
|
||||
callback(null, res);
|
||||
};
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
if (self._remote.trace) {
|
||||
@@ -303,7 +306,7 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
}
|
||||
|
||||
callback(err);
|
||||
};
|
||||
}
|
||||
|
||||
var request = this._remote.requestSubscribe();
|
||||
request.addStream('transactions');
|
||||
@@ -333,7 +336,7 @@ OrderBook.prototype.resetCache = function() {
|
||||
|
||||
OrderBook.prototype.hasOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
return this._ownerFunds[account] !== void(0);
|
||||
return this._ownerFunds[account] !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -355,12 +358,25 @@ OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
|
||||
* Get owner's cached, transfer rate adjusted, funds
|
||||
*
|
||||
* @param {String} account - owner's account address
|
||||
* @return {String}
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
return this._ownerFunds[account];
|
||||
|
||||
var amount;
|
||||
|
||||
if (this.hasOwnerFunds(account)) {
|
||||
if (this._currencyGets.is_native()) {
|
||||
amount = Amount.from_json(this._ownerFunds[account]);
|
||||
} else {
|
||||
amount = Amount.from_json(
|
||||
this._ownerFunds[account] + OrderBook.IOU_SUFFIX
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -383,7 +399,7 @@ OrderBook.prototype.getUnadjustedOwnerFunds = function(account) {
|
||||
|
||||
OrderBook.prototype.deleteOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
this._ownerFunds[account] = void(0);
|
||||
this._ownerFunds[account] = undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -436,23 +452,27 @@ OrderBook.prototype.decrementOwnerOfferCount = function(account) {
|
||||
* Add amount sum being offered for owner
|
||||
*
|
||||
* @param {String} account - owner's account address
|
||||
* @param {Object|String} amount - offer amount as native string or IOU currency format
|
||||
* @param {Object|String} amount - offer amount as native string or IOU
|
||||
* currency format
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
var previousAmount = this.getOwnerOfferTotal(account);
|
||||
var newAmount = previousAmount.add(Amount.from_json(amount));
|
||||
this._ownerOffersTotal[account] = newAmount;
|
||||
return newAmount;
|
||||
var currentAmount = previousAmount.add(Amount.from_json(amount));
|
||||
|
||||
this._ownerOffersTotal[account] = currentAmount;
|
||||
|
||||
return currentAmount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subtract amount sum being offered for owner
|
||||
*
|
||||
* @param {String} account - owner's account address
|
||||
* @param {Object|String} amount - offer amount as native string or IOU currency format
|
||||
* @param {Object|String} amount - offer amount as native string or IOU
|
||||
* currency format
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
@@ -478,7 +498,7 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
var amount = this._ownerOffersTotal[account];
|
||||
|
||||
|
||||
if (!amount) {
|
||||
if (this._currencyGets.is_native()) {
|
||||
amount = Amount.from_json('0');
|
||||
@@ -496,6 +516,7 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
* @param {String} account - owner's account address
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
var amount;
|
||||
|
||||
@@ -511,15 +532,29 @@ OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute adjusted balance that would be left after issuer's transfer fee is deducted
|
||||
* Casts and returns offer's taker gets funded amount as a default IOU amount
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOfferTakerGetsFunded = function(offer) {
|
||||
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
|
||||
|
||||
return Amount.from_json(offer.taker_gets_funded + OrderBook.IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute adjusted balance that would be left after issuer's transfer fee is
|
||||
* deducted
|
||||
*
|
||||
* @param {String} balance
|
||||
* @return {Amount}
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.applyTransferRate = function(balance) {
|
||||
assert(!isNaN(balance), 'Balance is invalid');
|
||||
assert(!_.isNull(this._issuerTransferRate) && !isNaN(this._issuerTransferRate), 'Transfer rate is invalid');
|
||||
assertValidNumber(this._issuerTransferRate, 'Transfer rate is invalid');
|
||||
|
||||
var adjustedBalance = Amount.from_json(balance + OrderBook.IOU_SUFFIX)
|
||||
.divide(this._issuerTransferRate)
|
||||
@@ -553,40 +588,42 @@ OrderBook.prototype.requestTransferRate = function(callback) {
|
||||
return callback(null, this._issuerTransferRate);
|
||||
}
|
||||
|
||||
this._remote.requestAccountInfo({ account: this._issuerGets }, function(err, info) {
|
||||
function handleAccountInfo(err, info) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// When transfer rate is not explicitly set on account, it implies the default transfer rate
|
||||
self._issuerTransferRate = info.account_data.TransferRate || OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
// When transfer rate is not explicitly set on account, it implies the
|
||||
// default transfer rate
|
||||
self._issuerTransferRate = info.account_data.TransferRate ||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
callback(null, self._issuerTransferRate);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this._remote.requestAccountInfo(
|
||||
{account: this._issuerGets},
|
||||
handleAccountInfo
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set funded amount on offer with its owner's cached funds
|
||||
*
|
||||
* Offers have is_fully_funded, indicating whether these funds are sufficient for the offer placed.
|
||||
* Offers have taker_gets_funded, reflecting the amount this account can afford to offer.
|
||||
* Offers have taker_pays_funded, reflecting an adjusted TakerPays in the case of a partially funded order.
|
||||
*
|
||||
* is_fully_funded indicates if these funds are sufficient for the offer placed.
|
||||
* taker_gets_funded indicates the amount this account can afford to offer.
|
||||
* taker_pays_funded indicates adjusted TakerPays for partially funded offer.
|
||||
*
|
||||
* @param {Object} offer
|
||||
* @return offer
|
||||
*/
|
||||
|
||||
OrderBook.prototype.setOfferFundedAmount = function(offer) {
|
||||
assert.strictEqual(typeof offer, 'object', 'Offer is invalid');
|
||||
assert(!isNaN(this.getOwnerFunds(offer.Account)), 'Funds is invalid');
|
||||
|
||||
var fundedAmount = Amount.from_json(
|
||||
this._currencyGets.is_native()
|
||||
? this.getOwnerFunds(offer.Account)
|
||||
: this.getOwnerFunds(offer.Account) + OrderBook.IOU_SUFFIX
|
||||
);
|
||||
var fundedAmount = this.getOwnerFunds(offer.Account);
|
||||
var previousOfferSum = this.getOwnerOfferTotal(offer.Account);
|
||||
var currentOfferSum = Amount.from_json(offer.TakerGets).add(previousOfferSum);
|
||||
var currentOfferSum = previousOfferSum.add(Amount.from_json(offer.TakerGets));
|
||||
|
||||
offer.owner_funds = this.getUnadjustedOwnerFunds(offer.Account);
|
||||
|
||||
@@ -596,23 +633,15 @@ OrderBook.prototype.setOfferFundedAmount = function(offer) {
|
||||
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
|
||||
offer.taker_pays_funded = Amount.from_json(offer.TakerPays).to_text();
|
||||
} else if (previousOfferSum.compareTo(fundedAmount) < 0) {
|
||||
var takerGetsFunded = fundedAmount.subtract(previousOfferSum);
|
||||
offer.taker_gets_funded = fundedAmount.subtract(previousOfferSum).to_text();
|
||||
|
||||
var takerPaysValue = this._currencyPays.is_native()
|
||||
? offer.TakerPays
|
||||
: offer.TakerPays.value;
|
||||
var takerPays = Amount.from_json(takerPaysValue + OrderBook.IOU_SUFFIX);
|
||||
var takerPaysFunded = this.getOfferQuality(offer).multiply(
|
||||
this.getOfferTakerGetsFunded(offer)
|
||||
);
|
||||
|
||||
var takerGetsValue = this._currencyGets.is_native()
|
||||
? offer.TakerGets
|
||||
: offer.TakerGets.value;
|
||||
var takerGets = Amount.from_json(takerGetsValue + OrderBook.IOU_SUFFIX);
|
||||
|
||||
var quality = takerPays.divide(takerGets);
|
||||
var takerPaysFunded = Amount.from_json(takerGetsFunded.to_text() + OrderBook.IOU_SUFFIX).multiply(quality);
|
||||
|
||||
offer.taker_gets_funded = takerGetsFunded.to_text();
|
||||
offer.taker_pays_funded = this._currencyPays.is_native() ? String(parseInt(takerPaysFunded.to_json().value, 10)) : takerPaysFunded.to_json().value;
|
||||
offer.taker_pays_funded = this._currencyPays.is_native()
|
||||
? String(Math.floor(takerPaysFunded.to_number()))
|
||||
: takerPaysFunded.to_json().value;
|
||||
} else {
|
||||
offer.taker_gets_funded = '0';
|
||||
offer.taker_pays_funded = '0';
|
||||
@@ -627,10 +656,11 @@ OrderBook.prototype.setOfferFundedAmount = function(offer) {
|
||||
* @param {Object} node - RippleState or AccountRoot meta node
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
OrderBook.prototype.parseAccountBalanceFromNode = function(node) {
|
||||
var result = {
|
||||
account: void(0),
|
||||
balance: void(0)
|
||||
account: undefined,
|
||||
balance: undefined
|
||||
};
|
||||
|
||||
switch (node.entryType) {
|
||||
@@ -695,7 +725,7 @@ OrderBook.prototype.isBalanceChangeNode = function(node) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates funded amounts/balances using ModifiedNode
|
||||
* Updates funded amounts/balances using modified balance nodes
|
||||
*
|
||||
* Update owner funds using modified AccountRoot and RippleState nodes.
|
||||
* Update funded amounts for offers in the orderbook using owner funds.
|
||||
@@ -749,7 +779,9 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
var self = this;
|
||||
|
||||
if (this._remote.trace) {
|
||||
log.info('updating offer funds', this._key, account, this.getOwnerFunds(account));
|
||||
var ownerFunds = this.getOwnerFunds(account).to_text();
|
||||
|
||||
log.info('updating offer funds', this._key, account, ownerFunds);
|
||||
}
|
||||
|
||||
this.resetOwnerOfferTotal(account);
|
||||
@@ -761,21 +793,19 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
|
||||
// Save a copy of the old offer so we can show how the offer has changed
|
||||
var previousOffer = extend({}, offer);
|
||||
var previousFundedGetsValue = offer.taker_gets_funded;
|
||||
var previousFundedGets;
|
||||
|
||||
if (_.isString(previousFundedGetsValue)) {
|
||||
previousFundedGets = Amount.from_json(
|
||||
offer.taker_gets_funded + OrderBook.IOU_SUFFIX
|
||||
);
|
||||
if (_.isString(offer.taker_gets_funded)) {
|
||||
// Offer is not new, so we should consider it for offer_changed and
|
||||
// offer_funds_changed events
|
||||
previousFundedGets = self.getOfferTakerGetsFunded(offer);
|
||||
}
|
||||
|
||||
self.setOfferFundedAmount(offer);
|
||||
self.addOwnerOfferTotal(offer.Account, offer.TakerGets);
|
||||
|
||||
var areFundsChanged = previousFundedGets && !previousFundedGets.equals(
|
||||
Amount.from_json(offer.taker_gets_funded + OrderBook.IOU_SUFFIX)
|
||||
);
|
||||
var areFundsChanged = previousFundedGets
|
||||
&& !self.getOfferTakerGetsFunded(offer).equals(previousFundedGets);
|
||||
|
||||
if (areFundsChanged) {
|
||||
self.emit('offer_changed', previousOffer, offer);
|
||||
@@ -818,18 +848,19 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
var takerGetsTotal = Amount.from_json(
|
||||
'0' + ((Currency.from_json(this._currencyGets).is_native())
|
||||
? ''
|
||||
: ('/' + this._currencyGets + '/' + this._issuerGets))
|
||||
: ('/' + this._currencyGets.to_human() + '/' + this._issuerGets))
|
||||
);
|
||||
|
||||
var takerPaysTotal = Amount.from_json(
|
||||
'0' + ((Currency.from_json(this._currencyPays).is_native())
|
||||
? ''
|
||||
: ('/' + this._currencyPays + '/' + this._issuerPays))
|
||||
: ('/' + this._currencyPays.to_human() + '/' + this._issuerPays))
|
||||
);
|
||||
|
||||
function handleNode(node) {
|
||||
var isOfferCancel = transaction.transaction.TransactionType === 'OfferCancel';
|
||||
var isOfferCancel = transaction.transaction.TransactionType === 'OfferCancel';
|
||||
var transactionOwnerFunds = transaction.transaction.owner_funds;
|
||||
|
||||
function handleNode(node) {
|
||||
switch (node.nodeType) {
|
||||
case 'DeletedNode':
|
||||
self.deleteOffer(node, isOfferCancel);
|
||||
@@ -844,16 +875,21 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
case 'ModifiedNode':
|
||||
self.modifyOffer(node);
|
||||
|
||||
takerGetsTotal = takerGetsTotal.add(node.fieldsPrev.TakerGets).subtract(node.fieldsFinal.TakerGets);
|
||||
takerPaysTotal = takerPaysTotal.add(node.fieldsPrev.TakerPays).subtract(node.fieldsFinal.TakerPays);
|
||||
takerGetsTotal = takerGetsTotal
|
||||
.add(node.fieldsPrev.TakerGets)
|
||||
.subtract(node.fieldsFinal.TakerGets);
|
||||
|
||||
takerPaysTotal = takerPaysTotal
|
||||
.add(node.fieldsPrev.TakerPays)
|
||||
.subtract(node.fieldsFinal.TakerPays);
|
||||
break;
|
||||
|
||||
case 'CreatedNode':
|
||||
self.setOwnerFunds(node.fields.Account, transaction.transaction.owner_funds);
|
||||
self.setOwnerFunds(node.fields.Account, transactionOwnerFunds);
|
||||
self.insertOffer(node);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_.each(affectedNodes, handleNode);
|
||||
|
||||
@@ -867,8 +903,8 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
/**
|
||||
* Insert an offer into the orderbook
|
||||
*
|
||||
* NOTE: We *MUST* update offers' funded amounts when a new offer is placed because funds go
|
||||
* to the highest quality offers first.
|
||||
* NOTE: We *MUST* update offers' funded amounts when a new offer is placed
|
||||
* because funds go to the highest quality offers first.
|
||||
*
|
||||
* @param {Object} node - Offer node
|
||||
*/
|
||||
@@ -879,33 +915,26 @@ OrderBook.prototype.insertOffer = function(node) {
|
||||
}
|
||||
|
||||
var offer = OrderBook.offerRewrite(node.fields);
|
||||
var takerGets = this.normalizeAmount(this._currencyGets, offer.TakerGets);
|
||||
var takerPays = this.normalizeAmount(this._currencyPays, offer.TakerPays);
|
||||
|
||||
offer.LedgerEntryType = node.entryType;
|
||||
offer.index = node.ledgerIndex;
|
||||
|
||||
var DATE_REF = {
|
||||
reference_date: new Date()
|
||||
};
|
||||
|
||||
// XXX Should use Amount#from_quality
|
||||
var price = Amount.from_json(
|
||||
offer.TakerPays
|
||||
).ratio_human(offer.TakerGets, DATE_REF);
|
||||
// We're safe to calculate quality for newly created offers
|
||||
offer.quality = takerPays.divide(takerGets).to_text();
|
||||
|
||||
var quality = this.getOfferQuality(offer);
|
||||
var originalLength = this._offers.length;
|
||||
|
||||
for (var i=0; i<originalLength; i++) {
|
||||
var currentOffer = this._offers[i];
|
||||
|
||||
var priceItem = Amount.from_json(
|
||||
currentOffer.TakerPays
|
||||
).ratio_human(currentOffer.TakerGets, DATE_REF);
|
||||
for (var i = 0; i < originalLength; i++) {
|
||||
var existingOfferQuality = this.getOfferQuality(this._offers[i]);
|
||||
|
||||
if (price.compareTo(priceItem) <= 0) {
|
||||
if (quality.compareTo(existingOfferQuality) <= 0) {
|
||||
this._offers.splice(i, 0, offer);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._offers.length === originalLength) {
|
||||
@@ -919,6 +948,47 @@ OrderBook.prototype.insertOffer = function(node) {
|
||||
this.emit('offer_added', offer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve offer quality
|
||||
*
|
||||
* @param {Object} offer
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOfferQuality = function(offer) {
|
||||
var amount;
|
||||
|
||||
if (this._currencyGets.has_interest()) {
|
||||
// XXX Should use Amount#from_quality
|
||||
amount = Amount.from_json(
|
||||
offer.TakerPays
|
||||
).ratio_human(offer.TakerGets, {
|
||||
reference_date: new Date()
|
||||
});
|
||||
} else {
|
||||
amount = Amount.from_json(offer.quality + OrderBook.IOU_SUFFIX);
|
||||
}
|
||||
|
||||
return amount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert any amount into default IOU
|
||||
*
|
||||
* NOTE: This is necessary in some places because Amount.js arithmetic
|
||||
* does not deal with native and non-native amounts well.
|
||||
*
|
||||
* @param {Currency} currency
|
||||
* @param {Object} amountObj
|
||||
*/
|
||||
|
||||
OrderBook.prototype.normalizeAmount = function(currency, amountObj) {
|
||||
var value = currency.is_native()
|
||||
? amountObj
|
||||
: amountObj.value;
|
||||
|
||||
return Amount.from_json(value + OrderBook.IOU_SUFFIX);
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify an existing offer in the orderbook
|
||||
*
|
||||
@@ -930,7 +1000,7 @@ OrderBook.prototype.modifyOffer = function(node) {
|
||||
log.info('modifying offer', this._key, node.fields);
|
||||
}
|
||||
|
||||
for (var i=0; i<this._offers.length; i++) {
|
||||
for (var i = 0; i < this._offers.length; i++) {
|
||||
var offer = this._offers[i];
|
||||
|
||||
if (offer.index === node.ledgerIndex) {
|
||||
@@ -945,7 +1015,7 @@ OrderBook.prototype.modifyOffer = function(node) {
|
||||
this.updateOwnerOffersFundedAmount(node.fields.Account);
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Delete an existing offer in the orderbook
|
||||
*
|
||||
* NOTE: We only update funded amounts when the node comes from an OfferCancel
|
||||
@@ -953,7 +1023,7 @@ OrderBook.prototype.modifyOffer = function(node) {
|
||||
* other existing offers in the book
|
||||
*
|
||||
* @param {Object} node - Offer node
|
||||
* @param {Boolean} isOfferCancel - whether node came from an OfferCancel transaction
|
||||
* @param {Boolean} isOfferCancel - whether node came from an OfferCancel
|
||||
*/
|
||||
|
||||
OrderBook.prototype.deleteOffer = function(node, isOfferCancel) {
|
||||
@@ -961,7 +1031,7 @@ OrderBook.prototype.deleteOffer = function(node, isOfferCancel) {
|
||||
log.info('deleting offer', this._key, node.fields);
|
||||
}
|
||||
|
||||
for (var i=0; i<this._offers.length; i++) {
|
||||
for (var i = 0; i < this._offers.length; i++) {
|
||||
var offer = this._offers[i];
|
||||
|
||||
if (offer.index === node.ledgerIndex) {
|
||||
@@ -1000,14 +1070,15 @@ OrderBook.prototype.setOffers = function(offers) {
|
||||
var offer = OrderBook.offerRewrite(rawOffer);
|
||||
|
||||
if (offer.hasOwnProperty('owner_funds')) {
|
||||
// The first offer from book_offers contains owner balance of offer's output
|
||||
// The first offer of each owner from book_offers contains owner balance
|
||||
// of offer's output
|
||||
self.setOwnerFunds(offer.Account, offer.owner_funds);
|
||||
}
|
||||
|
||||
self.incrementOwnerOfferCount(offer.Account);
|
||||
|
||||
|
||||
self.setOfferFundedAmount(offer);
|
||||
self.addOwnerOfferTotal(rawOffer.Account, rawOffer.TakerGets);
|
||||
self.addOwnerOfferTotal(offer.Account, offer.TakerGets);
|
||||
|
||||
return offer;
|
||||
});
|
||||
@@ -1031,8 +1102,6 @@ OrderBook.prototype.offers =
|
||||
OrderBook.prototype.getOffers = function(callback) {
|
||||
assert.strictEqual(typeof callback, 'function', 'Callback missing');
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this._synchronized) {
|
||||
callback(null, this._offers);
|
||||
} else {
|
||||
@@ -1107,4 +1176,4 @@ OrderBook.prototype.is_valid = function() {
|
||||
);
|
||||
};
|
||||
|
||||
exports.OrderBook = OrderBook;
|
||||
exports.OrderBook = OrderBook;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
// Interface to manage connections to rippled servers
|
||||
//
|
||||
// - We never send binary data.
|
||||
@@ -19,7 +21,6 @@ var async = require('async');
|
||||
var lodash = require('lodash');
|
||||
var Server = require('./server').Server;
|
||||
var Request = require('./request').Request;
|
||||
var Server = require('./server').Server;
|
||||
var Amount = require('./amount').Amount;
|
||||
var Currency = require('./currency').Currency;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
@@ -47,31 +48,31 @@ function Remote(opts) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
var opts = opts || { };
|
||||
opts = opts || { };
|
||||
|
||||
Object.keys(Remote.DEFAULTS).forEach(function(config) {
|
||||
if (opts.hasOwnProperty(config)) {
|
||||
this[config] = opts[config];
|
||||
Object.keys(Remote.DEFAULTS).forEach(function(option) {
|
||||
if (opts.hasOwnProperty(option)) {
|
||||
this[option] = opts[option];
|
||||
} else {
|
||||
this[config] = Remote.DEFAULTS[config];
|
||||
this[option] = Remote.DEFAULTS[option];
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this._server_fatal = false; // server exited
|
||||
this._stand_alone = void(0);
|
||||
this._testnet = void(0);
|
||||
this._stand_alone = undefined;
|
||||
this._testnet = undefined;
|
||||
|
||||
this._ledger_current_index = void(0);
|
||||
this._ledger_hash = void(0);
|
||||
this._ledger_time = void(0);
|
||||
this._ledger_current_index = undefined;
|
||||
this._ledger_hash = undefined;
|
||||
this._ledger_time = undefined;
|
||||
|
||||
this._connection_count = 0;
|
||||
this._connected = false;
|
||||
this._should_connect = true;
|
||||
|
||||
this._transaction_listeners = 0;
|
||||
this._received_tx = new LRU({ max: 100 });
|
||||
this._received_tx = new LRU({max: 100});
|
||||
this._cur_path_find = null;
|
||||
|
||||
if (this.local_signing) {
|
||||
@@ -81,7 +82,7 @@ function Remote(opts) {
|
||||
}
|
||||
|
||||
this._servers = [ ];
|
||||
this._primary_server = void(0);
|
||||
this._primary_server = undefined;
|
||||
|
||||
// Cache information for accounts.
|
||||
// DEPRECATED, will be removed
|
||||
@@ -174,7 +175,7 @@ function Remote(opts) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.on('newListener', function(event) {
|
||||
listenersModified('add', event);
|
||||
@@ -183,7 +184,7 @@ function Remote(opts) {
|
||||
this.on('removeListener', function(event) {
|
||||
listenersModified('remove', event);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(Remote, EventEmitter);
|
||||
|
||||
@@ -217,7 +218,10 @@ Remote.flags = {
|
||||
RequireDestTag: 0x00020000, // require a DestinationTag for payments
|
||||
RequireAuth: 0x00040000, // require a authorization to hold IOUs
|
||||
DisallowXRP: 0x00080000, // disallow sending XRP
|
||||
DisableMaster: 0x00100000 // force regular key
|
||||
DisableMaster: 0x00100000, // force regular key
|
||||
DefaultRipple: 0x00800000,
|
||||
NoFreeze: 0x00200000, // permanently disallowed freezing trustlines
|
||||
GlobalFreeze: 0x00400000 // trustlines globally frozen
|
||||
},
|
||||
// Offer
|
||||
offer: {
|
||||
@@ -249,7 +253,7 @@ Remote.from_config = function(obj, trace) {
|
||||
remote.setSecret(accountInfo.account, accountInfo.secret);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (config.accounts) {
|
||||
Object.keys(config.accounts).forEach(initializeAccount);
|
||||
@@ -361,7 +365,7 @@ Remote.prototype.setServerFatal = function() {
|
||||
*/
|
||||
|
||||
Remote.prototype.setTrace = function(trace) {
|
||||
this.trace = (trace === void(0) || trace);
|
||||
this.trace = (trace === undefined || trace);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -390,7 +394,7 @@ Remote.prototype.addServer = function(opts) {
|
||||
|
||||
function serverMessage(data) {
|
||||
self._handleMessage(data, server);
|
||||
};
|
||||
}
|
||||
|
||||
server.on('message', serverMessage);
|
||||
|
||||
@@ -406,7 +410,7 @@ Remote.prototype.addServer = function(opts) {
|
||||
if (self._connection_count === self._servers.length) {
|
||||
self.emit('ready');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
server.on('connect', serverConnect);
|
||||
|
||||
@@ -415,7 +419,7 @@ Remote.prototype.addServer = function(opts) {
|
||||
if (self._connection_count === 0) {
|
||||
self._setState('offline');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
server.on('disconnect', serverDisconnect);
|
||||
|
||||
@@ -438,8 +442,6 @@ Remote.prototype.reconnect = function() {
|
||||
this._servers.forEach(function(server) {
|
||||
server.reconnect();
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -479,7 +481,9 @@ Remote.prototype.disconnect = function(callback) {
|
||||
throw new Error('No servers available, not disconnecting');
|
||||
}
|
||||
|
||||
var callback = (typeof callback === 'function') ? callback : function(){};
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function() {};
|
||||
}
|
||||
|
||||
this._should_connect = false;
|
||||
|
||||
@@ -633,7 +637,8 @@ Remote.prototype._handleTransaction = function(message, server) {
|
||||
}, this);
|
||||
} else {
|
||||
// Transaction could be from proposed transaction stream
|
||||
[ 'Account', 'Destination' ].forEach(function(prop) {
|
||||
// XX
|
||||
['Account', 'Destination'].forEach(function(prop) {
|
||||
if (this._accounts[message.transaction[prop]]) {
|
||||
this._accounts[message.transaction[prop]].notify(message);
|
||||
}
|
||||
@@ -936,9 +941,12 @@ Remote.prototype.requestLedgerCurrent = function(callback) {
|
||||
* Get the contents of a specified ledger
|
||||
*
|
||||
* @param {Object} options
|
||||
* @property {Boolean} [options.binary] - Flag which determines if rippled returns binary or parsed JSON
|
||||
* @property {String|Number} [options.ledger] - Hash or sequence of a ledger to get contents for
|
||||
* @property {Number} [options.limit] - Number of contents to retrieve from the ledger
|
||||
* @property {Boolean} [options.binary]- Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @property {String|Number} [options.ledger] - Hash or sequence of a ledger
|
||||
* to get contents for
|
||||
* @property {Number} [options.limit] - Number of contents to retrieve
|
||||
* from the ledger
|
||||
* @property {Function} callback
|
||||
*
|
||||
* @callback
|
||||
@@ -965,7 +973,7 @@ Remote.prototype.requestLedgerData = function(options, callback) {
|
||||
async.setImmediate(function() {
|
||||
next(null, Remote.parseBinaryLedgerData(ledgerData));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function complete(err, state) {
|
||||
if (err) {
|
||||
@@ -974,7 +982,7 @@ Remote.prototype.requestLedgerData = function(options, callback) {
|
||||
res.state = state;
|
||||
request.emit('state', res);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async.mapSeries(res.state, iterator, complete);
|
||||
});
|
||||
@@ -1028,7 +1036,7 @@ Remote.prototype.requestLedgerEntry = function(type, callback) {
|
||||
// Emulate fetch of ledger entry.
|
||||
// console.log('request_ledger_entry: emulating');
|
||||
// YYY Missing lots of fields.
|
||||
request.emit('success', { node: node });
|
||||
request.emit('success', {node: node});
|
||||
bDefault = false;
|
||||
} else { // Was not cached.
|
||||
// XXX Only allow with trusted mode. Must sync response with advance
|
||||
@@ -1073,7 +1081,7 @@ Remote.prototype.requestSubscribe = function(streams, callback) {
|
||||
var request = new Request(this, 'subscribe');
|
||||
|
||||
if (streams) {
|
||||
request.message.streams = Array.isArray(streams) ? streams : [ streams ];
|
||||
request.message.streams = Array.isArray(streams) ? streams : [streams];
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
@@ -1093,7 +1101,7 @@ Remote.prototype.requestUnsubscribe = function(streams, callback) {
|
||||
var request = new Request(this, 'unsubscribe');
|
||||
|
||||
if (streams) {
|
||||
request.message.streams = Array.isArray(streams) ? streams : [ streams ];
|
||||
request.message.streams = Array.isArray(streams) ? streams : [streams];
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
@@ -1110,7 +1118,8 @@ Remote.prototype.requestUnsubscribe = function(streams, callback) {
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
Remote.prototype.requestTransactionEntry = function(hash, ledgerHash, callback) {
|
||||
Remote.prototype.requestTransactionEntry =
|
||||
function(hash, ledgerHash, callback) {
|
||||
// If not trusted, need to check proof, maybe talk packet protocol.
|
||||
// utils.assert(this.trusted);
|
||||
var request = new Request(this, 'transaction_entry');
|
||||
@@ -1119,7 +1128,7 @@ Remote.prototype.requestTransactionEntry = function(hash, ledgerHash, callback)
|
||||
ledgerHash = hash.ledger || hash.ledger_hash || hash.ledger_index;
|
||||
hash = hash.hash || hash.tx || hash.transaction;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1151,7 +1160,8 @@ Remote.prototype.requestTransactionEntry = function(hash, ledgerHash, callback)
|
||||
*
|
||||
* @param {Object|String} hash
|
||||
* @property {String} hash.hash - Transaction hash
|
||||
* @property {Boolean} [hash.binary=true] - Flag which determines if rippled returns binary or parsed JSON
|
||||
* @property {Boolean} [hash.binary=true] - Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @param [Function] callback
|
||||
* @return {Request} request
|
||||
*/
|
||||
@@ -1161,8 +1171,8 @@ Remote.prototype.requestTx = function(hash, callback) {
|
||||
var options;
|
||||
|
||||
if (typeof hash === 'string') {
|
||||
options = { hash: hash };
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
options = {hash: hash};
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
} else {
|
||||
options = hash;
|
||||
@@ -1219,7 +1229,7 @@ Remote.accountRequest = function(type, options, callback) {
|
||||
limit = options.limit;
|
||||
marker = options.marker;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1387,15 +1397,15 @@ Remote.prototype.requestAccountTx = function(options, callback) {
|
||||
|
||||
options.binary = options.binary !== false;
|
||||
|
||||
if (options.min_ledger !== void(0)) {
|
||||
if (options.min_ledger !== undefined) {
|
||||
options.ledger_index_min = options.min_ledger;
|
||||
}
|
||||
|
||||
if (options.max_ledger !== void(0)) {
|
||||
if (options.max_ledger !== undefined) {
|
||||
options.ledger_index_max = options.max_ledger;
|
||||
}
|
||||
|
||||
if (options.binary && options.parseBinary === void(0)) {
|
||||
if (options.binary && options.parseBinary === undefined) {
|
||||
options.parseBinary = true;
|
||||
}
|
||||
|
||||
@@ -1428,7 +1438,7 @@ Remote.prototype.requestAccountTx = function(options, callback) {
|
||||
async.setImmediate(function() {
|
||||
next(null, Remote.parseBinaryAccountTransaction(transaction));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function complete(err, transactions) {
|
||||
if (err) {
|
||||
@@ -1437,7 +1447,7 @@ Remote.prototype.requestAccountTx = function(options, callback) {
|
||||
res.transactions = transactions;
|
||||
request.emit('transactions', res);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async.mapSeries(res.transactions, iterator, complete);
|
||||
});
|
||||
@@ -1494,15 +1504,18 @@ Remote.parseBinaryTransaction = function(transaction) {
|
||||
tx_result.meta = meta;
|
||||
tx_result.validated = transaction.validated;
|
||||
|
||||
if (typeof meta.DeliveredAmount === 'string' || typeof meta.DeliveredAmount === 'object') {
|
||||
tx_result.meta.delivered_amount = meta.DeliveredAmount;
|
||||
} else {
|
||||
switch (typeof tx_obj.Amount) {
|
||||
case 'string':
|
||||
case 'object':
|
||||
tx_result.meta.delivered_amount = tx_obj.Amount;
|
||||
break;
|
||||
}
|
||||
switch (typeof meta.DeliveredAmount) {
|
||||
case 'string':
|
||||
case 'object':
|
||||
tx_result.meta.delivered_amount = meta.DeliveredAmount;
|
||||
break;
|
||||
default:
|
||||
switch (typeof tx_obj.Amount) {
|
||||
case 'string':
|
||||
case 'object':
|
||||
tx_result.meta.delivered_amount = tx_obj.Amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tx_result;
|
||||
@@ -1548,7 +1561,7 @@ Remote.prototype.requestTxHistory = function(start, callback) {
|
||||
if (typeof start === 'object') {
|
||||
start = start.start;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1588,7 +1601,7 @@ Remote.prototype.requestBookOffers = function(gets, pays, taker, callback) {
|
||||
ledger = options.ledger;
|
||||
limit = options.limit;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1654,7 +1667,7 @@ Remote.prototype.requestWalletAccounts = function(seed, callback) {
|
||||
if (typeof seed === 'object') {
|
||||
seed = seed.seed;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1682,7 +1695,7 @@ Remote.prototype.requestSign = function(secret, tx_json, callback) {
|
||||
tx_json = secret.tx_json;
|
||||
secret = secret.secret;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1718,7 +1731,7 @@ Remote.prototype.requestSubmit = function(callback) {
|
||||
|
||||
Remote.prototype._serverPrepareSubscribe = function(server, callback) {
|
||||
var self = this;
|
||||
var feeds = [ 'ledger', 'server' ];
|
||||
var feeds = ['ledger', 'server'];
|
||||
|
||||
if (typeof server === 'function') {
|
||||
callback = server;
|
||||
@@ -1747,7 +1760,7 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback) {
|
||||
self._handleLedgerClosed(message, server);
|
||||
|
||||
self.emit('subscribed');
|
||||
};
|
||||
}
|
||||
|
||||
request.on('error', function(err) {
|
||||
if (self.trace) {
|
||||
@@ -1775,13 +1788,15 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback) {
|
||||
|
||||
Remote.prototype.ledgerAccept =
|
||||
Remote.prototype.requestLedgerAccept = function(callback) {
|
||||
/* eslint-disable consistent-return */
|
||||
var request = new Request(this, 'ledger_accept');
|
||||
|
||||
if (!this._stand_alone) {
|
||||
// XXX This should emit error on the request
|
||||
this.emit('error', new RippleError('notStandAlone'));
|
||||
return;
|
||||
}
|
||||
|
||||
var request = new Request(this, 'ledger_accept');
|
||||
|
||||
this.once('ledger_closed', function(ledger) {
|
||||
request.emit('ledger_closed', ledger);
|
||||
});
|
||||
@@ -1790,6 +1805,7 @@ Remote.prototype.requestLedgerAccept = function(callback) {
|
||||
request.request();
|
||||
|
||||
return request;
|
||||
/* eslint-enable consistent-return */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1799,13 +1815,14 @@ Remote.prototype.requestLedgerAccept = function(callback) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Remote.accountRootRequest = function(type, responseFilter, account, ledger, callback) {
|
||||
Remote.accountRootRequest =
|
||||
function(type, responseFilter, account, ledger, callback) {
|
||||
if (typeof account === 'object') {
|
||||
callback = ledger;
|
||||
ledger = account.ledger;
|
||||
account = account.account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1841,7 +1858,7 @@ Remote.accountRootRequest = function(type, responseFilter, account, ledger, call
|
||||
Remote.prototype.requestAccountBalance = function() {
|
||||
function responseFilter(message) {
|
||||
return Amount.from_json(message.node.Balance);
|
||||
};
|
||||
}
|
||||
|
||||
var args = Array.prototype.concat.apply(
|
||||
['account_balance', responseFilter],
|
||||
@@ -1864,7 +1881,7 @@ Remote.prototype.requestAccountBalance = function() {
|
||||
Remote.prototype.requestAccountFlags = function() {
|
||||
function responseFilter(message) {
|
||||
return message.node.Flags;
|
||||
};
|
||||
}
|
||||
|
||||
var args = Array.prototype.concat.apply(
|
||||
['account_flags', responseFilter],
|
||||
@@ -1887,7 +1904,7 @@ Remote.prototype.requestAccountFlags = function() {
|
||||
Remote.prototype.requestOwnerCount = function() {
|
||||
function responseFilter(message) {
|
||||
return message.node.OwnerCount;
|
||||
};
|
||||
}
|
||||
|
||||
var args = Array.prototype.concat.apply(
|
||||
['owner_count', responseFilter],
|
||||
@@ -1949,7 +1966,8 @@ Remote.prototype.findAccount = function(accountID) {
|
||||
*/
|
||||
|
||||
Remote.prototype.pathFind =
|
||||
Remote.prototype.createPathFind = function(src_account, dst_account, dst_amount, src_currencies) {
|
||||
Remote.prototype.createPathFind =
|
||||
function(src_account, dst_account, dst_amount, src_currencies) {
|
||||
if (typeof src_account === 'object') {
|
||||
var options = src_account;
|
||||
src_currencies = options.src_currencies;
|
||||
@@ -1957,7 +1975,7 @@ Remote.prototype.createPathFind = function(src_account, dst_account, dst_amount,
|
||||
dst_account = options.dst_account;
|
||||
src_account = options.src_account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -1990,7 +2008,8 @@ Remote.prepareTrade = function(currency, issuer) {
|
||||
*/
|
||||
|
||||
Remote.prototype.book =
|
||||
Remote.prototype.createOrderBook = function(currency_gets, issuer_gets, currency_pays, issuer_pays) {
|
||||
Remote.prototype.createOrderBook =
|
||||
function(currency_gets, issuer_gets, currency_pays, issuer_pays) {
|
||||
if (typeof currency_gets === 'object') {
|
||||
var options = currency_gets;
|
||||
issuer_pays = options.issuer_pays;
|
||||
@@ -1998,7 +2017,7 @@ Remote.prototype.createOrderBook = function(currency_gets, issuer_gets, currency
|
||||
issuer_gets = options.issuer_gets;
|
||||
currency_gets = options.currency_gets;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -2032,15 +2051,15 @@ Remote.prototype.createOrderBook = function(currency_gets, issuer_gets, currency
|
||||
|
||||
Remote.prototype.accountSeq =
|
||||
Remote.prototype.getAccountSequence = function(account, advance) {
|
||||
var account = UInt160.json_rewrite(account);
|
||||
account = UInt160.json_rewrite(account);
|
||||
var accountInfo = this.accounts[account];
|
||||
|
||||
if (!accountInfo) {
|
||||
return;
|
||||
return NaN;
|
||||
}
|
||||
|
||||
var seq = accountInfo.seq;
|
||||
var change = { ADVANCE: 1, REWIND: -1 }[advance.toUpperCase()] || 0;
|
||||
var change = {ADVANCE: 1, REWIND: -1}[advance.toUpperCase()] || 0;
|
||||
|
||||
accountInfo.seq += change;
|
||||
|
||||
@@ -2056,7 +2075,7 @@ Remote.prototype.getAccountSequence = function(account, advance) {
|
||||
|
||||
Remote.prototype.setAccountSequence =
|
||||
Remote.prototype.setAccountSeq = function(account, sequence) {
|
||||
var account = UInt160.json_rewrite(account);
|
||||
account = UInt160.json_rewrite(account);
|
||||
|
||||
if (!this.accounts.hasOwnProperty(account)) {
|
||||
this.accounts[account] = { };
|
||||
@@ -2081,7 +2100,7 @@ Remote.prototype.accountSeqCache = function(account, ledger, callback) {
|
||||
ledger = options.ledger;
|
||||
account = options.account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -2099,13 +2118,13 @@ Remote.prototype.accountSeqCache = function(account, ledger, callback) {
|
||||
account_info.seq = seq;
|
||||
|
||||
request.emit('success_cache', message);
|
||||
};
|
||||
}
|
||||
|
||||
function accountRootError(message) {
|
||||
delete account_info.caching_seq_request;
|
||||
|
||||
request.emit('error_cache', message);
|
||||
};
|
||||
}
|
||||
|
||||
if (!request) {
|
||||
request = this.requestLedgerEntry('account_root');
|
||||
@@ -2129,7 +2148,7 @@ Remote.prototype.accountSeqCache = function(account, ledger, callback) {
|
||||
*/
|
||||
|
||||
Remote.prototype.dirtyAccountRoot = function(account) {
|
||||
var account = UInt160.json_rewrite(account);
|
||||
account = UInt160.json_rewrite(account);
|
||||
delete this.ledgers.current.account_root[account];
|
||||
};
|
||||
|
||||
@@ -2140,7 +2159,8 @@ Remote.prototype.dirtyAccountRoot = function(account) {
|
||||
* @param {String|Number} options.ledger
|
||||
* @param {String} [options.account] - Required unless using options.index
|
||||
* @param {Number} [options.sequence] - Required unless using options.index
|
||||
* @param {String} [options.index] - Required only if options.account and options.sequence not provided
|
||||
* @param {String} [options.index] - Required only if options.account and
|
||||
* options.sequence not provided
|
||||
*
|
||||
* @callback
|
||||
* @param {Error} error
|
||||
@@ -2181,7 +2201,8 @@ Remote.prototype.requestOffer = function(options, callback) {
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledger, callback) {
|
||||
Remote.prototype.requestRippleBalance =
|
||||
function(account, issuer, currency, ledger, callback) {
|
||||
if (typeof account === 'object') {
|
||||
var options = account;
|
||||
callback = issuer;
|
||||
@@ -2190,7 +2211,7 @@ Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledg
|
||||
issuer = options.issuer;
|
||||
account = options.account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -2238,7 +2259,7 @@ Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledg
|
||||
? node.HighQualityOut
|
||||
: node.LowQualityOut)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
request.once('success', rippleState);
|
||||
request.callback(callback, 'ripple_state');
|
||||
@@ -2270,7 +2291,8 @@ Remote.prepareCurrencies = function(currency) {
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestRipplePathFind = function(src_account, dst_account, dst_amount, src_currencies, callback) {
|
||||
Remote.prototype.requestRipplePathFind =
|
||||
function(src_account, dst_account, dst_amount, src_currencies, callback) {
|
||||
if (typeof src_account === 'object') {
|
||||
var options = src_account;
|
||||
callback = dst_account;
|
||||
@@ -2279,7 +2301,7 @@ Remote.prototype.requestRipplePathFind = function(src_account, dst_account, dst_
|
||||
dst_account = options.dst_account;
|
||||
src_account = options.src_account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -2307,7 +2329,8 @@ Remote.prototype.requestRipplePathFind = function(src_account, dst_account, dst_
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestPathFindCreate = function(src_account, dst_account, dst_amount, src_currencies, callback) {
|
||||
Remote.prototype.requestPathFindCreate =
|
||||
function(src_account, dst_account, dst_amount, src_currencies, callback) {
|
||||
if (typeof src_account === 'object') {
|
||||
var options = src_account;
|
||||
callback = dst_account;
|
||||
@@ -2316,7 +2339,7 @@ Remote.prototype.requestPathFindCreate = function(src_account, dst_account, dst_
|
||||
dst_account = options.dst_account;
|
||||
src_account = options.src_account;
|
||||
} else {
|
||||
console.error('DEPRECATED: First argument to request constructor should be'
|
||||
log.warn('DEPRECATED: First argument to request constructor should be'
|
||||
+ ' an object containing request properties');
|
||||
}
|
||||
|
||||
@@ -2380,7 +2403,7 @@ Remote.prototype.requestUnlAdd = function(address, comment, callback) {
|
||||
|
||||
if (comment) {
|
||||
// note is not specified anywhere, should remove?
|
||||
request.message.comment = void(0);
|
||||
request.message.comment = undefined;
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Type definitions for binary format.
|
||||
*
|
||||
@@ -6,30 +8,29 @@
|
||||
* SerializedObject.parse() or SerializedObject.serialize().
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var extend = require('extend');
|
||||
var binformat = require('./binformat');
|
||||
var utils = require('./utils');
|
||||
var sjcl = utils.sjcl;
|
||||
var assert = require('assert');
|
||||
var extend = require('extend');
|
||||
var GlobalBigNumber = require('bignumber.js');
|
||||
var Amount = require('./amount').Amount;
|
||||
var Currency = require('./currency').Currency;
|
||||
var binformat = require('./binformat');
|
||||
var utils = require('./utils');
|
||||
var sjcl = utils.sjcl;
|
||||
var SJCL_BN = sjcl.bn;
|
||||
|
||||
var UInt128 = require('./uint128').UInt128;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
var amount = require('./amount');
|
||||
var Amount = amount.Amount;
|
||||
var Currency = amount.Currency;
|
||||
var UInt128 = require('./uint128').UInt128;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
var BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
|
||||
var SerializedType = function (methods) {
|
||||
function SerializedType(methods) {
|
||||
extend(this, methods);
|
||||
};
|
||||
}
|
||||
|
||||
function isNumber(val) {
|
||||
return typeof val === 'number' && isFinite(val);
|
||||
@@ -57,9 +58,13 @@ function serializeHex(so, hexData, noLength) {
|
||||
|
||||
/**
|
||||
* parses bytes as hex
|
||||
*
|
||||
* @param {Array} byte_array bytes
|
||||
* @return {String} hex string
|
||||
*/
|
||||
function convertByteArrayToHex (byte_array) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array)).toUpperCase();
|
||||
function convertByteArrayToHex(byte_array) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array))
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function convertHexToString(hexString) {
|
||||
@@ -67,6 +72,24 @@ function convertHexToString(hexString) {
|
||||
return sjcl.codec.utf8String.fromBits(bits);
|
||||
}
|
||||
|
||||
function sort_fields(keys) {
|
||||
function sort_field_compare(a, b) {
|
||||
var a_field_coordinates = binformat.fieldsInverseMap[a];
|
||||
var a_type_bits = a_field_coordinates[0];
|
||||
var a_field_bits = a_field_coordinates[1];
|
||||
var b_field_coordinates = binformat.fieldsInverseMap[b];
|
||||
var b_type_bits = b_field_coordinates[0];
|
||||
var b_field_bits = b_field_coordinates[1];
|
||||
|
||||
// Sort by type id first, then by field id
|
||||
return a_type_bits !== b_type_bits
|
||||
? a_type_bits - b_type_bits
|
||||
: a_field_bits - b_field_bits;
|
||||
}
|
||||
|
||||
return keys.sort(sort_field_compare);
|
||||
}
|
||||
|
||||
SerializedType.serialize_varint = function (so, val) {
|
||||
if (val < 0) {
|
||||
throw new Error('Variable integers are unsigned.');
|
||||
@@ -79,7 +102,7 @@ SerializedType.serialize_varint = function (so, val) {
|
||||
so.append([193 + (val >>> 8), val & 0xff]);
|
||||
} else if (val <= 918744) {
|
||||
val -= 12481;
|
||||
so.append([ 241 + (val >>> 16), val >>> 8 & 0xff, val & 0xff ]);
|
||||
so.append([241 + (val >>> 16), val >>> 8 & 0xff, val & 0xff]);
|
||||
} else {
|
||||
throw new Error('Variable integer overflow.');
|
||||
}
|
||||
@@ -107,13 +130,18 @@ SerializedType.prototype.parse_varint = function (so) {
|
||||
return result;
|
||||
};
|
||||
|
||||
// In the following, we assume that the inputs are in the proper range. Is this correct?
|
||||
// In the following, we assume that the inputs are in the proper range. Is this
|
||||
// correct?
|
||||
// Helper functions for 1-, 2-, and 4-byte integers.
|
||||
|
||||
/**
|
||||
* Convert an integer value into an array of bytes.
|
||||
*
|
||||
* The result is appended to the serialized object ('so').
|
||||
*
|
||||
* @param {Number} val value
|
||||
* @param {Number} bytes byte size
|
||||
* @return {Array} byte array
|
||||
*/
|
||||
function convertIntegerToByteArray(val, bytes) {
|
||||
if (!isNumber(val)) {
|
||||
@@ -126,14 +154,15 @@ function convertIntegerToByteArray(val, bytes) {
|
||||
|
||||
var newBytes = [ ];
|
||||
|
||||
for (var i=0; i<bytes; i++) {
|
||||
for (var i = 0; i < bytes; i++) {
|
||||
newBytes.unshift(val >>> (i * 8) & 0xff);
|
||||
}
|
||||
|
||||
return newBytes;
|
||||
}
|
||||
|
||||
// Convert a certain number of bytes from the serialized object ('so') into an integer.
|
||||
// Convert a certain number of bytes from the serialized object ('so') into an
|
||||
// integer.
|
||||
function readAndSum(so, bytes) {
|
||||
var sum = 0;
|
||||
|
||||
@@ -141,7 +170,7 @@ function readAndSum(so, bytes) {
|
||||
throw new Error('This function only supports up to four bytes.');
|
||||
}
|
||||
|
||||
for (var i=0; i<bytes; i++) {
|
||||
for (var i = 0; i < bytes; i++) {
|
||||
var byte = so.read(1)[0];
|
||||
sum += (byte << (8 * (bytes - i - 1)));
|
||||
}
|
||||
@@ -161,6 +190,89 @@ var STInt8 = exports.Int8 = new SerializedType({
|
||||
|
||||
STInt8.id = 16;
|
||||
|
||||
function serialize(so, field_name, value) {
|
||||
// so: a byte-stream to serialize into.
|
||||
// field_name: a string for the field name ('LedgerEntryType' etc.)
|
||||
// value: the value of that field.
|
||||
var field_coordinates = binformat.fieldsInverseMap[field_name];
|
||||
var type_bits = field_coordinates[0];
|
||||
var field_bits = field_coordinates[1];
|
||||
var tag_byte = (type_bits < 16
|
||||
? type_bits << 4
|
||||
: 0) | (field_bits < 16
|
||||
? field_bits
|
||||
: 0);
|
||||
|
||||
if (field_name === 'LedgerEntryType' && typeof value === 'string') {
|
||||
value = binformat.ledger[value][0];
|
||||
}
|
||||
|
||||
if (field_name === 'TransactionResult' && typeof value === 'string') {
|
||||
value = binformat.ter[value];
|
||||
}
|
||||
|
||||
STInt8.serialize(so, tag_byte);
|
||||
|
||||
if (type_bits >= 16) {
|
||||
STInt8.serialize(so, type_bits);
|
||||
}
|
||||
|
||||
if (field_bits >= 16) {
|
||||
STInt8.serialize(so, field_bits);
|
||||
}
|
||||
|
||||
// Get the serializer class (ST...)
|
||||
var serialized_object_type;
|
||||
|
||||
if (field_name === 'Memo' && typeof value === 'object') {
|
||||
// for Memo we override the default behavior with our STMemo serializer
|
||||
serialized_object_type = exports.STMemo;
|
||||
} else {
|
||||
// for a field based on the type bits.
|
||||
serialized_object_type = exports[binformat.types[type_bits]];
|
||||
}
|
||||
|
||||
try {
|
||||
serialized_object_type.serialize(so, value);
|
||||
} catch (e) {
|
||||
e.message += ' (' + field_name + ')';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
exports.serialize = exports.serialize_whatever = serialize;
|
||||
|
||||
// Take the serialized object, figure out what type/field it is, and return the
|
||||
// parsing of that.
|
||||
|
||||
function parse(so) {
|
||||
var tag_byte = so.read(1)[0];
|
||||
var type_bits = tag_byte >> 4;
|
||||
|
||||
if (type_bits === 0) {
|
||||
type_bits = so.read(1)[0];
|
||||
}
|
||||
|
||||
var field_bits = tag_byte & 0x0f;
|
||||
var field_name = (field_bits === 0)
|
||||
? field_name = binformat.fields[type_bits][so.read(1)[0]]
|
||||
: field_name = binformat.fields[type_bits][field_bits];
|
||||
|
||||
assert(field_name, 'Unknown field - header byte is 0x'
|
||||
+ tag_byte.toString(16));
|
||||
|
||||
// Get the parser class (ST...) for a field based on the type bits.
|
||||
var type = (field_name === 'Memo')
|
||||
? exports.STMemo
|
||||
: exports[binformat.types[type_bits]];
|
||||
|
||||
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
|
||||
|
||||
return [field_name, type.parse(so)]; // key, value
|
||||
}
|
||||
|
||||
exports.parse = exports.parse_whatever = parse;
|
||||
|
||||
var STInt16 = exports.Int16 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
so.append(convertIntegerToByteArray(val, 2));
|
||||
@@ -192,13 +304,13 @@ var STInt64 = exports.Int64 = new SerializedType({
|
||||
if (val < 0) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
bigNumObject = new sjcl.bn(val, 10);
|
||||
bigNumObject = new SJCL_BN(val, 10);
|
||||
} else if (isString(val)) {
|
||||
if (!isHexInt64String(val)) {
|
||||
throw new Error('Not a valid hex Int64.');
|
||||
}
|
||||
bigNumObject = new sjcl.bn(val, 16);
|
||||
} else if (val instanceof sjcl.bn) {
|
||||
bigNumObject = new SJCL_BN(val, 16);
|
||||
} else if (val instanceof SJCL_BN) {
|
||||
if (!val.greaterEquals(0)) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
@@ -206,11 +318,11 @@ var STInt64 = exports.Int64 = new SerializedType({
|
||||
} else {
|
||||
throw new Error('Invalid type for Int64');
|
||||
}
|
||||
serializeBits(so, bigNumObject.toBits(64), true); //noLength = true
|
||||
serializeBits(so, bigNumObject.toBits(64), true); // noLength = true
|
||||
},
|
||||
parse: function (so) {
|
||||
var bytes = so.read(8);
|
||||
return sjcl.bn.fromBits(sjcl.codec.bytes.toBits(bytes));
|
||||
return SJCL_BN.fromBits(sjcl.codec.bytes.toBits(bytes));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -222,7 +334,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash128');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); //noLength = true
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
},
|
||||
parse: function (so) {
|
||||
return UInt128.from_bytes(so.read(16));
|
||||
@@ -237,7 +349,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash256');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); //noLength = true
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
},
|
||||
parse: function (so) {
|
||||
return UInt256.from_bytes(so.read(32));
|
||||
@@ -252,7 +364,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash160');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); //noLength = true
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
},
|
||||
parse: function (so) {
|
||||
return UInt160.from_bytes(so.read(20));
|
||||
@@ -267,7 +379,8 @@ var STCurrency = new SerializedType({
|
||||
var currencyData = val.to_bytes();
|
||||
|
||||
if (!currencyData) {
|
||||
throw new Error('Tried to serialize invalid/unimplemented currency type.');
|
||||
throw new Error(
|
||||
'Tried to serialize invalid/unimplemented currency type.');
|
||||
}
|
||||
|
||||
so.append(currencyData);
|
||||
@@ -277,10 +390,11 @@ var STCurrency = new SerializedType({
|
||||
var currency = Currency.from_bytes(bytes);
|
||||
// XXX Disabled check. Theoretically, the Currency class should support any
|
||||
// UInt160 value and consider it valid. But it doesn't, so for the
|
||||
// deserialization to be usable, we need to allow invalid results for now.
|
||||
//if (!currency.is_valid()) {
|
||||
// throw new Error('Invalid currency: '+convertByteArrayToHex(bytes));
|
||||
//}
|
||||
// deserialization to be usable, we need to allow invalid results for
|
||||
// now.
|
||||
// if (!currency.is_valid()) {
|
||||
// throw new Error('Invalid currency: '+convertByteArrayToHex(bytes));
|
||||
// }
|
||||
return currency;
|
||||
}
|
||||
});
|
||||
@@ -288,6 +402,7 @@ var STCurrency = new SerializedType({
|
||||
var STAmount = exports.Amount = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
var amount = Amount.from_json(val);
|
||||
|
||||
if (!amount.is_valid()) {
|
||||
throw new Error('Not a valid Amount object.');
|
||||
}
|
||||
@@ -301,12 +416,12 @@ var STAmount = exports.Amount = new SerializedType({
|
||||
if (amount.is_native()) {
|
||||
var valueHex = value.abs().toString(16);
|
||||
|
||||
if (value.abs().greaterThan(Amount.bi_xns_max)) {
|
||||
if (Amount.strict_mode && value.abs().greaterThan(Amount.bi_xns_max)) {
|
||||
throw new Error('Value out of bounds');
|
||||
}
|
||||
|
||||
// Enforce correct length (64 bits)
|
||||
if (valueHex.length > 16) {
|
||||
if (Amount.strict_mode && valueHex.length > 16) {
|
||||
throw new Error('Value out of bounds');
|
||||
}
|
||||
|
||||
@@ -366,14 +481,14 @@ var STAmount = exports.Amount = new SerializedType({
|
||||
var value_bytes = so.read(8);
|
||||
var is_zero = !(value_bytes[0] & 0x7f);
|
||||
|
||||
for (var i=1; i<8; i++) {
|
||||
for (var i = 1; i < 8; i++) {
|
||||
is_zero = is_zero && !value_bytes[i];
|
||||
}
|
||||
|
||||
var is_negative = !is_zero && !(value_bytes[0] & 0x40);
|
||||
|
||||
if (value_bytes[0] & 0x80) {
|
||||
//non-native
|
||||
// non-native
|
||||
var currency = STCurrency.parse(so);
|
||||
var issuer_bytes = so.read(20);
|
||||
var issuer = UInt160.from_bytes(issuer_bytes);
|
||||
@@ -390,14 +505,14 @@ var STAmount = exports.Amount = new SerializedType({
|
||||
issuer: issuer.to_json(),
|
||||
value: valueString
|
||||
});
|
||||
} else {
|
||||
//native
|
||||
var integer_bytes = value_bytes.slice();
|
||||
integer_bytes[0] &= 0x3f;
|
||||
var integer_hex = utils.arrayToHex(integer_bytes);
|
||||
var value = new BigNumber(integer_hex, 16);
|
||||
return Amount.from_json((is_negative ? '-' : '') + value.toString());
|
||||
}
|
||||
|
||||
// native
|
||||
var integer_bytes = value_bytes.slice();
|
||||
integer_bytes[0] &= 0x3f;
|
||||
var integer_hex = utils.arrayToHex(integer_bytes);
|
||||
var value = new BigNumber(integer_hex, 16);
|
||||
return Amount.from_json((is_negative ? '-' : '') + value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -405,7 +520,6 @@ STAmount.id = 6;
|
||||
|
||||
var STVL = exports.VariableLength = exports.VL = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
|
||||
if (typeof val === 'string') {
|
||||
serializeHex(so, val);
|
||||
} else {
|
||||
@@ -449,21 +563,21 @@ var STAccount = exports.Account = new SerializedType({
|
||||
STAccount.id = 8;
|
||||
|
||||
var STPathSet = exports.PathSet = new SerializedType({
|
||||
typeBoundary: 0xff,
|
||||
typeEnd: 0x00,
|
||||
typeAccount: 0x01,
|
||||
typeCurrency: 0x10,
|
||||
typeIssuer: 0x20,
|
||||
typeBoundary: 0xff,
|
||||
typeEnd: 0x00,
|
||||
typeAccount: 0x01,
|
||||
typeCurrency: 0x10,
|
||||
typeIssuer: 0x20,
|
||||
serialize: function (so, val) {
|
||||
for (var i=0, l=val.length; i<l; i++) {
|
||||
for (var i = 0, l = val.length; i < l; i++) {
|
||||
// Boundary
|
||||
if (i) {
|
||||
STInt8.serialize(so, this.typeBoundary);
|
||||
}
|
||||
|
||||
for (var j=0, l2=val[i].length; j<l2; j++) {
|
||||
for (var j = 0, l2 = val[i].length; j < l2; j++) {
|
||||
var entry = val[i][j];
|
||||
//if (entry.hasOwnProperty('_value')) {entry = entry._value;}
|
||||
// if (entry.hasOwnProperty('_value')) {entry = entry._value;}
|
||||
var type = 0;
|
||||
|
||||
if (entry.account) {
|
||||
@@ -505,41 +619,39 @@ var STPathSet = exports.PathSet = new SerializedType({
|
||||
[]
|
||||
]
|
||||
|
||||
each entry has one or more of the following attributes: amount, currency, issuer.
|
||||
each entry has one or more of the following attributes:
|
||||
amount, currency, issuer.
|
||||
*/
|
||||
|
||||
var path_list = [];
|
||||
var path_list = [];
|
||||
var current_path = [];
|
||||
var tag_byte;
|
||||
|
||||
/* eslint-disable no-cond-assign */
|
||||
|
||||
while ((tag_byte = so.read(1)[0]) !== this.typeEnd) {
|
||||
//TODO: try/catch this loop, and catch when we run out of data without reaching the end of the data structure.
|
||||
//Now determine: is this an end, boundary, or entry-begin-tag?
|
||||
//console.log('Tag byte:', tag_byte);
|
||||
// TODO: try/catch this loop, and catch when we run out of data without
|
||||
// reaching the end of the data structure.
|
||||
// Now determine: is this an end, boundary, or entry-begin-tag?
|
||||
// console.log('Tag byte:', tag_byte);
|
||||
if (tag_byte === this.typeBoundary) {
|
||||
//console.log('Boundary');
|
||||
if (current_path) { //close the current path, if there is one,
|
||||
if (current_path) { // close the current path, if there is one,
|
||||
path_list.push(current_path);
|
||||
}
|
||||
current_path = [ ]; //and start a new one.
|
||||
current_path = [ ]; // and start a new one.
|
||||
continue;
|
||||
}
|
||||
|
||||
//It's an entry-begin tag.
|
||||
//console.log('It's an entry-begin tag.');
|
||||
// It's an entry-begin tag.
|
||||
var entry = {};
|
||||
var type = 0;
|
||||
|
||||
if (tag_byte & this.typeAccount) {
|
||||
//console.log('entry.account');
|
||||
/*var bta = so.read(20);
|
||||
console.log('BTA:', bta);*/
|
||||
entry.account = STHash160.parse(so);
|
||||
entry.account.set_version(Base.VER_ACCOUNT_ID);
|
||||
type = type | this.typeAccount;
|
||||
}
|
||||
if (tag_byte & this.typeCurrency) {
|
||||
//console.log('entry.currency');
|
||||
entry.currency = STCurrency.parse(so);
|
||||
if (entry.currency.to_json() === 'XRP' && !entry.currency.is_native()) {
|
||||
entry.non_native = true;
|
||||
@@ -547,27 +659,24 @@ var STPathSet = exports.PathSet = new SerializedType({
|
||||
type = type | this.typeCurrency;
|
||||
}
|
||||
if (tag_byte & this.typeIssuer) {
|
||||
//console.log('entry.issuer');
|
||||
entry.issuer = STHash160.parse(so);
|
||||
// Enable and set correct type of base-58 encoding
|
||||
entry.issuer.set_version(Base.VER_ACCOUNT_ID);
|
||||
//console.log('DONE WITH ISSUER!');
|
||||
|
||||
type = type | this.typeIssuer;
|
||||
}
|
||||
|
||||
if (entry.account || entry.currency || entry.issuer) {
|
||||
entry.type = type;
|
||||
entry.type_hex = ('000000000000000' + type.toString(16)).slice(-16);
|
||||
|
||||
current_path.push(entry);
|
||||
} else {
|
||||
throw new Error('Invalid path entry'); //It must have at least something in it.
|
||||
// It must have at least something in it.
|
||||
throw new Error('Invalid path entry');
|
||||
}
|
||||
}
|
||||
|
||||
if (current_path) {
|
||||
//close the current path, if there is one,
|
||||
// close the current path, if there is one,
|
||||
path_list.push(current_path);
|
||||
}
|
||||
|
||||
@@ -578,9 +687,10 @@ var STPathSet = exports.PathSet = new SerializedType({
|
||||
STPathSet.id = 18;
|
||||
|
||||
var STVector256 = exports.Vector256 = new SerializedType({
|
||||
serialize: function (so, val) { //Assume val is an array of STHash256 objects.
|
||||
serialize: function(so, val) {
|
||||
// Assume val is an array of STHash256 objects.
|
||||
SerializedType.serialize_varint(so, val.length * 32);
|
||||
for (var i=0, l=val.length; i<l; i++) {
|
||||
for (var i = 0, l = val.length; i < l; i++) {
|
||||
STHash256.serialize(so, val[i]);
|
||||
}
|
||||
},
|
||||
@@ -588,7 +698,7 @@ var STVector256 = exports.Vector256 = new SerializedType({
|
||||
var length = this.parse_varint(so);
|
||||
var output = [];
|
||||
// length is number of bytes not number of Hash256
|
||||
for (var i=0; i<length / 32; i++) {
|
||||
for (var i = 0; i < length / 32; i++) {
|
||||
output.push(STHash256.parse(so));
|
||||
}
|
||||
return output;
|
||||
@@ -600,7 +710,6 @@ STVector256.id = 19;
|
||||
// Internal
|
||||
exports.STMemo = new SerializedType({
|
||||
serialize: function(so, val, no_marker) {
|
||||
|
||||
var keys = [];
|
||||
|
||||
Object.keys(val).forEach(function (key) {
|
||||
@@ -620,26 +729,24 @@ exports.STMemo = new SerializedType({
|
||||
// Sort fields
|
||||
keys = sort_fields(keys);
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var key = keys[i];
|
||||
var value = val[key];
|
||||
serialize(so, key, value);
|
||||
}
|
||||
keys.forEach(function(key) {
|
||||
serialize(so, key, val[key]);
|
||||
});
|
||||
|
||||
if (!no_marker) {
|
||||
//Object ending marker
|
||||
// Object ending marker
|
||||
STInt8.serialize(so, 0xe1);
|
||||
}
|
||||
|
||||
},
|
||||
parse: function(so) {
|
||||
var output = {};
|
||||
|
||||
while (so.peek(1)[0] !== 0xe1) {
|
||||
var keyval = parse(so);
|
||||
output[keyval[0]] = keyval[1];
|
||||
}
|
||||
|
||||
if (output.MemoType !== void(0)) {
|
||||
if (output.MemoType !== undefined) {
|
||||
try {
|
||||
var parsedType = convertHexToString(output.MemoType);
|
||||
|
||||
@@ -647,34 +754,39 @@ exports.STMemo = new SerializedType({
|
||||
output.parsed_memo_type = parsedType;
|
||||
}
|
||||
} catch (e) {
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8 string
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_type field
|
||||
}
|
||||
}
|
||||
|
||||
if (output.MemoFormat !== void(0)) {
|
||||
if (output.MemoFormat !== undefined) {
|
||||
try {
|
||||
output.parsed_memo_format = convertHexToString(output.MemoFormat);
|
||||
} catch (e) {
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8 string
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_format field
|
||||
}
|
||||
}
|
||||
|
||||
if (output.MemoData !== void(0)) {
|
||||
if (output.MemoData !== undefined) {
|
||||
|
||||
try {
|
||||
if (output.parsed_memo_format === 'json') {
|
||||
// see if we can parse JSON
|
||||
output.parsed_memo_data = JSON.parse(convertHexToString(output.MemoData));
|
||||
output.parsed_memo_data =
|
||||
JSON.parse(convertHexToString(output.MemoData));
|
||||
|
||||
} else if(output.parsed_memo_format === 'text') {
|
||||
} else if (output.parsed_memo_format === 'text') {
|
||||
// otherwise see if we can parse text
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
} catch(e) {
|
||||
// we'll fail in case the content does not match what the MemoFormat described
|
||||
// this is fine, we won't add the parsed_memo_data, the user has to parse themselves
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
// described
|
||||
// this is fine, we won't add the parsed_memo_data, the user has to
|
||||
// parse themselves
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,98 +796,6 @@ exports.STMemo = new SerializedType({
|
||||
|
||||
});
|
||||
|
||||
exports.serialize = exports.serialize_whatever = serialize;
|
||||
|
||||
function serialize(so, field_name, value) {
|
||||
//so: a byte-stream to serialize into.
|
||||
//field_name: a string for the field name ('LedgerEntryType' etc.)
|
||||
//value: the value of that field.
|
||||
var field_coordinates = binformat.fieldsInverseMap[field_name];
|
||||
var type_bits = field_coordinates[0];
|
||||
var field_bits = field_coordinates[1];
|
||||
var tag_byte = (type_bits < 16 ? type_bits << 4 : 0) | (field_bits < 16 ? field_bits : 0);
|
||||
|
||||
if (field_name === 'LedgerEntryType' && 'string' === typeof value) {
|
||||
value = binformat.ledger[value][0];
|
||||
}
|
||||
|
||||
if (field_name === 'TransactionResult' && 'string' === typeof value) {
|
||||
value = binformat.ter[value];
|
||||
}
|
||||
|
||||
STInt8.serialize(so, tag_byte);
|
||||
|
||||
if (type_bits >= 16) {
|
||||
STInt8.serialize(so, type_bits);
|
||||
}
|
||||
|
||||
if (field_bits >= 16) {
|
||||
STInt8.serialize(so, field_bits);
|
||||
}
|
||||
|
||||
// Get the serializer class (ST...)
|
||||
var serialized_object_type;
|
||||
if (field_name === 'Memo' && typeof value === 'object') {
|
||||
// for Memo we override the default behavior with our STMemo serializer
|
||||
serialized_object_type = exports.STMemo;
|
||||
} else {
|
||||
// for a field based on the type bits.
|
||||
serialized_object_type = exports[binformat.types[type_bits]];
|
||||
}
|
||||
|
||||
try {
|
||||
serialized_object_type.serialize(so, value);
|
||||
} catch (e) {
|
||||
e.message += ' (' + field_name + ')';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
//Take the serialized object, figure out what type/field it is, and return the parsing of that.
|
||||
exports.parse = exports.parse_whatever = parse;
|
||||
|
||||
function parse(so) {
|
||||
var tag_byte = so.read(1)[0];
|
||||
var type_bits = tag_byte >> 4;
|
||||
|
||||
if (type_bits === 0) {
|
||||
type_bits = so.read(1)[0];
|
||||
}
|
||||
|
||||
|
||||
var field_bits = tag_byte & 0x0f;
|
||||
var field_name = (field_bits === 0)
|
||||
? field_name = binformat.fields[type_bits][so.read(1)[0]]
|
||||
: field_name = binformat.fields[type_bits][field_bits];
|
||||
|
||||
assert(field_name, 'Unknown field - header byte is 0x' + tag_byte.toString(16));
|
||||
|
||||
// Get the parser class (ST...) for a field based on the type bits.
|
||||
var type = (field_name === 'Memo')
|
||||
? exports.STMemo
|
||||
: exports[binformat.types[type_bits]];
|
||||
|
||||
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
|
||||
|
||||
return [ field_name, type.parse(so) ]; //key, value
|
||||
}
|
||||
|
||||
function sort_fields(keys) {
|
||||
function sort_field_compare(a, b) {
|
||||
var a_field_coordinates = binformat.fieldsInverseMap[a];
|
||||
var a_type_bits = a_field_coordinates[0];
|
||||
var a_field_bits = a_field_coordinates[1];
|
||||
var b_field_coordinates = binformat.fieldsInverseMap[b];
|
||||
var b_type_bits = b_field_coordinates[0];
|
||||
var b_field_bits = b_field_coordinates[1];
|
||||
|
||||
// Sort by type id first, then by field id
|
||||
return a_type_bits !== b_type_bits ? a_type_bits - b_type_bits : a_field_bits - b_field_bits;
|
||||
}
|
||||
|
||||
return keys.sort(sort_field_compare);
|
||||
}
|
||||
|
||||
var STObject = exports.Object = new SerializedType({
|
||||
serialize: function (so, val, no_marker) {
|
||||
var keys = [];
|
||||
@@ -797,12 +817,12 @@ var STObject = exports.Object = new SerializedType({
|
||||
// Sort fields
|
||||
keys = sort_fields(keys);
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
serialize(so, keys[i], val[keys[i]]);
|
||||
}
|
||||
|
||||
if (!no_marker) {
|
||||
//Object ending marker
|
||||
// Object ending marker
|
||||
STInt8.serialize(so, 0xe1);
|
||||
}
|
||||
},
|
||||
@@ -822,11 +842,12 @@ STObject.id = 14;
|
||||
|
||||
var STArray = exports.Array = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
for (var i=0, l=val.length; i<l; i++) {
|
||||
for (var i = 0, l = val.length; i < l; i++) {
|
||||
var keys = Object.keys(val[i]);
|
||||
|
||||
if (keys.length !== 1) {
|
||||
throw Error('Cannot serialize an array containing non-single-key objects');
|
||||
throw new Error(
|
||||
'Cannot serialize an array containing non-single-key objects');
|
||||
}
|
||||
|
||||
var field_name = keys[0];
|
||||
@@ -834,7 +855,7 @@ var STArray = exports.Array = new SerializedType({
|
||||
serialize(so, field_name, value);
|
||||
}
|
||||
|
||||
//Array ending marker
|
||||
// Array ending marker
|
||||
STInt8.serialize(so, 0xf1);
|
||||
},
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var RangeSet = require('./rangeset').RangeSet;
|
||||
var log = require('./log').internal.sub('server');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* @constructor Server
|
||||
@@ -424,9 +423,7 @@ Server.prototype.connect = function() {
|
||||
log.info(this.getServerID(), 'connect');
|
||||
}
|
||||
|
||||
var ws = this._ws = new WebSocket(this._opts.url, {
|
||||
headers: { 'User-Agent': 'ripple-lib/' + utils.getPackageVersion() }
|
||||
});
|
||||
var ws = this._ws = new WebSocket(this._opts.url);
|
||||
|
||||
this._shouldConnect = true;
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ Transaction.set_clear_flags = {
|
||||
asfDisableMaster: 4,
|
||||
asfAccountTxnID: 5,
|
||||
asfNoFreeze: 6,
|
||||
asfGlobalFreeze: 7
|
||||
asfGlobalFreeze: 7,
|
||||
asfDefaultRipple: 8
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var RippleError = require('./rippleerror').RippleError;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var RippleError = require('./rippleerror').RippleError;
|
||||
var PendingQueue = require('./transactionqueue').TransactionQueue;
|
||||
var log = require('./log').internal.sub('transactionmanager');
|
||||
var log = require('./log').internal.sub('transactionmanager');
|
||||
|
||||
/**
|
||||
* @constructor TransactionManager
|
||||
@@ -20,7 +22,7 @@ function TransactionManager(account) {
|
||||
this._account = account;
|
||||
this._accountID = account._account_id;
|
||||
this._remote = account._remote;
|
||||
this._nextSequence = void(0);
|
||||
this._nextSequence = undefined;
|
||||
this._maxFee = this._remote.max_fee;
|
||||
this._maxAttempts = this._remote.max_attempts;
|
||||
this._submissionTimeout = this._remote.submission_timeout;
|
||||
@@ -37,7 +39,7 @@ function TransactionManager(account) {
|
||||
|
||||
function updatePendingStatus(ledger) {
|
||||
self._updatePendingStatus(ledger);
|
||||
};
|
||||
}
|
||||
|
||||
this._remote.on('ledger_closed', updatePendingStatus);
|
||||
|
||||
@@ -47,7 +49,7 @@ function TransactionManager(account) {
|
||||
// hooking back into ledger_closed
|
||||
self._remote.on('ledger_closed', updatePendingStatus);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this._remote.on('disconnect', function() {
|
||||
self._remote.removeListener('ledger_closed', updatePendingStatus);
|
||||
@@ -56,7 +58,7 @@ function TransactionManager(account) {
|
||||
|
||||
// Query server for next account transaction sequence
|
||||
this._loadSequence();
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(TransactionManager, EventEmitter);
|
||||
|
||||
@@ -96,7 +98,7 @@ TransactionManager.normalizeTransaction = function(tx) {
|
||||
var transaction = { };
|
||||
var keys = Object.keys(tx);
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var k = keys[i];
|
||||
switch (k) {
|
||||
case 'transaction':
|
||||
@@ -162,7 +164,8 @@ TransactionManager.prototype._transactionReceived = function(tx) {
|
||||
|
||||
if (!(submission instanceof Transaction)) {
|
||||
// The received transaction does not correlate to one submitted
|
||||
return this._pending.addReceivedId(hash, transaction);
|
||||
this._pending.addReceivedId(hash, transaction);
|
||||
return;
|
||||
}
|
||||
|
||||
// ND: A `success` handler will `finalize` this later
|
||||
@@ -197,7 +200,7 @@ TransactionManager.prototype._adjustFees = function() {
|
||||
transaction.once('presubmit', function() {
|
||||
transaction.emit('error', 'tejMaxFeeExceeded');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this._pending.forEach(function(transaction) {
|
||||
if (transaction._setFixedFee) {
|
||||
@@ -209,7 +212,8 @@ TransactionManager.prototype._adjustFees = function() {
|
||||
|
||||
if (Number(newFee) > self._maxFee) {
|
||||
// Max transaction fee exceeded, abort submission
|
||||
return maxFeeExceeded(transaction);
|
||||
maxFeeExceeded(transaction);
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.tx_json.Fee = newFee;
|
||||
@@ -244,6 +248,10 @@ TransactionManager.prototype._updatePendingStatus = function(ledger) {
|
||||
assert.strictEqual(typeof ledger.ledger_index, 'number');
|
||||
|
||||
this._pending.forEach(function(transaction) {
|
||||
if (transaction.finalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ledger.ledger_index - transaction.submitIndex) {
|
||||
case 4:
|
||||
transaction.emit('missing', ledger);
|
||||
@@ -253,10 +261,6 @@ TransactionManager.prototype._updatePendingStatus = function(ledger) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (transaction.finalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ledger.ledger_index > transaction.tx_json.LastLedgerSequence) {
|
||||
// Transaction must fail
|
||||
transaction.emit('error', new RippleError(
|
||||
@@ -265,46 +269,54 @@ TransactionManager.prototype._updatePendingStatus = function(ledger) {
|
||||
});
|
||||
};
|
||||
|
||||
//Fill an account transaction sequence
|
||||
// Fill an account transaction sequence
|
||||
TransactionManager.prototype._fillSequence = function(tx, callback) {
|
||||
var self = this;
|
||||
|
||||
function submitFill(sequence, callback) {
|
||||
var fill = self._remote.transaction();
|
||||
fill.account_set(self._accountID);
|
||||
fill.tx_json.Sequence = sequence;
|
||||
fill.once('submitted', callback);
|
||||
function submitFill(sequence, fCallback) {
|
||||
var fillTransaction = self._remote.createTransaction('AccountSet', {
|
||||
account: self._accountID
|
||||
});
|
||||
fillTransaction.tx_json.Sequence = sequence;
|
||||
|
||||
// Secrets may be set on a per-transaction basis
|
||||
if (tx._secret) {
|
||||
fill.secret(tx._secret);
|
||||
fillTransaction.secret(tx._secret);
|
||||
}
|
||||
|
||||
fill.submit();
|
||||
};
|
||||
fillTransaction.once('submitted', fCallback);
|
||||
fillTransaction.submit();
|
||||
}
|
||||
|
||||
function sequenceLoaded(err, sequence) {
|
||||
if (typeof sequence !== 'number') {
|
||||
return callback(new Error('Failed to fetch account transaction sequence'));
|
||||
log.info('fill sequence: failed to fetch account transaction sequence');
|
||||
return callback();
|
||||
}
|
||||
|
||||
var sequenceDif = tx.tx_json.Sequence - sequence;
|
||||
var sequenceDiff = tx.tx_json.Sequence - sequence;
|
||||
var submitted = 0;
|
||||
|
||||
;(function nextFill(sequence) {
|
||||
if (sequence >= tx.tx_json.Sequence) {
|
||||
return;
|
||||
}
|
||||
|
||||
submitFill(sequence, function() {
|
||||
if (++submitted === sequenceDif) {
|
||||
async.whilst(
|
||||
function() {
|
||||
return submitted < sequenceDiff;
|
||||
},
|
||||
function(asyncCallback) {
|
||||
submitFill(sequence, function(res) {
|
||||
++submitted;
|
||||
if (res.engine_result === 'tesSUCCESS') {
|
||||
self.emit('sequence_filled', err);
|
||||
}
|
||||
asyncCallback();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
if (callback) {
|
||||
callback();
|
||||
} else {
|
||||
nextFill(sequence + 1);
|
||||
}
|
||||
});
|
||||
})(sequence);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this._loadSequence(sequenceLoaded);
|
||||
};
|
||||
@@ -318,7 +330,7 @@ TransactionManager.prototype._fillSequence = function(tx, callback) {
|
||||
|
||||
TransactionManager.prototype._loadSequence = function(callback) {
|
||||
var self = this;
|
||||
var callback = (typeof callback === 'function') ? callback : function(){};
|
||||
callback = (typeof callback === 'function') ? callback : function() {};
|
||||
|
||||
function sequenceLoaded(err, sequence) {
|
||||
if (err || typeof sequence !== 'number') {
|
||||
@@ -331,7 +343,7 @@ TransactionManager.prototype._loadSequence = function(callback) {
|
||||
self._nextSequence = sequence;
|
||||
self.emit('sequence_loaded', sequence);
|
||||
callback(err, sequence);
|
||||
};
|
||||
}
|
||||
|
||||
this._account.getNextSequence(sequenceLoaded);
|
||||
};
|
||||
@@ -346,10 +358,11 @@ TransactionManager.prototype._loadSequence = function(callback) {
|
||||
|
||||
TransactionManager.prototype._handleReconnect = function(callback) {
|
||||
var self = this;
|
||||
var callback = (typeof callback === 'function') ? callback : function(){};
|
||||
callback = (typeof callback === 'function') ? callback : function() {};
|
||||
|
||||
if (!this._pending.length()) {
|
||||
return callback();
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
function handleTransactions(err, transactions) {
|
||||
@@ -372,7 +385,7 @@ TransactionManager.prototype._handleReconnect = function(callback) {
|
||||
// Resubmit pending transactions after sequence is loaded
|
||||
self._resubmit();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var options = {
|
||||
account: this._accountID,
|
||||
@@ -410,7 +423,7 @@ TransactionManager.prototype._waitLedgers = function(ledgers, callback) {
|
||||
self._remote.removeListener('ledger_closed', ledgerClosed);
|
||||
callback();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this._remote.on('ledger_closed', ledgerClosed);
|
||||
};
|
||||
@@ -426,8 +439,16 @@ TransactionManager.prototype._waitLedgers = function(ledgers, callback) {
|
||||
|
||||
TransactionManager.prototype._resubmit = function(ledgers, pending) {
|
||||
var self = this;
|
||||
var ledgers = ledgers || 0;
|
||||
var pending = pending ? [ pending ] : this._pending;
|
||||
|
||||
if (ledgers && typeof ledgers !== 'number') {
|
||||
pending = ledgers;
|
||||
ledgers = 0;
|
||||
}
|
||||
|
||||
ledgers = ledgers || 0;
|
||||
pending = pending instanceof Transaction
|
||||
? [pending]
|
||||
: this.getPending().getQueue();
|
||||
|
||||
function resubmitTransaction(transaction, next) {
|
||||
if (!transaction || transaction.finalized) {
|
||||
@@ -449,7 +470,7 @@ TransactionManager.prototype._resubmit = function(ledgers, pending) {
|
||||
}
|
||||
|
||||
while (self._pending.hasSequence(transaction.tx_json.Sequence)) {
|
||||
//Sequence number has been consumed by another transaction
|
||||
// Sequence number has been consumed by another transaction
|
||||
transaction.tx_json.Sequence += 1;
|
||||
|
||||
if (self._remote.trace) {
|
||||
@@ -467,7 +488,7 @@ TransactionManager.prototype._resubmit = function(ledgers, pending) {
|
||||
});
|
||||
|
||||
self._request(transaction);
|
||||
};
|
||||
}
|
||||
|
||||
this._waitLedgers(ledgers, function() {
|
||||
async.eachSeries(pending, resubmitTransaction);
|
||||
@@ -528,8 +549,8 @@ TransactionManager.prototype._request = function(tx) {
|
||||
}
|
||||
|
||||
if (tx.attempts > 0 && !remote.local_signing) {
|
||||
var message = 'Automatic resubmission requires local signing';
|
||||
tx.emit('error', new RippleError('tejLocalSigningRequired', message));
|
||||
var errMessage = 'Automatic resubmission requires local signing';
|
||||
tx.emit('error', new RippleError('tejLocalSigningRequired', errMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -542,22 +563,22 @@ TransactionManager.prototype._request = function(tx) {
|
||||
// Transaction may succeed after Sequence is updated
|
||||
self._resubmit(1, tx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function transactionRetry(message) {
|
||||
function transactionRetry() {
|
||||
// XXX This may no longer be necessary. Instead, update sequence numbers
|
||||
// after a transaction fails definitively
|
||||
self._fillSequence(tx, function() {
|
||||
self._resubmit(1, tx);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function transactionFailedLocal(message) {
|
||||
if (message.engine_result === 'telINSUF_FEE_P') {
|
||||
// Transaction may succeed after Fee is updated
|
||||
self._resubmit(1, tx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function submissionError(error) {
|
||||
// Either a tem-class error or generic server error such as tooBusy. This
|
||||
@@ -568,7 +589,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
self._nextSequence--;
|
||||
tx.emit('error', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function submitted(message) {
|
||||
if (tx.finalized) {
|
||||
@@ -611,7 +632,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
// tem
|
||||
submissionError(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function requestTimeout() {
|
||||
// ND: What if the response is just slow and we get a response that
|
||||
@@ -630,7 +651,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
}
|
||||
self._resubmit(1, tx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tx.submitIndex = this._remote._ledger_current_index;
|
||||
|
||||
@@ -661,10 +682,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
|
||||
tx.emit('postsubmit');
|
||||
|
||||
//XXX
|
||||
submitRequest.timeout(self._submissionTimeout, requestTimeout);
|
||||
|
||||
return submitRequest;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -675,7 +693,6 @@ TransactionManager.prototype._request = function(tx) {
|
||||
|
||||
TransactionManager.prototype.submit = function(tx) {
|
||||
var self = this;
|
||||
var remote = this._remote;
|
||||
|
||||
if (typeof this._nextSequence !== 'number') {
|
||||
// If sequence number is not yet known, defer until it is.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var lodash = require('lodash');
|
||||
var LRU = require('lru-cache');
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
|
||||
@@ -7,9 +10,9 @@ var Transaction = require('./transaction').Transaction;
|
||||
|
||||
function TransactionQueue() {
|
||||
this._queue = [ ];
|
||||
this._idCache = LRU({ max: 200 });
|
||||
this._sequenceCache = LRU({ max: 200 });
|
||||
};
|
||||
this._idCache = new LRU({max: 200});
|
||||
this._sequenceCache = new LRU({max: 200});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store received (validated) sequence
|
||||
@@ -64,16 +67,9 @@ TransactionQueue.prototype.getReceived = function(id) {
|
||||
*/
|
||||
|
||||
TransactionQueue.prototype.getSubmission = function(id) {
|
||||
var result = void(0);
|
||||
|
||||
for (var i=0, tx; (tx=this._queue[i]); i++) {
|
||||
if (~tx.submittedIDs.indexOf(id)) {
|
||||
result = tx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return lodash.find(this._queue, function(tx) {
|
||||
return lodash.contains(tx.submittedIDs, id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,11 +79,15 @@ TransactionQueue.prototype.getSubmission = function(id) {
|
||||
*/
|
||||
|
||||
TransactionQueue.prototype.getMinLedger = function() {
|
||||
if (this.length() < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var result = Infinity;
|
||||
|
||||
for (var i=0, tx; (tx=this._queue[i]); i++) {
|
||||
if (tx.initialSubmitIndex < result) {
|
||||
result = tx.initialSubmitIndex;
|
||||
for (var i = 0; i < this.length(); i++) {
|
||||
if (this._queue[i].initialSubmitIndex < result) {
|
||||
result = this._queue[i].initialSubmitIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,4 +153,12 @@ TransactionQueue.prototype.getLength = function() {
|
||||
return this._queue.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array} pending queue
|
||||
*/
|
||||
|
||||
TransactionQueue.prototype.getQueue = function() {
|
||||
return this._queue;
|
||||
};
|
||||
|
||||
exports.TransactionQueue = TransactionQueue;
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
var packageJson = require('../../../package.json');
|
||||
|
||||
function getPackageVersion() {
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function getMantissaDecimalString(bignum) {
|
||||
var mantissa = bignum.toPrecision(16)
|
||||
@@ -175,7 +170,6 @@ exports.arrayUnique = arrayUnique;
|
||||
exports.toTimestamp = toTimestamp;
|
||||
exports.fromTimestamp = fromTimestamp;
|
||||
exports.getMantissaDecimalString = getMantissaDecimalString;
|
||||
exports.getPackageVersion = getPackageVersion;
|
||||
|
||||
// Going up three levels is needed to escape the src-cov folder used for the
|
||||
// test coverage stuff.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
var assert = require('assert');
|
||||
var Amount = require('ripple-lib').Amount;
|
||||
var UInt160 = require('ripple-lib').UInt160;
|
||||
/* eslint-disable max-len */
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var Amount = require('ripple-lib').Amount;
|
||||
var UInt160 = require('ripple-lib').UInt160;
|
||||
var load_config = require('ripple-lib').config.load;
|
||||
var config = require('./config-example');
|
||||
var config = require('./config-example');
|
||||
|
||||
load_config(config);
|
||||
|
||||
@@ -26,97 +28,103 @@ describe('Amount', function() {
|
||||
// also tested extensively in other cases
|
||||
describe('to_human', function() {
|
||||
it('12345.6789 XAU', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_human(), '12,345.6789');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 XAU').to_human(), '12,345.6789');
|
||||
});
|
||||
it('12345.678901234 XAU', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human(), '12,345.678901234');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human(), '12,345.678901234');
|
||||
});
|
||||
it('to human, precision -1, should be ignored, precision needs to be >= 0', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:-1}), '12,346');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: -1}), '12,346');
|
||||
});
|
||||
it('to human, precision 0', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:0}), '12,346');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 0}), '12,346');
|
||||
});
|
||||
it('to human, precision 1', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:1}), '12,345.7');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 1}), '12,345.7');
|
||||
});
|
||||
it('to human, precision 2', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:2}), '12,345.68');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 2}), '12,345.68');
|
||||
});
|
||||
it('to human, precision 3', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:3}), '12,345.679');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 3}), '12,345.679');
|
||||
});
|
||||
it('to human, precision 4', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:4}), '12,345.6789');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 4}), '12,345.6789');
|
||||
});
|
||||
it('to human, precision 5', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:5}), '12,345.67890');
|
||||
assert.strictEqual(Amount.from_human('12345.678901234 XAU').to_human({precision: 5}), '12,345.67890');
|
||||
});
|
||||
it('to human, precision -1, should be ignored, precision needs to be >= 0', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:-1}), '0');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: -1}), '0');
|
||||
});
|
||||
it('to human, precision 0', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:0}), '0');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 0}), '0');
|
||||
});
|
||||
it('to human, precision 1', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:1}), '0.0');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 1}), '0.0');
|
||||
});
|
||||
it('to human, precision 2', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:2}), '0.00');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 2}), '0.00');
|
||||
});
|
||||
it('to human, precision 5', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:5}), '0.00012');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 5}), '0.00012');
|
||||
});
|
||||
it('to human, precision 6', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:6}), '0.000123');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 6}), '0.000123');
|
||||
});
|
||||
it('to human, precision 16', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16}), '0.00012345');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 16}), '0.00012345');
|
||||
});
|
||||
it('to human, precision 16, min_precision 16', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16, min_precision:16}), '0.0001234500000000');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 16, min_precision: 16}), '0.0001234500000000');
|
||||
});
|
||||
it('to human, precision 16, min_precision 12', function() {
|
||||
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16, min_precision:12}), '0.000123450000');
|
||||
assert.strictEqual(Amount.from_human('0.00012345 XAU').to_human({precision: 16, min_precision: 12}), '0.000123450000');
|
||||
});
|
||||
it('to human, precision 0, first decimal 4', function() {
|
||||
assert.strictEqual(Amount.from_human("0.4 XAU").to_human({precision:0}), '0');
|
||||
assert.strictEqual(Amount.from_human('0.4 XAU').to_human({precision: 0}), '0');
|
||||
});
|
||||
it('to human, precision 0, first decimal 5', function() {
|
||||
assert.strictEqual(Amount.from_human("0.5 XAU").to_human({precision:0}), '1');
|
||||
assert.strictEqual(Amount.from_human('0.5 XAU').to_human({precision: 0}), '1');
|
||||
});
|
||||
it('to human, precision 0, first decimal 8', function() {
|
||||
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
|
||||
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');
|
||||
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');
|
||||
assert.strictEqual(Amount.from_human('0.0 XAU').to_human({precision: 8, min_precision: 16}), '0.0000000000000000');
|
||||
});
|
||||
it('to human, precision 0, first decimal 8', function() {
|
||||
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
|
||||
assert.strictEqual(Amount.from_human('0.8 XAU').to_human({precision: 0}), '1');
|
||||
});
|
||||
it('to human, precision 6, min_precision 6, max_sig_digits 20', function() {
|
||||
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision: 6, min_precision: 6, max_sig_digits: 20}), '0.000000');
|
||||
assert.strictEqual(Amount.from_human('0.0 XAU').to_human({precision: 6, min_precision: 6, max_sig_digits: 20}), '0.000000');
|
||||
});
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
assert.strictEqual(Amount.from_human('0.999 XAU').to_human({precision: 2}), '1.00');
|
||||
});
|
||||
it('to human very small number', function() {
|
||||
assert.strictEqual(Amount.from_json('12e-20/USD').to_human(), '0.00000000000000000012');
|
||||
});
|
||||
it('to human very small number with precision', function() {
|
||||
assert.strictEqual(Amount.from_json('12e-20/USD').to_human({precision: 20}), '0.00000000000000000012');
|
||||
});
|
||||
});
|
||||
describe('from_human', function() {
|
||||
@@ -127,117 +135,117 @@ describe('Amount', function() {
|
||||
assert.strictEqual(Amount.from_human('USD').to_text_full(), 'NaN');
|
||||
});
|
||||
it('1 XRP', function() {
|
||||
assert.strictEqual(Amount.from_human("1 XRP").to_text_full(), '1/XRP');
|
||||
assert.strictEqual(Amount.from_human('1 XRP').to_text_full(), '1/XRP');
|
||||
});
|
||||
it('1 XRP human', function() {
|
||||
assert.strictEqual(Amount.from_human("1 XRP").to_human_full(), '1/XRP');
|
||||
assert.strictEqual(Amount.from_human('1 XRP').to_human_full(), '1/XRP');
|
||||
});
|
||||
it('1XRP human', function() {
|
||||
assert.strictEqual(Amount.from_human('1XRP').to_human_full(), '1/XRP');
|
||||
});
|
||||
it('0.1 XRP', function() {
|
||||
assert.strictEqual(Amount.from_human("0.1 XRP").to_text_full(), '0.1/XRP');
|
||||
assert.strictEqual(Amount.from_human('0.1 XRP').to_text_full(), '0.1/XRP');
|
||||
});
|
||||
it('0.1 XRP human', function() {
|
||||
assert.strictEqual(Amount.from_human("0.1 XRP").to_human_full(), '0.1/XRP');
|
||||
assert.strictEqual(Amount.from_human('0.1 XRP').to_human_full(), '0.1/XRP');
|
||||
});
|
||||
it('0.1 USD', function() {
|
||||
assert.strictEqual(Amount.from_human("0.1 USD").to_text_full(), '0.1/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('0.1 USD').to_text_full(), '0.1/USD/NaN');
|
||||
});
|
||||
it('0.1 USD human', function() {
|
||||
assert.strictEqual(Amount.from_human("0.1 USD").to_human_full(), '0.1/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('0.1 USD').to_human_full(), '0.1/USD/NaN');
|
||||
});
|
||||
it('10000 USD', function() {
|
||||
assert.strictEqual(Amount.from_human("10000 USD").to_text_full(), '10000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('10000 USD').to_text_full(), '10000/USD/NaN');
|
||||
});
|
||||
it('10000 USD human', function() {
|
||||
assert.strictEqual(Amount.from_human("10000 USD").to_human_full(), '10,000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('10000 USD').to_human_full(), '10,000/USD/NaN');
|
||||
});
|
||||
it('USD 10000', function() {
|
||||
assert.strictEqual(Amount.from_human("USD 10000").to_text_full(), '10000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('USD 10000').to_text_full(), '10000/USD/NaN');
|
||||
});
|
||||
it('USD 10000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("USD 10000").to_human_full(), '10,000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('USD 10000').to_human_full(), '10,000/USD/NaN');
|
||||
});
|
||||
it('12345.6789 XAU', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_text_full(), '12345.6789/XAU/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 XAU').to_text_full(), '12345.6789/XAU/NaN');
|
||||
});
|
||||
it('12345.6789 XAU human', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_human_full(), '12,345.6789/XAU/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 XAU').to_human_full(), '12,345.6789/XAU/NaN');
|
||||
});
|
||||
it('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000").to_text_full(), '12345.6789/XAU (-0.5%pa)/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000').to_text_full(), '12345.6789/XAU (-0.5%pa)/NaN');
|
||||
});
|
||||
it('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human_full(), '12,345.6789/XAU (-0.5%pa)/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000').to_human_full(), '12,345.6789/XAU (-0.5%pa)/NaN');
|
||||
});
|
||||
it('12345.6789 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 0000000000000000000000005553440000000000").to_text_full(), '12345.6789/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 0000000000000000000000005553440000000000').to_text_full(), '12345.6789/USD/NaN');
|
||||
});
|
||||
it('12345.6789 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 0000000000000000000000005553440000000000").to_human_full(), '12,345.6789/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 0000000000000000000000005553440000000000').to_human_full(), '12,345.6789/USD/NaN');
|
||||
});
|
||||
it('10 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("10 0000000000000000000000005553440000000000").to_text_full(), '10/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('10 0000000000000000000000005553440000000000').to_text_full(), '10/USD/NaN');
|
||||
});
|
||||
it('10 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("10 0000000000000000000000005553440000000000").to_human_full(), '10/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('10 0000000000000000000000005553440000000000').to_human_full(), '10/USD/NaN');
|
||||
});
|
||||
it('100 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("100 0000000000000000000000005553440000000000").to_text_full(), '100/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('100 0000000000000000000000005553440000000000').to_text_full(), '100/USD/NaN');
|
||||
});
|
||||
it('100 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("100 0000000000000000000000005553440000000000").to_human_full(), '100/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('100 0000000000000000000000005553440000000000').to_human_full(), '100/USD/NaN');
|
||||
});
|
||||
it('1000 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("1000 0000000000000000000000005553440000000000").to_text_full(), '1000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('1000 0000000000000000000000005553440000000000').to_text_full(), '1000/USD/NaN');
|
||||
});
|
||||
it('1000 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("1000 0000000000000000000000005553440000000000").to_human_full(), '1,000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('1000 0000000000000000000000005553440000000000').to_human_full(), '1,000/USD/NaN');
|
||||
});
|
||||
it('-100 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("-100 0000000000000000000000005553440000000000").to_text_full(), '-100/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-100 0000000000000000000000005553440000000000').to_text_full(), '-100/USD/NaN');
|
||||
});
|
||||
it('-100 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("-100 0000000000000000000000005553440000000000").to_human_full(), '-100/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-100 0000000000000000000000005553440000000000').to_human_full(), '-100/USD/NaN');
|
||||
});
|
||||
it('-1000 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("-1000 0000000000000000000000005553440000000000").to_text_full(), '-1000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-1000 0000000000000000000000005553440000000000').to_text_full(), '-1000/USD/NaN');
|
||||
});
|
||||
it('-1000 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("-1000 0000000000000000000000005553440000000000").to_human_full(), '-1,000/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-1000 0000000000000000000000005553440000000000').to_human_full(), '-1,000/USD/NaN');
|
||||
});
|
||||
it('-1000.001 0000000000000000000000005553440000000000', function() {
|
||||
assert.strictEqual(Amount.from_human("-1000.001 0000000000000000000000005553440000000000").to_text_full(), '-1000.001/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-1000.001 0000000000000000000000005553440000000000').to_text_full(), '-1000.001/USD/NaN');
|
||||
});
|
||||
it('-1000.001 0000000000000000000000005553440000000000 human', function() {
|
||||
assert.strictEqual(Amount.from_human("-1000.001 0000000000000000000000005553440000000000").to_human_full(), '-1,000.001/USD/NaN');
|
||||
assert.strictEqual(Amount.from_human('-1000.001 0000000000000000000000005553440000000000').to_human_full(), '-1,000.001/USD/NaN');
|
||||
});
|
||||
it('XAU 12345.6789', function() {
|
||||
assert.strictEqual(Amount.from_human("XAU 12345.6789").to_text_full(), '12345.6789/XAU/NaN');
|
||||
assert.strictEqual(Amount.from_human('XAU 12345.6789').to_text_full(), '12345.6789/XAU/NaN');
|
||||
});
|
||||
it('XAU 12345.6789 human', function() {
|
||||
assert.strictEqual(Amount.from_human("XAU 12345.6789").to_human_full(), '12,345.6789/XAU/NaN');
|
||||
assert.strictEqual(Amount.from_human('XAU 12345.6789').to_human_full(), '12,345.6789/XAU/NaN');
|
||||
});
|
||||
it('101 12345.6789', function() {
|
||||
assert.strictEqual(Amount.from_human("101 12345.6789").to_text_full(), '12345.6789/101/NaN');
|
||||
assert.strictEqual(Amount.from_human('101 12345.6789').to_text_full(), '12345.6789/101/NaN');
|
||||
});
|
||||
it('101 12345.6789 human', function() {
|
||||
assert.strictEqual(Amount.from_human("101 12345.6789").to_human_full(), '12,345.6789/101/NaN');
|
||||
assert.strictEqual(Amount.from_human('101 12345.6789').to_human_full(), '12,345.6789/101/NaN');
|
||||
});
|
||||
it('12345.6789 101', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 101").to_text_full(), '12345.6789/101/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 101').to_text_full(), '12345.6789/101/NaN');
|
||||
});
|
||||
it('12345.6789 101 human', function() {
|
||||
assert.strictEqual(Amount.from_human("12345.6789 101").to_human_full(), '12,345.6789/101/NaN');
|
||||
assert.strictEqual(Amount.from_human('12345.6789 101').to_human_full(), '12,345.6789/101/NaN');
|
||||
});
|
||||
});
|
||||
describe('from_json', function() {
|
||||
it('1 XRP', function() {
|
||||
assert.strictEqual(Amount.from_json("1/XRP").to_text_full(), "1/XRP/NaN");
|
||||
assert.strictEqual(Amount.from_json('1/XRP').to_text_full(), '1/XRP/NaN');
|
||||
});
|
||||
it('1 XRP human', function() {
|
||||
assert.strictEqual(Amount.from_json("1/XRP").to_human_full(), "1/XRP/NaN");
|
||||
assert.strictEqual(Amount.from_json('1/XRP').to_human_full(), '1/XRP/NaN');
|
||||
});
|
||||
});
|
||||
describe('from_number', function() {
|
||||
@@ -302,7 +310,7 @@ describe('Amount', function() {
|
||||
assert.strictEqual(UInt160.ACCOUNT_ONE, UInt160.from_json('rrrrrrrrrrrrrrrrrrrrBZbvji').to_json());
|
||||
});
|
||||
it('Parse mtgox export', function () {
|
||||
assert.strictEqual(config.accounts['mtgox'].account, UInt160.from_json('mtgox').to_json());
|
||||
assert.strictEqual(config.accounts.mtgox.account, UInt160.from_json('mtgox').to_json());
|
||||
});
|
||||
it('is_valid rrrrrrrrrrrrrrrrrrrrrhoLvTp', function () {
|
||||
assert(UInt160.is_valid('rrrrrrrrrrrrrrrrrrrrrhoLvTp'));
|
||||
@@ -353,10 +361,10 @@ describe('Amount', function() {
|
||||
assert.strictEqual(Amount.from_json('10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Parse 800/USD/mtgox', function () {
|
||||
assert.strictEqual('800/USD/'+config.accounts['mtgox'].account, Amount.from_json('800/USD/mtgox').to_text_full());
|
||||
assert.strictEqual('800/USD/' + config.accounts.mtgox.account, Amount.from_json('800/USD/mtgox').to_text_full());
|
||||
});
|
||||
it('Parse 800/USD/mtgox human', function () {
|
||||
assert.strictEqual('800/USD/'+config.accounts['mtgox'].account, Amount.from_json('800/USD/mtgox').to_human_full());
|
||||
assert.strictEqual('800/USD/' + config.accounts.mtgox.account, Amount.from_json('800/USD/mtgox').to_human_full());
|
||||
});
|
||||
it('Parse native 0', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('0').to_text_full());
|
||||
@@ -449,19 +457,19 @@ describe('Amount', function() {
|
||||
});
|
||||
describe('Amount to_json', function() {
|
||||
it('10 USD', function() {
|
||||
var amount = Amount.from_human("10 USD").to_json();
|
||||
assert.strictEqual("10", amount.value);
|
||||
assert.strictEqual("USD", amount.currency);
|
||||
var amount = Amount.from_human('10 USD').to_json();
|
||||
assert.strictEqual('10', amount.value);
|
||||
assert.strictEqual('USD', amount.currency);
|
||||
});
|
||||
it('10 0000000000000000000000005553440000000000', function() {
|
||||
var amount = Amount.from_human("10 0000000000000000000000005553440000000000").to_json();
|
||||
assert.strictEqual("10", amount.value);
|
||||
assert.strictEqual("USD", amount.currency);
|
||||
var amount = Amount.from_human('10 0000000000000000000000005553440000000000').to_json();
|
||||
assert.strictEqual('10', amount.value);
|
||||
assert.strictEqual('USD', amount.currency);
|
||||
});
|
||||
it('10 015841551A748AD2C1F76FF6ECB0CCCD00000000', function() {
|
||||
var amount = Amount.from_human("10 015841551A748AD2C1F76FF6ECB0CCCD00000000").to_json();
|
||||
assert.strictEqual("10", amount.value);
|
||||
assert.strictEqual("015841551A748AD2C1F76FF6ECB0CCCD00000000", amount.currency);
|
||||
var amount = Amount.from_human('10 015841551A748AD2C1F76FF6ECB0CCCD00000000').to_json();
|
||||
assert.strictEqual('10', amount.value);
|
||||
assert.strictEqual('015841551A748AD2C1F76FF6ECB0CCCD00000000', amount.currency);
|
||||
});
|
||||
});
|
||||
describe('Amount operations', function() {
|
||||
@@ -487,7 +495,7 @@ describe('Amount', function() {
|
||||
assert.strictEqual('200.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').add(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Add 0 USD to 1 USD', function() {
|
||||
assert.strictEqual('1' , Amount.from_json('1/USD').add('0/USD').to_text());
|
||||
assert.strictEqual('1', Amount.from_json('1/USD').add('0/USD').to_text());
|
||||
});
|
||||
it('Subtract USD from USD', function() {
|
||||
assert.strictEqual('99.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').subtract(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
@@ -625,7 +633,7 @@ describe('Amount', function() {
|
||||
assert.strictEqual('200.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').add(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
|
||||
});
|
||||
it('Add 0 USD to 1 USD human', function() {
|
||||
assert.strictEqual('1' , Amount.from_json('1/USD').add('0/USD').to_human());
|
||||
assert.strictEqual('1', Amount.from_json('1/USD').add('0/USD').to_human());
|
||||
});
|
||||
it('Subtract USD from USD human', function() {
|
||||
assert.strictEqual('99.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').subtract(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
|
||||
@@ -766,7 +774,7 @@ describe('Amount', function() {
|
||||
assert(isNaN(Amount.from_json('x').copyTo(new Amount())._value));
|
||||
});
|
||||
it('amount.copyTo zero', function() {
|
||||
assert(!(Amount.from_json(0).copyTo(new Amount())._is_negative))
|
||||
assert(!(Amount.from_json(0).copyTo(new Amount())._is_negative));
|
||||
});
|
||||
});
|
||||
describe('Amount comparisons', function() {
|
||||
@@ -1085,7 +1093,7 @@ describe('Amount', function() {
|
||||
describe('from_quality', function() {
|
||||
it('XRP/XRP', function () {
|
||||
assert.throws(function() {
|
||||
Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'XRP'}).to_text_full()
|
||||
Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'XRP'}).to_text_full();
|
||||
});
|
||||
});
|
||||
it('BTC/XRP', function () {
|
||||
@@ -1151,50 +1159,50 @@ describe('Amount', function() {
|
||||
});
|
||||
|
||||
describe('apply interest', function() {
|
||||
it ('from_json apply interest 10 XAU', function() {
|
||||
it('from_json apply interest 10 XAU', function() {
|
||||
var demAmount = Amount.from_json('10/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_text_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
demAmount = demAmount.applyInterest(459990264);
|
||||
assert.strictEqual(demAmount.to_text_full(), '9.294949401870435/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
|
||||
});
|
||||
it ('from_json apply interest XAU', function() {
|
||||
it('from_json apply interest XAU', function() {
|
||||
var demAmount = Amount.from_json('1235.5/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_text_full(), '1235.5/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
demAmount = demAmount.applyInterest(459990264);
|
||||
assert.strictEqual(demAmount.to_text_full(), '1148.390998601092/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it ('from_human with reference date', function() {
|
||||
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date:459990264});
|
||||
demAmount.set_issuer("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
|
||||
it('from_human with reference date', function() {
|
||||
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date: 459990264});
|
||||
demAmount.set_issuer('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_text_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it ('from_json apply interest 10 XAU human', function() {
|
||||
it('from_json apply interest 10 XAU human', function() {
|
||||
var demAmount = Amount.from_json('10/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_human_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
demAmount = demAmount.applyInterest(459990264);
|
||||
assert.strictEqual(demAmount.to_human_full(), '9.294949401870435/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
|
||||
});
|
||||
it ('from_json apply interest XAU human', function() {
|
||||
it('from_json apply interest XAU human', function() {
|
||||
var demAmount = Amount.from_json('1235.5/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_human_full(), '1,235.5/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
demAmount = demAmount.applyInterest(459990264);
|
||||
assert.strictEqual(demAmount.to_human_full(), '1,148.390998601092/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it ('from_human with reference date human', function() {
|
||||
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date:459990264});
|
||||
demAmount.set_issuer("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
|
||||
it('from_human with reference date human', function() {
|
||||
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date: 459990264});
|
||||
demAmount.set_issuer('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(demAmount.to_human_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
describe('amount limits', function() {
|
||||
it ('max JSON wire limite', function() {
|
||||
it('max JSON wire limite', function() {
|
||||
assert.strictEqual(Amount.bi_xns_max.toString(), '100000000000000000');
|
||||
});
|
||||
|
||||
it ('max JSON wire limite', function() {
|
||||
it('max JSON wire limite', function() {
|
||||
assert.strictEqual(Amount.bi_xns_min.toString(), '-100000000000000000');
|
||||
});
|
||||
|
||||
@@ -1206,29 +1214,29 @@ describe('Amount', function() {
|
||||
assert.strictEqual(Amount.bi_man_min_value.toString(), '1000000000000000');
|
||||
});
|
||||
|
||||
it ('from_json minimum XRP', function() {
|
||||
it('from_json minimum XRP', function() {
|
||||
var amt = Amount.from_json('-100000000000000000');
|
||||
assert.strictEqual(amt.to_json(), '-100000000000000000');
|
||||
});
|
||||
|
||||
it ('from_json maximum XRP', function() {
|
||||
it('from_json maximum XRP', function() {
|
||||
var amt = Amount.from_json('100000000000000000');
|
||||
assert.strictEqual(amt.to_json(), '100000000000000000');
|
||||
});
|
||||
|
||||
it ('from_json less than minimum XRP', function() {
|
||||
it('from_json less than minimum XRP', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('-100000000000000001');
|
||||
});
|
||||
});
|
||||
|
||||
it ('from_json more than maximum XRP', function() {
|
||||
it('from_json more than maximum XRP', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('100000000000000001');
|
||||
});
|
||||
});
|
||||
|
||||
it ('from_json minimum IOU', function() {
|
||||
it('from_json minimum IOU', function() {
|
||||
var amt = Amount.from_json('-1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '-1000000000000000e-96');
|
||||
assert.strictEqual(amt.to_text(), Amount.min_value);
|
||||
@@ -1236,27 +1244,27 @@ describe('Amount', function() {
|
||||
|
||||
it('from_json exceed minimum IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
|
||||
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding min value of ' + Amount.min_value);
|
||||
});
|
||||
|
||||
it ('from_json maximum IOU', function() {
|
||||
it('from_json maximum IOU', function() {
|
||||
var amt = Amount.from_json('9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '9999999999999999e80');
|
||||
});
|
||||
|
||||
it ('from_json exceed maximum IOU', function() {
|
||||
it('from_json exceed maximum IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('9999999999999999e81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
|
||||
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() {
|
||||
it('from_json normalize mantissa to valid max range, lost significant digits', function() {
|
||||
var amt = Amount.from_json('99999999999999999999999999999999/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '9999999999999999e16');
|
||||
});
|
||||
|
||||
it ('from_json normalize mantissa to min valid range, lost significant digits', function() {
|
||||
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.to_text(), '-1000000000000000e-40');
|
||||
});
|
||||
|
||||
66
test/base-test.js
Normal file
66
test/base-test.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var Base = require('ripple-lib').Base;
|
||||
var fixtures = require('./fixtures/base58.json');
|
||||
|
||||
function digitArray(str) {
|
||||
return str.split('').map(function(d) {
|
||||
return parseInt(d, 10);
|
||||
});
|
||||
}
|
||||
|
||||
function hexToByteArray(hex) {
|
||||
var byteArray = [];
|
||||
for (var i = 0; i < hex.length / 2; i++) {
|
||||
byteArray.push(parseInt(hex.slice(2 * i, 2 * i + 2), 16));
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
describe('Base', function() {
|
||||
describe('encode_check', function() {
|
||||
it('0', function () {
|
||||
var encoded = Base.encode_check(0, digitArray('00000000000000000000'));
|
||||
assert.strictEqual(encoded, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
|
||||
});
|
||||
it('1', function () {
|
||||
var encoded = Base.encode_check(0, digitArray('00000000000000000001'));
|
||||
assert.strictEqual(encoded, 'rrrrrrrrrrrrrrrrrrrrBZbvji');
|
||||
});
|
||||
});
|
||||
describe('decode_check', function() {
|
||||
it('rrrrrrrrrrrrrrrrrrrrrhoLvTp', function() {
|
||||
var decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
|
||||
assert(decoded.equals(0));
|
||||
});
|
||||
it('rrrrrrrrrrrrrrrrrrrrBZbvji', function() {
|
||||
var decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrBZbvji');
|
||||
assert(decoded.equals(1));
|
||||
});
|
||||
});
|
||||
describe('decode-encode identity', function() {
|
||||
it('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function() {
|
||||
var decoded = Base.decode('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
var encoded = Base.encode(decoded);
|
||||
assert.strictEqual(encoded, 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
describe('encode', function() {
|
||||
it('fixtures', function() {
|
||||
for (var i = 0; i < fixtures.ripple.length; i++) {
|
||||
var testCase = fixtures.ripple[i];
|
||||
var encoded = Base.encode(hexToByteArray(testCase.hex));
|
||||
assert.strictEqual(encoded, testCase.string);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('decode', function() {
|
||||
it('fixtures', function() {
|
||||
for (var i = 0; i < fixtures.ripple.length; i++) {
|
||||
var testCase = fixtures.ripple[i];
|
||||
var decoded = Base.decode(testCase.string);
|
||||
assert.deepEqual(decoded, hexToByteArray(testCase.hex));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var Seed = require('ripple-lib').Seed;
|
||||
|
||||
describe('Base58', function() {
|
||||
describe('Seed', function() {
|
||||
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function () {
|
||||
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
||||
});
|
||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function () {
|
||||
var seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
53
test/baseconverter-test.js
Normal file
53
test/baseconverter-test.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var convertBase = require('ripple-lib').convertBase;
|
||||
|
||||
// Test cases from RFC-1924 (a joke RFC)
|
||||
var BASE85 = ('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
+ 'abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~');
|
||||
var BASE10 = BASE85.slice(0, 10);
|
||||
var BASE16 = BASE85.slice(0, 16);
|
||||
|
||||
var DATA16 = '108000000000000000080800200C417A';
|
||||
var DATA10 = '21932261930451111902915077091070067066';
|
||||
var DATA85 = '4)+k&C#VzJ4br>0wv%Yp';
|
||||
|
||||
function encode(digitArray, encoding) {
|
||||
return digitArray.map(function(i) {
|
||||
return encoding.charAt(i);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function decode(encoded, encoding) {
|
||||
return encoded.split('').map(function(c) {
|
||||
return encoding.indexOf(c);
|
||||
});
|
||||
}
|
||||
|
||||
function convertBaseEncoded(value, fromEncoding, toEncoding) {
|
||||
var digitArray = decode(value, fromEncoding);
|
||||
var converted = convertBase(digitArray, fromEncoding.length,
|
||||
toEncoding.length);
|
||||
return encode(converted, toEncoding);
|
||||
}
|
||||
|
||||
describe('convertBase', function() {
|
||||
it('DEC -> HEX', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA10, BASE10, BASE16), DATA16);
|
||||
});
|
||||
it('HEX -> DEC', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA16, BASE16, BASE10), DATA10);
|
||||
});
|
||||
it('DEC -> B85', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA10, BASE10, BASE85), DATA85);
|
||||
});
|
||||
it('HEX -> B85', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA16, BASE16, BASE85), DATA85);
|
||||
});
|
||||
it('B85 -> DEC', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA85, BASE85, BASE10), DATA10);
|
||||
});
|
||||
it('B85 -> HEX', function () {
|
||||
assert.strictEqual(convertBaseEncoded(DATA85, BASE85, BASE16), DATA16);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
/*eslint-disable */
|
||||
|
||||
var assert = require('assert');
|
||||
var currency = require('ripple-lib').Currency;
|
||||
var timeUtil = require('ripple-lib').utils.time;
|
||||
@@ -54,6 +56,16 @@ describe('Currency', function() {
|
||||
assert(r.is_valid());
|
||||
assert.strictEqual('1D2', r.to_json());
|
||||
});
|
||||
it('from_json("1").to_human()', function() {
|
||||
var r = currency.from_json('1');
|
||||
assert(r.is_valid());
|
||||
assert.strictEqual(1, r.to_json());
|
||||
});
|
||||
it('from_json("#$%").to_human()', function() {
|
||||
var r = currency.from_json('#$%');
|
||||
assert(r.is_valid());
|
||||
assert.strictEqual('0000000000000000000000002324250000000000', r.to_json());
|
||||
});
|
||||
it('from_json("XAU").to_json() hex', function() {
|
||||
var r = currency.from_json("XAU");
|
||||
assert.strictEqual('0000000000000000000000005841550000000000', r.to_json({force_hex: true}));
|
||||
|
||||
141
test/fixtures/base58.json
vendored
Normal file
141
test/fixtures/base58.json
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"ripple": [
|
||||
{
|
||||
"hex": "",
|
||||
"string": ""
|
||||
},
|
||||
{
|
||||
"hex": "61",
|
||||
"string": "pg"
|
||||
},
|
||||
{
|
||||
"hex": "626262",
|
||||
"string": "2sgV"
|
||||
},
|
||||
{
|
||||
"hex": "636363",
|
||||
"string": "2PNi"
|
||||
},
|
||||
{
|
||||
"hex": "73696d706c792061206c6f6e6720737472696e67",
|
||||
"string": "pcEuFj68N1S8n9qHX1tmKpCCFLvp"
|
||||
},
|
||||
{
|
||||
"hex": "00eb15231dfceb60925886b67d065299925915aeb172c06647",
|
||||
"string": "r4Srf52g9jJgTHDrVXjvLUN8ZuQsiJDN9L"
|
||||
},
|
||||
{
|
||||
"hex": "516b6fcd0f",
|
||||
"string": "wB8LTmg"
|
||||
},
|
||||
{
|
||||
"hex": "bf4f89001e670274dd",
|
||||
"string": "sSNosLWLoP8tU"
|
||||
},
|
||||
{
|
||||
"hex": "572e4794",
|
||||
"string": "sNE7fm"
|
||||
},
|
||||
{
|
||||
"hex": "ecac89cad93923c02321",
|
||||
"string": "NJDM3diCXwauyw"
|
||||
},
|
||||
{
|
||||
"hex": "10c8511e",
|
||||
"string": "Rtnzm"
|
||||
},
|
||||
{
|
||||
"hex": "00000000000000000000",
|
||||
"string": "rrrrrrrrrr"
|
||||
},
|
||||
{
|
||||
"hex": "801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e",
|
||||
"string": "nHxrnHEGyeFpUCPx1JKepCXJ1UV8nDN5yoeGGEaJZjGbTR8qC5D"
|
||||
},
|
||||
{
|
||||
"hex": "003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187",
|
||||
"string": "ra7jcY4BG9GTKhuqpCfyYNbu5CqUzoLMGS"
|
||||
}
|
||||
],
|
||||
"bitcoin": [
|
||||
{
|
||||
"hex": "",
|
||||
"string": ""
|
||||
},
|
||||
{
|
||||
"hex": "61",
|
||||
"string": "2g"
|
||||
},
|
||||
{
|
||||
"hex": "626262",
|
||||
"string": "a3gV"
|
||||
},
|
||||
{
|
||||
"hex": "636363",
|
||||
"string": "aPEr"
|
||||
},
|
||||
{
|
||||
"hex": "73696d706c792061206c6f6e6720737472696e67",
|
||||
"string": "2cFupjhnEsSn59qHXstmK2ffpLv2"
|
||||
},
|
||||
{
|
||||
"hex": "00eb15231dfceb60925886b67d065299925915aeb172c06647",
|
||||
"string": "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"
|
||||
},
|
||||
{
|
||||
"hex": "516b6fcd0f",
|
||||
"string": "ABnLTmg"
|
||||
},
|
||||
{
|
||||
"hex": "bf4f89001e670274dd",
|
||||
"string": "3SEo3LWLoPntC"
|
||||
},
|
||||
{
|
||||
"hex": "572e4794",
|
||||
"string": "3EFU7m"
|
||||
},
|
||||
{
|
||||
"hex": "ecac89cad93923c02321",
|
||||
"string": "EJDM8drfXA6uyA"
|
||||
},
|
||||
{
|
||||
"hex": "10c8511e",
|
||||
"string": "Rt5zm"
|
||||
},
|
||||
{
|
||||
"hex": "00000000000000000000",
|
||||
"string": "1111111111"
|
||||
},
|
||||
{
|
||||
"hex": "801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e",
|
||||
"string": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD"
|
||||
},
|
||||
{
|
||||
"hex": "003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187",
|
||||
"string": "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS"
|
||||
}
|
||||
],
|
||||
"invalid": [
|
||||
{
|
||||
"description": "non-base58 string",
|
||||
"string": "invalid"
|
||||
},
|
||||
{
|
||||
"description": "non-base58 alphabet",
|
||||
"string": "c2F0b3NoaQo="
|
||||
},
|
||||
{
|
||||
"description": "leading whitespace",
|
||||
"string": " 1111111111"
|
||||
},
|
||||
{
|
||||
"description": "trailing whitespace",
|
||||
"string": "1111111111 "
|
||||
},
|
||||
{
|
||||
"description": "unexpected character after whitespace",
|
||||
"string": " \t\n\u000b\f\r skip \r\f\u000b\n\t a"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
93
test/fixtures/orderbook.js
vendored
93
test/fixtures/orderbook.js
vendored
@@ -1,9 +1,13 @@
|
||||
var _ = require('lodash');
|
||||
/*eslint-disable max-len */
|
||||
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var addresses = require('./addresses');
|
||||
var Meta = require('ripple-lib').Meta;
|
||||
var Meta = require('ripple-lib').Meta;
|
||||
|
||||
module.exports.FIAT_BALANCE = '10';
|
||||
module.exports.NATIVE_BALANCE = '25';
|
||||
module.exports.NATIVE_BALANCE = '55';
|
||||
module.exports.NATIVE_BALANCE_PREVIOUS = '100';
|
||||
|
||||
module.exports.TAKER_GETS = '19.84580331';
|
||||
@@ -93,7 +97,7 @@ module.exports.fiatOffers = function (options) {
|
||||
index: module.exports.OTHER_LEDGER_INDEX,
|
||||
owner_funds: options.other_account_funds,
|
||||
quality: '195796912.5171664'
|
||||
},
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
@@ -161,7 +165,7 @@ module.exports.NATIVE_OFFERS = [
|
||||
index: 'D3338DA77BA23122FB5647B74B53636AB54BE246D4B21707C9D6887DEB334252',
|
||||
owner_funds: '235.0194163432668',
|
||||
quality: '195796912.5171664'
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.REQUEST_OFFERS = [
|
||||
@@ -187,7 +191,7 @@ module.exports.REQUEST_OFFERS = [
|
||||
},
|
||||
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
|
||||
owner_funds: '0.1129267125000245',
|
||||
quality: '496.5'
|
||||
quality: '496.4999999999999'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
@@ -262,7 +266,7 @@ module.exports.REQUEST_OFFERS = [
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
quality: '498.6116758238228'
|
||||
quality: '199.4446703295291'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -282,7 +286,8 @@ module.exports.REQUEST_OFFERS_NATIVE = [
|
||||
value: '56.06639660617357'
|
||||
},
|
||||
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
|
||||
owner_funds: '600'
|
||||
owner_funds: '600',
|
||||
quality: '.0560663966061735'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
@@ -301,6 +306,7 @@ module.exports.REQUEST_OFFERS_NATIVE = [
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '4000',
|
||||
quality: '0.049861167582382'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
@@ -319,6 +325,7 @@ module.exports.REQUEST_OFFERS_NATIVE = [
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '3900',
|
||||
quality: '0.049861167582382'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
@@ -338,7 +345,61 @@ module.exports.REQUEST_OFFERS_NATIVE = [
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
quality: '498.6116758238228'
|
||||
quality: '0.049861167582382'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.QUALITY_OFFERS = [
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C1AFE1EE71A605F',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000009',
|
||||
PreviousTxnID: 'BCA728C17DBA10F100C41D4EF8B37318F33BC6156E94DB16703D2A1EE43DCCE6',
|
||||
PreviousTxnLgrSeq: 11929146,
|
||||
Sequence: 668643,
|
||||
TakerGets: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '301.3426005599325'
|
||||
},
|
||||
TakerPays: '22895281765',
|
||||
index: '79B34D7DF703580B86099EFD6B2E419AA39A585A50C82A3F9B446721B7C1490C',
|
||||
owner_funds: '5910.437716613066',
|
||||
quality: '75977580.74216543'
|
||||
}
|
||||
];
|
||||
|
||||
// This fixture is to exercise a bug where taker_pays_funded = taker_gets_funded * quality
|
||||
// has decimal amounts.
|
||||
module.exports.DECIMAL_TAKER_PAYS_FUNDED_OFFERS = [
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5D0689673FA9094A',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000006',
|
||||
PreviousTxnID: 'C1BB04CE39E30BF5982B7660793723E9B3A832F5B458DB1C5938F4737E0E9ABF',
|
||||
PreviousTxnLgrSeq: 11631257,
|
||||
Sequence: 2936,
|
||||
TakerGets: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '9280.04'
|
||||
},
|
||||
TakerPays: '1707459061637',
|
||||
index: '89D85BBE91E0F419953EB89CE62E194922ED930EE57BE0C62FCC3B22DDB20852',
|
||||
owner_funds: '9280.037154029904',
|
||||
quality: '183992640.2943306',
|
||||
taker_gets_funded: {
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '9261.514125778347'
|
||||
},
|
||||
taker_pays_funded: '1704050437125'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -612,7 +673,7 @@ module.exports.transactionWithInvalidAccountRoot = function(options) {
|
||||
options = options || {};
|
||||
_.defaults(options, {
|
||||
account: addresses.ACCOUNT,
|
||||
balance: module.exports.NATIVE_BALANCE,
|
||||
balance: module.exports.NATIVE_BALANCE
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -674,7 +735,7 @@ module.exports.transactionWithCreatedOffer = function(options) {
|
||||
TransactionType: 'OfferCreate',
|
||||
owner_funds: '2010.027702881682'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.transactionWithDeletedOffer = function(options) {
|
||||
@@ -719,7 +780,7 @@ module.exports.transactionWithDeletedOffer = function(options) {
|
||||
TransactionType: options.transaction_type,
|
||||
owner_funds: '2010.027702881682'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.transactionWithModifiedOffer = function() {
|
||||
@@ -735,7 +796,7 @@ module.exports.transactionWithModifiedOffer = function() {
|
||||
TransactionType: 'OfferCreate',
|
||||
owner_funds: '2010.027702881682'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.transactionWithModifiedOffers = function() {
|
||||
@@ -751,7 +812,7 @@ module.exports.transactionWithModifiedOffers = function() {
|
||||
TransactionType: 'OfferCreate',
|
||||
owner_funds: '2010.027702881682'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.transactionWithNoNodes = function() {
|
||||
@@ -767,7 +828,7 @@ module.exports.transactionWithNoNodes = function() {
|
||||
TransactionType: 'OfferCreate',
|
||||
owner_funds: '2010.027702881682'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.accountInfoResponse = function(options) {
|
||||
@@ -793,4 +854,4 @@ module.exports.accountInfoResponse = function(options) {
|
||||
urlgravatar: 'http:www.gravatar.com/avatar/5b33b93c7ffe384d53450fc666bb11fb'
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
294
test/fixtures/transactionmanager.json
vendored
Normal file
294
test/fixtures/transactionmanager.json
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
{
|
||||
"ACCOUNT": {
|
||||
"address": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"secret": "snoBcf899BdZP6nyaRHnSvE7qAJFP"
|
||||
},
|
||||
"ACCOUNT2": {
|
||||
"address": "rn7GZeJpUafLAyKQ7wrU5SUCu2hpQq7S2W",
|
||||
"secret": "ssThngbFXQWyJpK1mTeaNBrhHxHhp"
|
||||
},
|
||||
"SUBSCRIBE_RESPONSE": {
|
||||
"id": 1,
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"result": {
|
||||
"fee_base": 10,
|
||||
"fee_ref": 10,
|
||||
"hostid": "NBZ",
|
||||
"ledger_hash": "F1AF7D977B01D99D013EEE75136263A0937575882CC9A741662C3C111B08B112",
|
||||
"ledger_index": 1,
|
||||
"ledger_time": 463782770,
|
||||
"load_base": 256,
|
||||
"load_factor": 256,
|
||||
"pubkey_node": "n3Lp7DfQmxjHF5mYJsV2U9anALHmPem8PWQHWGpw4XMz79HA5aJH",
|
||||
"random": "EECFEE93BBB608914F190EC177B11DE52FC1D75D2C97DACBD26D2DFC6050E874",
|
||||
"reserve_base": 20000000,
|
||||
"reserve_inc": 5000000,
|
||||
"server_status": "full",
|
||||
"validated_ledgers": "32570-11692908"
|
||||
}
|
||||
},
|
||||
"ACCOUNT_INFO_RESPONSE": {
|
||||
"id": 1,
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"result": {
|
||||
"account_data": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Balance": "10000",
|
||||
"Flags": 4849664,
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"OwnerCount": 1,
|
||||
"Sequence": 3,
|
||||
"index": "01F3A2D58A5B986BF31CD92B513EB539CE48F705BB0E18FA39EF042DBE07A5DE"
|
||||
},
|
||||
"ledger_current_index": 11699032,
|
||||
"validated": false
|
||||
}
|
||||
|
||||
},
|
||||
"TX_STREAM_TRANSACTION": {
|
||||
"engine_result": "tesSUCCESS",
|
||||
"engine_result_code": 0,
|
||||
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
|
||||
"ledger_hash": "8093A78DEFD1F02037ABD349BD452081554A1DB1FE5E20DCB82D8DF16DD23B6D",
|
||||
"ledger_index": 1,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Balance": "1000",
|
||||
"Flags": 4849664,
|
||||
"OwnerCount": 1,
|
||||
"Sequence": 1
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "A4B28FB972EF890DC39A8557DF8960D41DADA00D39B0F1EFCD4BBB85FCA13A30",
|
||||
"PreviousFields": {
|
||||
"Balance": "1000",
|
||||
"Sequence": 3864
|
||||
},
|
||||
"PreviousTxnID": "F4910E55A39C42AB82071212D84119631DDE0B0F4F8F9040F252B0066898DBDF",
|
||||
"PreviousTxnLgrSeq": 11693103
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 9,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"status": "closed",
|
||||
"transaction": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Fee": "10",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 11693114,
|
||||
"Sequence": 1,
|
||||
"SigningPubKey": "2D7F6A1F5F1AE2E0105A88F47D33A0B68031DE7FC43FF42388DA7D4B4C93865F",
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "39A0EC2307723546901BC8017CED16DF3DD79F50CA68A32377E0CF1BE097DCA004C21D99120220321921230C53A87629D79E6A5E74C08ECB59F8CBB9D695256DE5D0F8CB22FF0A",
|
||||
"date": 4771619,
|
||||
"hash": "01D66ACBD00B2A8F5D66FC8F67AC879CAECF49BC94FB97CF24F66B8406F4C040"
|
||||
},
|
||||
"type": "transaction",
|
||||
"validated": true
|
||||
|
||||
},
|
||||
"ACCOUNT_TX_TRANSACTION": {
|
||||
"validated": true,
|
||||
"meta": {
|
||||
"TransactionIndex": 3,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 11693103,
|
||||
"PreviousTxnID": "F4910E55A39C42AB82071212D84119631DDE0B0F4F8F9040F252B0066898DBDF",
|
||||
"LedgerIndex": "A4B28FB972EF890DC39A8557DF8960D41DADA00D39B0F1EFCD4BBB85FCA13A30",
|
||||
"PreviousFields": {
|
||||
"Sequence": 3864,
|
||||
"Balance": "1000"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 4849664,
|
||||
"Sequence": 3865,
|
||||
"OwnerCount": 1,
|
||||
"Balance": "1000",
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"tx": {
|
||||
"TransactionType": "AccountSet",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 3864,
|
||||
"LastLedgerSequence": 2,
|
||||
"Fee": "10",
|
||||
"SigningPubKey": "2D7F6A1F5F1AE2E0105A88F47D33A0B68031DE7FC43FF42388DA7D4B4C93865F",
|
||||
"TxnSignature": "39A0EC2307723546901BC8017CED16DF3DD79F50CA68A32377E0CF1BE097DCA004C21D99120220321921230C53A87629D79E6A5E74C08ECB59F8CBB9D695256DE5D0F8CB22FF",
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"hash": "01D66ACBD00B2A8F5D66FC8F67AC879CAECF49BC94FB97CF24F66B8406F4C040",
|
||||
"ledger_index": 1,
|
||||
"inLedger": 1
|
||||
}
|
||||
},
|
||||
"ACCOUNT_TX_RESPONSE": {
|
||||
"id": 1,
|
||||
"status": "success",
|
||||
"type": "response",
|
||||
"result": {
|
||||
"account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"ledger_index_max": 100,
|
||||
"ledger_index_min": 1,
|
||||
"limit": 10,
|
||||
"transactions": [ ]
|
||||
}
|
||||
},
|
||||
"LEDGER": {
|
||||
"type": "ledgerClosed",
|
||||
"fee_base": 10,
|
||||
"fee_ref": 10,
|
||||
"ledger_hash": "FF757ECCF710C27DBCEC569840C38C5583594B56C49693079D95D8A99C30A928",
|
||||
"ledger_index": 1,
|
||||
"ledger_time": 478088,
|
||||
"reserve_base": 20000000,
|
||||
"reserve_inc": 5000000,
|
||||
"txn_count": 1,
|
||||
"validated_ledgers": "1-2"
|
||||
},
|
||||
"ACCOUNT_TX_ERROR": {
|
||||
"id": 1,
|
||||
"status": "error",
|
||||
"type": "response",
|
||||
"error": "actMalformed",
|
||||
"error_code": 33,
|
||||
"error_message": "Account malformed.",
|
||||
"request": {
|
||||
"account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3dZ",
|
||||
"binary": true,
|
||||
"command": "account_tx",
|
||||
"id": 1,
|
||||
"ledger_index_max": -1,
|
||||
"ledger_index_min": -1,
|
||||
"limit": 10
|
||||
}
|
||||
},
|
||||
"SUBMIT_RESPONSE": {
|
||||
"id": 1,
|
||||
"result": {
|
||||
"engine_result": "tesSUCCESS",
|
||||
"engine_result_code": 0,
|
||||
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
|
||||
"tx_blob": "12000322800000002400000001201B0000000568400000000000000C732102999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC744730450221008DFE8B50D66F2094652AA6BD1B354CC97E885BC1AEDF586C7ED61C7D786AA66202200FEE38460CF25C726A7F31626BD7A45542AFB2E468EBE9A175F9A9EC3FD19DDB811492DECA2DC92352BE97C1F6347F7E6CCB9A8241C8",
|
||||
"tx_json": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Fee": "12000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 1,
|
||||
"Sequence": 1,
|
||||
"SigningPubKey": "02999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC",
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "30440220332316AC7B96B1385F4BD0159706439889A9E05AD62D168E61AC5CBD7BD417C00220361D9FBC31A5919F68FA70E9FB78C6E6C25CC7F89D0A2F9CC42660594EE0D0A2",
|
||||
"hash": "0D15A847D605DB5F1B76A4EE88EDCD5D279D47F009CFCE27F1D3DFE763225975"
|
||||
}
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
|
||||
},
|
||||
"SUBMIT_TEC_RESPONSE": {
|
||||
"id": 1,
|
||||
"result": {
|
||||
"engine_result": "tecNO_REGULAR_KEY",
|
||||
"engine_result_code": 131,
|
||||
"engine_result_message": "Regular key is not set.",
|
||||
"tx_blob": "12000322800000002400000001201B0000000520210000000468400000000000000C732102999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC74473045022100EEB2D1EFDC4B63A3FA9BD934307D3377197E86133A156022A01FFAD7CE3A78FA022024EB39D525FE42853F8E6B62BB74124FD0A1F763C56621AA8EC6A0FC0D0A6FC0811492DECA2DC92352BE97C1F6347F7E6CCB9A8241C8",
|
||||
"tx_json": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Fee": "12000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 2,
|
||||
"Sequence": 1,
|
||||
"SetFlag": 4,
|
||||
"SigningPubKey": "02999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC",
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "304402202DF7ED29DCA16F7A6191A74127437ACA04ADFCE6DB570101737C6426DD664FF5022069E9C6965DED37207708420B3B77A8CE9D0D009EE1D2434580ED4871398574D0",
|
||||
"hash": "56DCAE121213AA2E3A74921F934E78635DC0576E4DA1184AEE7D0F8E49046790"
|
||||
}
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
},
|
||||
"SUBMIT_TER_RESPONSE": {
|
||||
"id": 1,
|
||||
"result": {
|
||||
"engine_result": "terNO_ACCOUNT",
|
||||
"engine_result_code": -96,
|
||||
"engine_result_message": "The source account does not exist.",
|
||||
"tx_blob": "12000022800000002400000004201B0000000561400000000000000168400000000000000C732102999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC74473045022100BDEF89CF25B541FFDBBE0B652BF29D8C30715C8226346C72DBA69B275BC471C50220582C5F3DE6DE489E10594BD8911B2917EE9A452E6ED3A1BEED25E402CD2E3EFF811492DECA2DC92352BE97C1F6347F7E6CCB9A8241C883143108B9AC27BF036EFE5CBE787921F54D622B7A5B",
|
||||
"tx_json": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Amount": "1",
|
||||
"Destination": "rn7GZeJpUafLAyKQ7wrU5SUCu2hpQq7S2W",
|
||||
"Fee": "12000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 2,
|
||||
"Sequence": 1,
|
||||
"SigningPubKey": "02999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC",
|
||||
"TransactionType": "Payment",
|
||||
"TxnSignature": "3044022041F099FD8F35F67E73B5F41FFD3CBCF5781748BFEBA6FD1AB56586EE71B7EE9902205F1C4DF58B40859A404939B44A169DA995DA03C6ED713C5DF76E57A843CCE7D1",
|
||||
"hash": "31090EA7996DFA6C2E0B43F704C30EC9AE635C18D7BEF90F42D4E0E2943A2680"
|
||||
}
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
},
|
||||
"SUBMIT_TEF_RESPONSE": {
|
||||
"id": 1,
|
||||
"result": {
|
||||
"engine_result": "tefPAST_SEQ",
|
||||
"engine_result_code": -189,
|
||||
"engine_result_message": "This sequence number has already past.",
|
||||
"tx_blob": "12000322800000002400000002201B0000000568400000000000000C732102999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC744730450221008B213EA1B4AACA9545E3DBFE43C69711DF295170B6F510CCD24B383684852C0702200E2A8FA3B205F1AFECF6D6DE298889D71E06336942ADF538847C8EFB88D3AA74811492DECA2DC92352BE97C1F6347F7E6CCB9A8241C8",
|
||||
"tx_json": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Fee": "12000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 2,
|
||||
"Sequence": 1,
|
||||
"SigningPubKey": "02999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC",
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "304402203F6B9445B3B2176957C1008EBC0F18668464080A2CB10E78D83FB67DE2ABC3F50220769673DE79E6C1AF0E076A716ABE53018158385615E7EC2866DCFD6268806BF8",
|
||||
"hash": "731E94632E219ECAF2043C09C0075F1DEA2EC08E66A381CD536F0B1F3B30844D"
|
||||
}
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
},
|
||||
"SUBMIT_TEL_RESPONSE": {
|
||||
"id": 1,
|
||||
"result": {
|
||||
"engine_result": "telINSUF_FEE_P",
|
||||
"engine_result_code": -394,
|
||||
"engine_result_message": "Fee insufficient.",
|
||||
"tx_blob": "12000322800000002400000003201B00000005684000000000000001732102999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC7446304402206480AC2410BE4370E74E27B4427EF05F8A156D313C9D768E57B6EEC3BB89CDD402201503759955F367F88E5442F08D713B5369C5528475F046500721E676C24D0DFD811492DECA2DC92352BE97C1F6347F7E6CCB9A8241C8",
|
||||
"tx_json": {
|
||||
"Account": "rNP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d",
|
||||
"Fee": "12000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 2,
|
||||
"Sequence": 1,
|
||||
"SigningPubKey": "02999FB4BC17144F83CDC2F17EA642519FF115EE7B0CC8C78DE9061F1A473F7BAC",
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "3044022002C0C13B21F9B690C6A3FF4B5A3F966AC47ACEE80453DAD553F5051FB3CED0E902202851BCC9EF8E2160BD1F3F6CF91B7C679149BD5FAD502C2BA544F7E61755AD1B",
|
||||
"hash": "ABF970193B8C9BE9BBD2777750924B28E7542DE1491EB55F4795539045AEA8B9"
|
||||
}
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
/*eslint-disable max-len */
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert-diff');
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
var Currency = require('ripple-lib').Currency;
|
||||
var Amount = require('ripple-lib').Amount;
|
||||
var Meta = require('ripple-lib').Meta;
|
||||
@@ -64,7 +68,7 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
};
|
||||
|
||||
book.on('model', function(){});
|
||||
book.on('model', function() {});
|
||||
});
|
||||
|
||||
it('Subscribe', function(done) {
|
||||
@@ -100,7 +104,7 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
book.on('model', function(){});
|
||||
book.on('model', function() {});
|
||||
|
||||
book.unsubscribe();
|
||||
|
||||
@@ -120,7 +124,7 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
};
|
||||
|
||||
book.on('model', function(){});
|
||||
book.on('model', function() {});
|
||||
book.removeAllListeners('model');
|
||||
});
|
||||
|
||||
@@ -135,7 +139,7 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
};
|
||||
|
||||
book.once('model', function(){});
|
||||
book.once('model', function() {});
|
||||
book.emit('model', {});
|
||||
});
|
||||
|
||||
@@ -149,7 +153,7 @@ describe('OrderBook', function() {
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book.setOwnerFunds(addresses.ACCOUNT, '1');
|
||||
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), '1');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), '1');
|
||||
});
|
||||
|
||||
it('Set owner funds - unadjusted funds', function() {
|
||||
@@ -190,7 +194,7 @@ describe('OrderBook', function() {
|
||||
});
|
||||
|
||||
it('Has owner funds', function() {
|
||||
var book = new Remote().createOrderBook({
|
||||
var book = new Remote().createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: addresses.ISSUER,
|
||||
currency_pays: 'BTC'
|
||||
@@ -276,7 +280,7 @@ describe('OrderBook', function() {
|
||||
|
||||
assert.strictEqual(book.decrementOwnerOfferCount(addresses.ACCOUNT), 0);
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.ACCOUNT), 0);
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT, undefined));
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), undefined);
|
||||
});
|
||||
|
||||
it('Decrement owner offer count - invalid address', function() {
|
||||
@@ -441,7 +445,7 @@ describe('OrderBook', function() {
|
||||
remote.request = function(request) {
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'account_info',
|
||||
id: void(0),
|
||||
id: undefined,
|
||||
account: addresses.ISSUER
|
||||
});
|
||||
|
||||
@@ -469,7 +473,7 @@ describe('OrderBook', function() {
|
||||
remote.request = function(request) {
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'account_info',
|
||||
id: void(0),
|
||||
id: undefined,
|
||||
account: addresses.ISSUER
|
||||
});
|
||||
|
||||
@@ -495,7 +499,7 @@ describe('OrderBook', function() {
|
||||
|
||||
book._issuerTransferRate = 1002000000;
|
||||
|
||||
remote.request = function(request) {
|
||||
remote.request = function() {
|
||||
assert(false);
|
||||
};
|
||||
|
||||
@@ -513,7 +517,7 @@ describe('OrderBook', function() {
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
remote.request = function(request) {
|
||||
remote.request = function() {
|
||||
assert(false);
|
||||
};
|
||||
|
||||
@@ -577,7 +581,8 @@ describe('OrderBook', function() {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER
|
||||
},
|
||||
TakerPays: '123456'
|
||||
TakerPays: '123456',
|
||||
quality: '1234.56'
|
||||
};
|
||||
|
||||
book.setOwnerFunds(addresses.ACCOUNT, '99');
|
||||
@@ -590,7 +595,8 @@ describe('OrderBook', function() {
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '99',
|
||||
taker_pays_funded: '122221',
|
||||
owner_funds: '99'
|
||||
owner_funds: '99',
|
||||
quality: '1234.56'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
@@ -649,7 +655,8 @@ describe('OrderBook', function() {
|
||||
value: '123.456',
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER
|
||||
}
|
||||
},
|
||||
quality: '1.23456'
|
||||
};
|
||||
|
||||
book.setOwnerFunds(addresses.ACCOUNT, '99');
|
||||
@@ -662,7 +669,8 @@ describe('OrderBook', function() {
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '99',
|
||||
taker_pays_funded: '122.22144',
|
||||
owner_funds: '99'
|
||||
owner_funds: '99',
|
||||
quality: '1.23456'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
@@ -1112,7 +1120,7 @@ describe('OrderBook', function() {
|
||||
|
||||
book._offers = fixtures.fiatOffers();
|
||||
|
||||
book.on('offer_changed', function(offer) {
|
||||
book.on('offer_changed', function() {
|
||||
receivedChangedEvents += 1;
|
||||
});
|
||||
|
||||
@@ -1138,7 +1146,7 @@ describe('OrderBook', function() {
|
||||
book.updateFundedAmounts(message);
|
||||
|
||||
setImmediate(function() {
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), fixtures.FIAT_BALANCE);
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), fixtures.FIAT_BALANCE);
|
||||
assert.strictEqual(receivedChangedEvents, 2);
|
||||
assert.strictEqual(receivedFundsChangedEvents, 2);
|
||||
done();
|
||||
@@ -1146,7 +1154,6 @@ describe('OrderBook', function() {
|
||||
});
|
||||
|
||||
it('Update funded amounts - increase funds', function() {
|
||||
var receivedChangedEvents = 0;
|
||||
var receivedFundsChangedEvents = 0;
|
||||
|
||||
var remote = new Remote();
|
||||
@@ -1237,7 +1244,7 @@ describe('OrderBook', function() {
|
||||
book.updateFundedAmounts(message);
|
||||
|
||||
setImmediate(function() {
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), '9.980039920159681');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), '9.980039920159681');
|
||||
|
||||
done();
|
||||
});
|
||||
@@ -1248,7 +1255,7 @@ describe('OrderBook', function() {
|
||||
var receivedFundsChangedEvents = 0;
|
||||
|
||||
var remote = new Remote();
|
||||
|
||||
|
||||
var message = fixtures.transactionWithAccountRoot();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
@@ -1260,7 +1267,7 @@ describe('OrderBook', function() {
|
||||
book._synchronized = true;
|
||||
book._offers = fixtures.NATIVE_OFFERS;
|
||||
|
||||
book.on('offer_changed', function(offer) {
|
||||
book.on('offer_changed', function() {
|
||||
receivedChangedEvents += 1;
|
||||
});
|
||||
|
||||
@@ -1270,10 +1277,10 @@ describe('OrderBook', function() {
|
||||
assert.notStrictEqual(previousFunds, newFunds);
|
||||
switch (++receivedFundsChangedEvents) {
|
||||
case 1:
|
||||
assert(!offer.is_fully_funded);
|
||||
assert(offer.is_fully_funded);
|
||||
break;
|
||||
case 2:
|
||||
assert(offer.is_fully_funded);
|
||||
assert(!offer.is_fully_funded);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -1291,7 +1298,7 @@ describe('OrderBook', function() {
|
||||
|
||||
it('Update funded amounts - no affected account', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
|
||||
var message = fixtures.transactionWithAccountRoot({
|
||||
account: addresses.ACCOUNT
|
||||
});
|
||||
@@ -1413,10 +1420,10 @@ describe('OrderBook', function() {
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.THIRD_ACCOUNT), 1);
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.FOURTH_ACCOUNT), 1);
|
||||
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), '2006.015671538605');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.OTHER_ACCOUNT), '24.01284027983332');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.THIRD_ACCOUNT), '9053.294314019701');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.FOURTH_ACCOUNT), '7229.594289344439');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), '2006.015671538605');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.OTHER_ACCOUNT).to_text(), '24.01284027983332');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.THIRD_ACCOUNT).to_text(), '9053.294314019701');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.FOURTH_ACCOUNT).to_text(), '7229.594289344439');
|
||||
});
|
||||
|
||||
it('Set offers - issuer transfer rate set - iou/xrp - funded amounts', function() {
|
||||
@@ -1510,47 +1517,23 @@ describe('OrderBook', function() {
|
||||
|
||||
assert.strictEqual(book._offers.length, 5);
|
||||
|
||||
var accountOfferTotal = Amount.from_json({
|
||||
value: 275.85192574,
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER
|
||||
});
|
||||
|
||||
var otherAccountOfferTotal = Amount.from_json({
|
||||
value: 24.060765960393,
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER
|
||||
});
|
||||
|
||||
var thirdAccountOfferTotal = Amount.from_json({
|
||||
value: 712.60995,
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER
|
||||
});
|
||||
|
||||
var fourthAccountOfferTotal = Amount.from_json({
|
||||
value: 288.08,
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER
|
||||
});
|
||||
|
||||
assert(book.getOwnerOfferTotal(addresses.ACCOUNT).equals(accountOfferTotal));
|
||||
assert(book.getOwnerOfferTotal(addresses.OTHER_ACCOUNT).equals(otherAccountOfferTotal));
|
||||
assert(book.getOwnerOfferTotal(addresses.THIRD_ACCOUNT).equals(thirdAccountOfferTotal));
|
||||
assert(book.getOwnerOfferTotal(addresses.FOURTH_ACCOUNT).equals(fourthAccountOfferTotal));
|
||||
assert.strictEqual(book.getOwnerOfferTotal(addresses.ACCOUNT).to_text(), '275.85192574');
|
||||
assert.strictEqual(book.getOwnerOfferTotal(addresses.OTHER_ACCOUNT).to_text(), '24.060765960393');
|
||||
assert.strictEqual(book.getOwnerOfferTotal(addresses.THIRD_ACCOUNT).to_text(), '712.60995');
|
||||
assert.strictEqual(book.getOwnerOfferTotal(addresses.FOURTH_ACCOUNT).to_text(), '288.08');
|
||||
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.ACCOUNT), 2);
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.OTHER_ACCOUNT), 1);
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.THIRD_ACCOUNT), 1);
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.FOURTH_ACCOUNT), 1);
|
||||
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), '2006.015671538605');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.OTHER_ACCOUNT), '24.01284027983332');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.THIRD_ACCOUNT), '9053.294314019701');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.FOURTH_ACCOUNT), '7229.594289344439');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), '2006.015671538605');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.OTHER_ACCOUNT).to_text(), '24.01284027983332');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.THIRD_ACCOUNT).to_text(), '9053.294314019701');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.FOURTH_ACCOUNT).to_text(), '7229.594289344439');
|
||||
});
|
||||
|
||||
it('Notify - created node', function() {
|
||||
it('Set offers - incorrect taker pays funded', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
@@ -1558,6 +1541,20 @@ describe('OrderBook', function() {
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1002000000;
|
||||
|
||||
var offers = fixtures.DECIMAL_TAKER_PAYS_FUNDED_OFFERS;
|
||||
|
||||
book.setOffers(offers);
|
||||
|
||||
assert.strictEqual(book._offers.length, 1);
|
||||
|
||||
assert.strictEqual(book._offers[0].taker_gets_funded, '9261.514125778347');
|
||||
assert.strictEqual(book._offers[0].taker_pays_funded, '1704050437125');
|
||||
});
|
||||
|
||||
it('Notify - created node', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
@@ -1573,16 +1570,10 @@ describe('OrderBook', function() {
|
||||
|
||||
book.notify(message);
|
||||
|
||||
var accountOfferTotal = Amount.from_json({
|
||||
value: 1.9951,
|
||||
currency: 'USD',
|
||||
issuer: addresses.ISSUER
|
||||
});
|
||||
|
||||
assert.strictEqual(book._offers.length, 1);
|
||||
assert(book.getOwnerOfferTotal(addresses.ACCOUNT).equals(accountOfferTotal));
|
||||
assert.strictEqual(book.getOwnerOfferTotal(addresses.ACCOUNT).to_text(), '1.9951');
|
||||
assert.strictEqual(book.getOwnerOfferCount(addresses.ACCOUNT), 1);
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT), '2006.015671538605');
|
||||
assert.strictEqual(book.getOwnerFunds(addresses.ACCOUNT).to_text(), '2006.015671538605');
|
||||
});
|
||||
|
||||
it('Notify - created nodes - correct sorting', function() {
|
||||
@@ -1663,13 +1654,6 @@ describe('OrderBook', function() {
|
||||
it('Notify - deleted node', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -1693,13 +1677,6 @@ describe('OrderBook', function() {
|
||||
it('Notify - deleted node - last offer', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -1771,7 +1748,7 @@ describe('OrderBook', function() {
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
|
||||
book.on('trade', function(tradeGets, tradePays) {
|
||||
book.on('trade', function(tradePays, tradeGets) {
|
||||
var expectedTradePays = Amount.from_json(fixtures.TAKER_PAYS);
|
||||
var expectedTradeGets = Amount.from_json({
|
||||
value: fixtures.TAKER_GETS,
|
||||
@@ -1798,13 +1775,6 @@ describe('OrderBook', function() {
|
||||
it('Notify - deleted node - offer cancel', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -1830,13 +1800,6 @@ describe('OrderBook', function() {
|
||||
it('Notify - deleted node - offer cancel - last offer', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -1861,13 +1824,6 @@ describe('OrderBook', function() {
|
||||
it('Notify - modified node', function() {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -2104,6 +2060,30 @@ describe('OrderBook', function() {
|
||||
assert.strictEqual(book._offers[0].taker_pays_funded, '881086106');
|
||||
});
|
||||
|
||||
it('Insert offer - best quality', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
|
||||
book._subscribed = true;
|
||||
book._issuerTransferRate = 1000000000;
|
||||
|
||||
book.setOffers(fixtures.QUALITY_OFFERS);
|
||||
|
||||
book.insertOffer(fixtures.transactionWithCreatedOffer({
|
||||
amount: '51.04587961502088'
|
||||
}).mmeta.getNodes()[0]);
|
||||
|
||||
assert.strictEqual(book._offers.length, 2);
|
||||
|
||||
assert.strictEqual(book._offers[0].taker_gets_funded, '51.04587961502088');
|
||||
assert.strictEqual(book._offers[0].taker_pays_funded, fixtures.TAKER_PAYS);
|
||||
assert.strictEqual(book._offers[0].quality, '75977580.74206542');
|
||||
});
|
||||
|
||||
it('Insert offer - best quality - insufficient funds for all offers', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
@@ -2225,7 +2205,7 @@ describe('OrderBook', function() {
|
||||
case 'book_offers':
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'book_offers',
|
||||
id: void(0),
|
||||
id: undefined,
|
||||
taker_gets: {
|
||||
currency: '0000000000000000000000004254430000000000',
|
||||
issuer: addresses.ISSUER
|
||||
@@ -2254,7 +2234,7 @@ describe('OrderBook', function() {
|
||||
book._issuerTransferRate = 1002000000;
|
||||
|
||||
var expected = [
|
||||
{
|
||||
{
|
||||
Account: addresses.ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
|
||||
BookNode: '0000000000000000',
|
||||
@@ -2262,7 +2242,8 @@ describe('OrderBook', function() {
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000000',
|
||||
Sequence: 195,
|
||||
TakerGets: { currency: 'BTC',
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.1129232560043778'
|
||||
},
|
||||
@@ -2275,7 +2256,8 @@ describe('OrderBook', function() {
|
||||
owner_funds: '0.1129267125000245',
|
||||
taker_gets_funded: '0.112701309880264',
|
||||
taker_pays_funded: '55.95620035555106',
|
||||
is_fully_funded: false
|
||||
is_fully_funded: false,
|
||||
quality: '496.4999999999999'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
@@ -2300,9 +2282,10 @@ describe('OrderBook', function() {
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '0.2',
|
||||
taker_pays_funded: '99.72233516476456'
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '498.6116758238228'
|
||||
},
|
||||
{
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
@@ -2311,7 +2294,8 @@ describe('OrderBook', function() {
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
Sequence: 29356,
|
||||
TakerGets: { currency: 'BTC',
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: addresses.ISSUER,
|
||||
value: '0.5'
|
||||
},
|
||||
@@ -2324,9 +2308,10 @@ describe('OrderBook', function() {
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '0.5',
|
||||
taker_pays_funded: '99.72233516476456'
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '498.6116758238228'
|
||||
},
|
||||
{
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
@@ -2349,7 +2334,8 @@ describe('OrderBook', function() {
|
||||
owner_funds: '0.950363009783092',
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '0.4484660776278363',
|
||||
taker_pays_funded: '89.44416900646082'
|
||||
taker_pays_funded: '89.44416900646082',
|
||||
quality: '199.4446703295291'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -2386,7 +2372,8 @@ describe('OrderBook', function() {
|
||||
owner_funds: '600',
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '600',
|
||||
taker_pays_funded: '33.63983796370414'
|
||||
taker_pays_funded: '33.6398379637041',
|
||||
quality: '.0560663966061735'
|
||||
},
|
||||
{
|
||||
Account: addresses.OTHER_ACCOUNT,
|
||||
@@ -2407,7 +2394,8 @@ describe('OrderBook', function() {
|
||||
owner_funds: '4000',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '2000',
|
||||
taker_pays_funded: '99.72233516476456'
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '0.049861167582382'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
@@ -2428,7 +2416,8 @@ describe('OrderBook', function() {
|
||||
owner_funds: '3900',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '2000',
|
||||
taker_pays_funded: '99.72233516476456'
|
||||
taker_pays_funded: '99.72233516476456',
|
||||
quality: '0.049861167582382'
|
||||
},
|
||||
{
|
||||
Account: addresses.THIRD_ACCOUNT,
|
||||
@@ -2448,8 +2437,9 @@ describe('OrderBook', function() {
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '1900',
|
||||
taker_pays_funded: '94.73621840652633',
|
||||
owner_funds: '3900'
|
||||
taker_pays_funded: '94.7362184065258',
|
||||
owner_funds: '3900',
|
||||
quality: '0.049861167582382'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -2458,9 +2448,9 @@ describe('OrderBook', function() {
|
||||
case 'book_offers':
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'book_offers',
|
||||
id: void(0),
|
||||
id: undefined,
|
||||
taker_gets: {
|
||||
currency: '0000000000000000000000000000000000000000',
|
||||
currency: '0000000000000000000000000000000000000000'
|
||||
},
|
||||
taker_pays: {
|
||||
currency: '0000000000000000000000005553440000000000',
|
||||
@@ -2488,4 +2478,4 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,41 +1,51 @@
|
||||
/* eslint max-len: 0 */
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var Seed = require('ripple-lib').Seed;
|
||||
var Seed = require('ripple-lib').Seed;
|
||||
|
||||
function assert_helper(seed_json, address_or_nth, expected) {
|
||||
var seed = Seed.from_json(seed_json);
|
||||
var keypair = seed.get_key(address_or_nth, 500);
|
||||
assert.strictEqual(keypair.to_hex_pub(), expected);
|
||||
}
|
||||
|
||||
describe('Seed', function() {
|
||||
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function () {
|
||||
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
||||
});
|
||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function () {
|
||||
var seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||
});
|
||||
it('can generate many addresses', function () {
|
||||
|
||||
var test_data = [
|
||||
// Format:
|
||||
// [passphrase, address, nth-for-seed, expected-public-key]
|
||||
["masterpassphrase", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 0,
|
||||
"0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020"],
|
||||
["masterpassphrase", "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", 1,
|
||||
"02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD"],
|
||||
["masterpassphrase", "rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx", 2,
|
||||
"0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC"],
|
||||
["otherpassphrase", "rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ", 0,
|
||||
"022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B"],
|
||||
["otherpassphrase", "raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S", 5,
|
||||
"03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994"],
|
||||
["yetanotherpassphrase", "rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo", 0,
|
||||
"0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036"],
|
||||
["yetanotherpassphrase", "rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n", 1,
|
||||
"023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC"]
|
||||
['masterpassphrase', 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', 0,
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'],
|
||||
['masterpassphrase', 'r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP', 1,
|
||||
'02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD'],
|
||||
['masterpassphrase', 'rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx', 2,
|
||||
'0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC'],
|
||||
['otherpassphrase', 'rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ', 0,
|
||||
'022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B'],
|
||||
['otherpassphrase', 'raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S', 5,
|
||||
'03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994'],
|
||||
['yetanotherpassphrase', 'rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo', 0,
|
||||
'0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036'],
|
||||
['yetanotherpassphrase', 'rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n', 1,
|
||||
'023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC']
|
||||
];
|
||||
|
||||
function assert_helper(seed_json, address_or_nth, expected) {
|
||||
var seed = Seed.from_json(seed_json);
|
||||
var keypair = seed.get_key(address_or_nth, 500);
|
||||
assert.strictEqual(keypair.to_hex_pub(), expected);
|
||||
}
|
||||
|
||||
for (var nth = 0; nth < test_data.length; nth++) {
|
||||
var seed_json = test_data[nth][0];
|
||||
var address = test_data[nth][1];
|
||||
var nth_for_seed = test_data[nth][2];
|
||||
var expected = test_data[nth][3];
|
||||
|
||||
//`seed.get_key($ripple_address)` is arguably an ill concieved feature
|
||||
// `seed.get_key($ripple_address)` is arguably an ill concieved feature
|
||||
// as it needs to generate `nth` many keypairs and generate hashed public
|
||||
// keys (addresses) for equality tests ??
|
||||
// Would need remote.set_secret(address, private_key_not_seed) ??
|
||||
@@ -43,7 +53,7 @@ describe('Seed', function() {
|
||||
|
||||
// This isn't too bad as it only needs to generate one keypair `seq`
|
||||
assert_helper(seed_json, nth_for_seed, expected);
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -63,7 +73,7 @@ describe('Seed', function() {
|
||||
seed.get_key(address);
|
||||
assert(false, 'should throw an error');
|
||||
} catch(e) {
|
||||
assert.strictEqual(e.message, 'Too many loops looking for KeyPair yielding '+address+' from '+secret);
|
||||
assert.strictEqual(e.message, 'Too many loops looking for KeyPair yielding ' + address + ' from ' + secret);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
var assert = require('assert');
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable max-len*/
|
||||
/* eslint-disable key-spacing*/
|
||||
/* eslint-disable no-spaced-func*/
|
||||
/* eslint-disable space-before-blocks*/
|
||||
/* eslint-disable space-unary-ops*/
|
||||
/* eslint-disable no-multi-spaces*/
|
||||
/* eslint-disable no-void*/
|
||||
/* eslint-disable semi*/
|
||||
/* eslint-disable no-unused-vars*/
|
||||
/* eslint-disable quotes*/
|
||||
|
||||
var assert = require('assert');
|
||||
var SerializedObject = require('ripple-lib').SerializedObject;
|
||||
var sjcl = require('ripple-lib').sjcl;
|
||||
var Amount = require('ripple-lib').Amount;
|
||||
var sjcl = require('ripple-lib').sjcl;
|
||||
|
||||
// Shortcuts
|
||||
var hex = sjcl.codec.hex;
|
||||
@@ -54,6 +68,56 @@ describe('Serialized object', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#from_json(v).to_json() == v -- invalid amount', function(){
|
||||
it('outputs same as passed to from_json', function() {
|
||||
var input_json = {
|
||||
Account: 'rUR9gTCcrUY9fMkz9rwcM9urPREh3LKXoW',
|
||||
Fee: '10',
|
||||
Flags: 0,
|
||||
Sequence: 65,
|
||||
SigningPubKey: '033D0B1FB932E0408C119107483190B61561DCE8529E29CB5D1C69128DA54DA715',
|
||||
TakerGets: '2188313981504612096',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'r9rp9MUFRJVCVLRm3MTmUvSPNBSL3BuEFx',
|
||||
value: '99999999999'
|
||||
},
|
||||
TransactionType: 'OfferCreate',
|
||||
TxnSignature: '304602210085C6AE945643150E6D450CF796E45D74FB24B4E03E964A29CC6AFFEB346C77C80221009BE1B6678CF6C2E61F8F2696144C75AFAF66DF4FC0733DF9118EDEFEEFE33243'
|
||||
};
|
||||
|
||||
assert.throws(function() {
|
||||
SerializedObject.from_json(input_json).to_json();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#from_json(v).to_json() == v -- invalid amount, strict_mode = false', function(){
|
||||
it('outputs same as passed to from_json', function() {
|
||||
var input_json = {
|
||||
Account: 'rUR9gTCcrUY9fMkz9rwcM9urPREh3LKXoW',
|
||||
Fee: '10',
|
||||
Flags: 0,
|
||||
Sequence: 65,
|
||||
SigningPubKey: '033D0B1FB932E0408C119107483190B61561DCE8529E29CB5D1C69128DA54DA715',
|
||||
TakerGets: '2188313981504612096',
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'r9rp9MUFRJVCVLRm3MTmUvSPNBSL3BuEFx',
|
||||
value: '99999999999'
|
||||
},
|
||||
TransactionType: 'OfferCreate',
|
||||
TxnSignature: 'FFFFFF210085C6AE945643150E6D450CF796E45D74FB24B4E03E964A29CC6AFFEB346C77C80221009BE1B6678CF6C2E61F8F2696144C75AFAF66DF4FC0733DF9118EDEFEEFE33243'
|
||||
};
|
||||
|
||||
var strictMode = Amount.strict_mode;
|
||||
Amount.strict_mode = false;
|
||||
var output_json = SerializedObject.from_json(input_json).to_json();
|
||||
assert.deepEqual(input_json, output_json);
|
||||
Amount.strict_mode = strictMode;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#from_json', function() {
|
||||
it('understands TransactionType as a Number', function() {
|
||||
var input_json = {
|
||||
@@ -109,7 +173,6 @@ describe('Serialized object', function() {
|
||||
});
|
||||
|
||||
describe('Memos', function() {
|
||||
|
||||
var input_json;
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -222,7 +285,7 @@ describe('Serialized object', function() {
|
||||
|
||||
|
||||
it('should serialize json with memo - match hex output', function() {
|
||||
var input_json = {
|
||||
input_json = {
|
||||
Flags: 2147483648,
|
||||
TransactionType: 'Payment',
|
||||
Account: 'rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF',
|
||||
|
||||
630
test/transaction-manager-test.js
Normal file
630
test/transaction-manager-test.js
Normal file
@@ -0,0 +1,630 @@
|
||||
'use strict';
|
||||
|
||||
var ws = require('ws');
|
||||
var lodash = require('lodash');
|
||||
var assert = require('assert-diff');
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
var SerializedObject = require('ripple-lib').SerializedObject;
|
||||
var Transaction = require('ripple-lib').Transaction;
|
||||
var TransactionManager = require('../src/js/ripple/transactionmanager')
|
||||
.TransactionManager;
|
||||
|
||||
var LEDGER = require('./fixtures/transactionmanager').LEDGER;
|
||||
var ACCOUNT = require('./fixtures/transactionmanager').ACCOUNT;
|
||||
var ACCOUNT2 = require('./fixtures/transactionmanager').ACCOUNT2;
|
||||
var SUBSCRIBE_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBSCRIBE_RESPONSE;
|
||||
var ACCOUNT_INFO_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.ACCOUNT_INFO_RESPONSE;
|
||||
var TX_STREAM_TRANSACTION = require('./fixtures/transactionmanager')
|
||||
.TX_STREAM_TRANSACTION;
|
||||
var ACCOUNT_TX_TRANSACTION = require('./fixtures/transactionmanager')
|
||||
.ACCOUNT_TX_TRANSACTION;
|
||||
var ACCOUNT_TX_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.ACCOUNT_TX_RESPONSE;
|
||||
var ACCOUNT_TX_ERROR = require('./fixtures/transactionmanager')
|
||||
.ACCOUNT_TX_ERROR;
|
||||
var SUBMIT_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBMIT_RESPONSE;
|
||||
var SUBMIT_TEC_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBMIT_TEC_RESPONSE;
|
||||
var SUBMIT_TER_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBMIT_TER_RESPONSE;
|
||||
var SUBMIT_TEF_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBMIT_TEF_RESPONSE;
|
||||
var SUBMIT_TEL_RESPONSE = require('./fixtures/transactionmanager')
|
||||
.SUBMIT_TEL_RESPONSE;
|
||||
|
||||
describe('TransactionManager', function() {
|
||||
var rippled;
|
||||
var rippledConnection;
|
||||
var remote;
|
||||
var account;
|
||||
var transactionManager;
|
||||
|
||||
beforeEach(function(done) {
|
||||
rippled = new ws.Server({port: 5763});
|
||||
|
||||
rippled.on('connection', function(c) {
|
||||
var ledger = lodash.extend({}, LEDGER);
|
||||
c.sendJSON = function(v) {
|
||||
try {
|
||||
c.send(JSON.stringify(v));
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
c.sendResponse = function(baseResponse, ext) {
|
||||
assert.strictEqual(typeof baseResponse, 'object');
|
||||
assert.strictEqual(baseResponse.type, 'response');
|
||||
c.sendJSON(lodash.extend(baseResponse, ext));
|
||||
};
|
||||
c.closeLedger = function() {
|
||||
c.sendJSON(lodash.extend(ledger, {
|
||||
ledger_index: ++ledger.ledger_index
|
||||
}));
|
||||
};
|
||||
c.on('message', function(m) {
|
||||
m = JSON.parse(m);
|
||||
rippled.emit('request_' + m.command, m, c);
|
||||
});
|
||||
rippledConnection = c;
|
||||
});
|
||||
|
||||
rippled.on('request_subscribe', function(message, c) {
|
||||
if (lodash.isEqual(message.streams, ['ledger', 'server'])) {
|
||||
c.sendResponse(SUBSCRIBE_RESPONSE, {id: message.id});
|
||||
}
|
||||
});
|
||||
rippled.on('request_account_info', function(message, c) {
|
||||
if (message.account === ACCOUNT.address) {
|
||||
c.sendResponse(ACCOUNT_INFO_RESPONSE, {id: message.id});
|
||||
}
|
||||
});
|
||||
|
||||
remote = new Remote({servers: ['ws://localhost:5763']});
|
||||
remote.setSecret(ACCOUNT.address, ACCOUNT.secret);
|
||||
account = remote.account(ACCOUNT.address);
|
||||
transactionManager = account._transactionManager;
|
||||
|
||||
remote.connect(function() {
|
||||
setTimeout(done, 10);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
remote.disconnect(function() {
|
||||
rippled.close();
|
||||
setImmediate(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Normalize transaction', function() {
|
||||
var t1 = TransactionManager.normalizeTransaction(TX_STREAM_TRANSACTION);
|
||||
var t2 = TransactionManager.normalizeTransaction(ACCOUNT_TX_TRANSACTION);
|
||||
|
||||
[t1, t2].forEach(function(t) {
|
||||
assert(t.hasOwnProperty('metadata'));
|
||||
assert(t.hasOwnProperty('tx_json'));
|
||||
assert.strictEqual(t.validated, true);
|
||||
assert.strictEqual(t.ledger_index, 1);
|
||||
assert.strictEqual(t.engine_result, 'tesSUCCESS');
|
||||
assert.strictEqual(t.type, 'transaction');
|
||||
assert.strictEqual(t.tx_json.hash,
|
||||
'01D66ACBD00B2A8F5D66FC8F67AC879CAECF49BC94FB97CF24F66B8406F4C040');
|
||||
});
|
||||
});
|
||||
|
||||
it('Handle received transaction', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
|
||||
transaction.once('success', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
transaction.addId(TX_STREAM_TRANSACTION.transaction.hash);
|
||||
transactionManager.getPending().push(transaction);
|
||||
rippledConnection.sendJSON(TX_STREAM_TRANSACTION);
|
||||
});
|
||||
it('Handle received transaction -- failed', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
|
||||
transaction.once('error', function(err) {
|
||||
assert.strictEqual(err.engine_result, 'tecINSUFF_FEE_P');
|
||||
done();
|
||||
});
|
||||
|
||||
transaction.addId(TX_STREAM_TRANSACTION.transaction.hash);
|
||||
transactionManager.getPending().push(transaction);
|
||||
rippledConnection.sendJSON(lodash.extend({ }, TX_STREAM_TRANSACTION, {
|
||||
engine_result: 'tecINSUFF_FEE_P'
|
||||
}));
|
||||
});
|
||||
it('Handle received transaction -- not submitted', function(done) {
|
||||
rippledConnection.sendJSON(TX_STREAM_TRANSACTION);
|
||||
|
||||
remote.once('transaction', function() {
|
||||
assert(transactionManager.getPending().getReceived(
|
||||
TX_STREAM_TRANSACTION.transaction.hash));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Handle received transaction -- Account mismatch', function(done) {
|
||||
var tx = lodash.extend({ }, TX_STREAM_TRANSACTION);
|
||||
lodash.extend(tx.transaction, {
|
||||
Account: 'rMP2Y5EZrVZdFKsow11NoKTE5FjXuBQd3d'
|
||||
});
|
||||
rippledConnection.sendJSON(tx);
|
||||
|
||||
setImmediate(function() {
|
||||
assert(!transactionManager.getPending().getReceived(
|
||||
TX_STREAM_TRANSACTION.transaction.hash));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Handle received transaction -- not validated', function(done) {
|
||||
var tx = lodash.extend({ }, TX_STREAM_TRANSACTION, {
|
||||
validated: false
|
||||
});
|
||||
rippledConnection.sendJSON(tx);
|
||||
|
||||
setImmediate(function() {
|
||||
assert(!transactionManager.getPending().getReceived(
|
||||
TX_STREAM_TRANSACTION.transaction.hash));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Handle received transaction -- from account_tx', function(done) {
|
||||
var transaction = Transaction.from_json(ACCOUNT_TX_TRANSACTION.tx);
|
||||
transaction.once('success', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
transaction.addId(ACCOUNT_TX_TRANSACTION.tx.hash);
|
||||
transactionManager.getPending().push(transaction);
|
||||
transactionManager._transactionReceived(ACCOUNT_TX_TRANSACTION);
|
||||
});
|
||||
|
||||
it('Adjust pending transaction fee', function(done) {
|
||||
var transaction = new Transaction(remote);
|
||||
transaction.tx_json = ACCOUNT_TX_TRANSACTION.tx;
|
||||
|
||||
transaction.once('fee_adjusted', function(a, b) {
|
||||
assert.strictEqual(a, '10');
|
||||
assert.strictEqual(b, '24');
|
||||
assert.strictEqual(transaction.tx_json.Fee, '24');
|
||||
done();
|
||||
});
|
||||
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
rippledConnection.sendJSON({
|
||||
type: 'serverStatus',
|
||||
load_base: 256,
|
||||
load_factor: 256 * 2,
|
||||
server_status: 'full'
|
||||
});
|
||||
});
|
||||
|
||||
it('Adjust pending transaction fee -- max fee exceeded', function(done) {
|
||||
transactionManager._maxFee = 10;
|
||||
|
||||
var transaction = new Transaction(remote);
|
||||
transaction.tx_json = ACCOUNT_TX_TRANSACTION.tx;
|
||||
|
||||
transaction.once('fee_adjusted', function() {
|
||||
assert(false, 'Fee should not be adjusted');
|
||||
});
|
||||
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
rippledConnection.sendJSON({
|
||||
type: 'serverStatus',
|
||||
load_base: 256,
|
||||
load_factor: 256 * 2,
|
||||
server_status: 'full'
|
||||
});
|
||||
|
||||
setImmediate(done);
|
||||
});
|
||||
|
||||
it('Adjust pending transaction fee -- no local fee', function(done) {
|
||||
remote.local_fee = false;
|
||||
|
||||
var transaction = new Transaction(remote);
|
||||
transaction.tx_json = ACCOUNT_TX_TRANSACTION.tx;
|
||||
|
||||
transaction.once('fee_adjusted', function() {
|
||||
assert(false, 'Fee should not be adjusted');
|
||||
});
|
||||
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
rippledConnection.sendJSON({
|
||||
type: 'serverStatus',
|
||||
load_base: 256,
|
||||
load_factor: 256 * 2,
|
||||
server_status: 'full'
|
||||
});
|
||||
|
||||
setImmediate(done);
|
||||
});
|
||||
|
||||
it('Wait ledgers', function(done) {
|
||||
transactionManager._waitLedgers(3, done);
|
||||
|
||||
for (var i = 1; i <= 3; i++) {
|
||||
rippledConnection.closeLedger();
|
||||
}
|
||||
});
|
||||
|
||||
it('Wait ledgers -- no ledgers', function(done) {
|
||||
transactionManager._waitLedgers(0, done);
|
||||
});
|
||||
|
||||
it('Update pending status', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
transaction.submitIndex = 1;
|
||||
transaction.tx_json.LastLedgerSequence = 10;
|
||||
|
||||
var receivedMissing = false;
|
||||
var receivedLost = false;
|
||||
|
||||
transaction.once('missing', function() {
|
||||
receivedMissing = true;
|
||||
});
|
||||
transaction.once('lost', function() {
|
||||
receivedLost = true;
|
||||
});
|
||||
transaction.once('error', function(err) {
|
||||
assert.strictEqual(err.engine_result, 'tejMaxLedger');
|
||||
assert(receivedMissing);
|
||||
assert(receivedLost);
|
||||
done();
|
||||
});
|
||||
|
||||
transaction.addId(TX_STREAM_TRANSACTION.transaction.hash);
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
rippledConnection.closeLedger();
|
||||
}
|
||||
});
|
||||
|
||||
it('Update pending status -- finalized before max ledger exceeded',
|
||||
function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
transaction.submitIndex = 1;
|
||||
transaction.tx_json.LastLedgerSequence = 10;
|
||||
transaction.finalized = true;
|
||||
|
||||
var receivedMissing = false;
|
||||
var receivedLost = false;
|
||||
|
||||
transaction.once('missing', function() {
|
||||
receivedMissing = true;
|
||||
});
|
||||
transaction.once('lost', function() {
|
||||
receivedLost = true;
|
||||
});
|
||||
transaction.once('error', function() {
|
||||
assert(false, 'Should not err');
|
||||
});
|
||||
|
||||
transaction.addId(TX_STREAM_TRANSACTION.transaction.hash);
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
rippledConnection.closeLedger();
|
||||
}
|
||||
|
||||
setImmediate(function() {
|
||||
assert(!receivedMissing);
|
||||
assert(!receivedLost);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Handle reconnect', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
|
||||
var binaryTx = lodash.extend({}, ACCOUNT_TX_TRANSACTION, {
|
||||
ledger_index: ACCOUNT_TX_TRANSACTION.tx.ledger_index,
|
||||
tx_blob: SerializedObject.from_json(ACCOUNT_TX_TRANSACTION.tx).to_hex(),
|
||||
meta: SerializedObject.from_json(ACCOUNT_TX_TRANSACTION.meta).to_hex()
|
||||
});
|
||||
|
||||
var hash = new SerializedObject(binaryTx.tx_blob).hash(0x54584E00).to_hex();
|
||||
|
||||
transaction.addId(hash);
|
||||
|
||||
transaction.once('success', function(res) {
|
||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
||||
done();
|
||||
});
|
||||
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
rippled.once('request_account_tx', function(m, req) {
|
||||
var response = lodash.extend({}, ACCOUNT_TX_RESPONSE);
|
||||
response.result.transactions = [binaryTx];
|
||||
req.sendResponse(response, {id: m.id});
|
||||
});
|
||||
|
||||
remote.disconnect(remote.connect);
|
||||
});
|
||||
|
||||
it('Handle reconnect -- no matching transaction found', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
|
||||
var binaryTx = lodash.extend({}, ACCOUNT_TX_TRANSACTION, {
|
||||
ledger_index: ACCOUNT_TX_TRANSACTION.tx.ledger_index,
|
||||
tx_blob: SerializedObject.from_json(ACCOUNT_TX_TRANSACTION.tx).to_hex(),
|
||||
meta: SerializedObject.from_json(ACCOUNT_TX_TRANSACTION.meta).to_hex()
|
||||
});
|
||||
|
||||
transactionManager._request = function() {
|
||||
// Resubmitting
|
||||
done();
|
||||
};
|
||||
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
rippled.once('request_account_tx', function(m, req) {
|
||||
var response = lodash.extend({}, ACCOUNT_TX_RESPONSE);
|
||||
response.result.transactions = [binaryTx];
|
||||
req.sendResponse(response, {id: m.id});
|
||||
});
|
||||
|
||||
remote.disconnect(remote.connect);
|
||||
});
|
||||
|
||||
it('Handle reconnect -- account_tx error', function(done) {
|
||||
var transaction = Transaction.from_json(TX_STREAM_TRANSACTION.transaction);
|
||||
transactionManager.getPending().push(transaction);
|
||||
|
||||
transactionManager._resubmit = function() {
|
||||
assert(false, 'Should not resubmit');
|
||||
};
|
||||
|
||||
rippled.once('request_account_tx', function(m, req) {
|
||||
req.sendResponse(ACCOUNT_TX_ERROR, {id: m.id});
|
||||
setImmediate(done);
|
||||
});
|
||||
|
||||
remote.disconnect(remote.connect);
|
||||
});
|
||||
|
||||
it('Submit transaction', function(done) {
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: ACCOUNT.address
|
||||
});
|
||||
|
||||
var receivedInitialSuccess = false;
|
||||
var receivedProposed = false;
|
||||
transaction.once('proposed', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'tesSUCCESS');
|
||||
receivedProposed = true;
|
||||
});
|
||||
transaction.once('submitted', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'tesSUCCESS');
|
||||
receivedInitialSuccess = true;
|
||||
});
|
||||
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
assert.strictEqual(m.tx_blob, SerializedObject.from_json(
|
||||
transaction.tx_json).to_hex());
|
||||
assert.strictEqual(transactionManager.getPending().length(), 1);
|
||||
req.sendResponse(SUBMIT_RESPONSE, {id: m.id});
|
||||
setImmediate(function() {
|
||||
var txEvent = lodash.extend({}, TX_STREAM_TRANSACTION);
|
||||
txEvent.transaction = transaction.tx_json;
|
||||
txEvent.transaction.hash = transaction.hash();
|
||||
rippledConnection.sendJSON(txEvent);
|
||||
});
|
||||
});
|
||||
|
||||
transaction.submit(function(err, res) {
|
||||
assert(!err, 'Transaction submission should succeed');
|
||||
assert(receivedInitialSuccess);
|
||||
assert(receivedProposed);
|
||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Submit transaction -- tec error', function(done) {
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: ACCOUNT.address,
|
||||
set_flag: 'asfDisableMaster'
|
||||
});
|
||||
|
||||
var receivedSubmitted = false;
|
||||
transaction.once('proposed', function() {
|
||||
assert(false, 'Should not receive proposed event');
|
||||
});
|
||||
transaction.once('submitted', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'tecNO_REGULAR_KEY');
|
||||
receivedSubmitted = true;
|
||||
});
|
||||
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
assert.strictEqual(m.tx_blob, SerializedObject.from_json(
|
||||
transaction.tx_json).to_hex());
|
||||
assert.strictEqual(transactionManager.getPending().length(), 1);
|
||||
req.sendResponse(SUBMIT_TEC_RESPONSE, {id: m.id});
|
||||
setImmediate(function() {
|
||||
var txEvent = lodash.extend({}, TX_STREAM_TRANSACTION,
|
||||
SUBMIT_TEC_RESPONSE.result);
|
||||
txEvent.transaction = transaction.tx_json;
|
||||
txEvent.transaction.hash = transaction.hash();
|
||||
rippledConnection.sendJSON(txEvent);
|
||||
});
|
||||
});
|
||||
|
||||
transaction.submit(function(err) {
|
||||
assert(err, 'Transaction submission should not succeed');
|
||||
assert(receivedSubmitted);
|
||||
assert.strictEqual(err.engine_result, 'tecNO_REGULAR_KEY');
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Submit transaction -- ter error', function(done) {
|
||||
var transaction = remote.createTransaction('Payment', {
|
||||
account: ACCOUNT.address,
|
||||
destination: ACCOUNT2.address,
|
||||
amount: '1'
|
||||
});
|
||||
transaction.tx_json.Sequence = ACCOUNT_INFO_RESPONSE.result
|
||||
.account_data.Sequence + 1;
|
||||
|
||||
var receivedSubmitted = false;
|
||||
transaction.once('proposed', function() {
|
||||
assert(false, 'Should not receive proposed event');
|
||||
});
|
||||
transaction.once('submitted', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'terNO_ACCOUNT');
|
||||
receivedSubmitted = true;
|
||||
});
|
||||
|
||||
rippled.on('request_submit', function(m, req) {
|
||||
var deserialized = new SerializedObject(m.tx_blob).to_json();
|
||||
|
||||
switch (deserialized.TransactionType) {
|
||||
case 'Payment':
|
||||
assert.strictEqual(transactionManager.getPending().length(), 1);
|
||||
assert.deepEqual(deserialized, transaction.tx_json);
|
||||
req.sendResponse(SUBMIT_TER_RESPONSE, {id: m.id});
|
||||
break;
|
||||
case 'AccountSet':
|
||||
assert.strictEqual(deserialized.Account, ACCOUNT.address);
|
||||
assert.strictEqual(deserialized.Flags, 2147483648);
|
||||
req.sendResponse(SUBMIT_RESPONSE, {id: m.id});
|
||||
req.closeLedger();
|
||||
break;
|
||||
}
|
||||
});
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
ledger_index: transaction.tx_json.LastLedgerSequence + 1
|
||||
}));
|
||||
});
|
||||
|
||||
transaction.submit(function(err) {
|
||||
assert(err, 'Transaction submission should not succeed');
|
||||
assert.strictEqual(err.engine_result, 'tejMaxLedger');
|
||||
assert(receivedSubmitted);
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
transactionManager.once('sequence_filled', done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Submit transaction -- tef error', function(done) {
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: ACCOUNT.address
|
||||
});
|
||||
|
||||
transaction.tx_json.Sequence = ACCOUNT_INFO_RESPONSE.result
|
||||
.account_data.Sequence - 1;
|
||||
|
||||
var receivedSubmitted = false;
|
||||
var receivedResubmitted = false;
|
||||
transaction.once('proposed', function() {
|
||||
assert(false, 'Should not receive proposed event');
|
||||
});
|
||||
transaction.once('submitted', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'tefPAST_SEQ');
|
||||
receivedSubmitted = true;
|
||||
});
|
||||
|
||||
rippled.on('request_submit', function(m, req) {
|
||||
assert.strictEqual(transactionManager.getPending().length(), 1);
|
||||
assert.strictEqual(m.tx_blob, SerializedObject.from_json(
|
||||
transaction.tx_json).to_hex());
|
||||
req.sendResponse(SUBMIT_TEF_RESPONSE, {id: m.id});
|
||||
});
|
||||
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
ledger_index: transaction.tx_json.LastLedgerSequence + 1
|
||||
}));
|
||||
});
|
||||
|
||||
req.closeLedger();
|
||||
});
|
||||
|
||||
transaction.submit(function(err) {
|
||||
assert(err, 'Transaction submission should not succeed');
|
||||
assert(receivedSubmitted);
|
||||
assert(receivedResubmitted);
|
||||
assert.strictEqual(err.engine_result, 'tejMaxLedger');
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Submit transaction -- tel error', function(done) {
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: ACCOUNT.address
|
||||
});
|
||||
|
||||
var receivedSubmitted = false;
|
||||
var receivedResubmitted = false;
|
||||
transaction.once('proposed', function() {
|
||||
assert(false, 'Should not receive proposed event');
|
||||
});
|
||||
transaction.once('submitted', function(m) {
|
||||
assert.strictEqual(m.engine_result, 'telINSUF_FEE_P');
|
||||
receivedSubmitted = true;
|
||||
});
|
||||
|
||||
rippled.on('request_submit', function(m, req) {
|
||||
assert.strictEqual(transactionManager.getPending().length(), 1);
|
||||
assert.strictEqual(m.tx_blob, SerializedObject.from_json(
|
||||
transaction.tx_json).to_hex());
|
||||
req.sendResponse(SUBMIT_TEL_RESPONSE, {id: m.id});
|
||||
});
|
||||
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
ledger_index: transaction.tx_json.LastLedgerSequence + 1
|
||||
}));
|
||||
});
|
||||
|
||||
req.closeLedger();
|
||||
});
|
||||
|
||||
transaction.submit(function(err) {
|
||||
assert(err, 'Transaction submission should not succeed');
|
||||
assert(receivedSubmitted);
|
||||
assert(receivedResubmitted);
|
||||
assert.strictEqual(err.engine_result, 'tejMaxLedger');
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Submit transaction -- invalid secret', function(done) {
|
||||
remote.setSecret(ACCOUNT.address, ACCOUNT.secret + 'z');
|
||||
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: ACCOUNT.address
|
||||
});
|
||||
|
||||
rippled.once('request_submit', function() {
|
||||
assert(false, 'Should not request submit');
|
||||
});
|
||||
|
||||
transaction.submit(function(err) {
|
||||
assert.strictEqual(err.engine_result, 'tejSecretInvalid');
|
||||
assert.strictEqual(transactionManager.getPending().length(), 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user