Compare commits

...

142 Commits

Author SHA1 Message Date
Geert Weening
ac0a4f521a Bump version to 0.12.5-rc2 2015-05-05 11:20:08 -07:00
Geert Weening
a3380c5cdd Update release notes 2015-05-05 11:15:31 -07:00
Geert Weening
6a6d2a0787 Merge pull request #339 from boxbag/autobridging-empty-orders
[TASK] add short circuit when there are no direct nor autobridged offers
2015-05-05 09:52:42 -07:00
Bo Chen
64809d9ae2 [TASK] add short circuit when there are no direct nor autobridged offers 2015-05-04 13:11:54 -07:00
Chris Clark
d14b38bc91 Merge pull request #335 from clark800/remove-float
Remove float.js and wallet.js
2015-05-01 13:31:17 -07:00
Chris Clark
440dfb5785 Merge pull request #334 from clark800/remove-config
Remove config singleton (global state)
2015-05-01 13:30:36 -07:00
Chris Clark
d4a4b5f4fb Remove float.js and wallet.js 2015-04-30 18:47:43 -07:00
Chris Clark
0c000a7fee Fix lint errors in amount-test.js 2015-04-30 18:31:59 -07:00
Chris Clark
c655c2a20e Remove config singleton (global state) 2015-04-30 18:30:53 -07:00
Geert Weening
3ba5a18b91 Bump version to 0.12.5-rc1 2015-04-29 11:54:36 -07:00
Geert Weening
bdb3415855 Update release notes 2015-04-29 11:54:05 -07:00
Geert Weening
5ef5bdd9d9 Merge pull request #332 from geertweening/develop
Add offer autobridging
2015-04-29 11:47:05 -07:00
Bo Chen
c7bbce8371 [FEATURE] add offer autobridging 2015-04-29 11:28:14 -07:00
Geert Weening
5e2c26a4a2 Merge branch 'release'
Conflicts:
	npm-shrinkwrap.json
	package.json
2015-04-29 08:50:44 -07:00
Geert Weening
631faa20ec Generate shrinkwrap for node 0.10.38 2015-04-29 08:48:39 -07:00
Geert Weening
2db17ba67c Regenerate shrinkwrap using npm-shrinkwrap
cleans up inconsistencies
2015-04-24 16:22:16 -07:00
Geert Weening
50eca42e35 Bump version to 0.12.4 2015-04-24 16:21:32 -07:00
Geert Weening
f327487157 Update release notes 2015-04-24 16:18:05 -07:00
Geert Weening
85b64b7ac3 Bump version to 0.12.4-rc1 2015-04-24 15:44:08 -07:00
Geert Weening
cf17a9e8d6 Update release notes 2015-04-24 15:44:07 -07:00
Chris Clark
71a1282b89 Remove unused dependency: superagent 2015-04-24 15:44:07 -07:00
Chris Clark
85e1f2f47d Fix lint errors in ledger.js 2015-04-24 15:44:07 -07:00
Chris Clark
1f68eba146 Delete unused crypt.js 2015-04-24 15:44:07 -07:00
Chris Clark
d71873442f Update ripple-wallet-generator version 2015-04-24 15:44:07 -07:00
Chris Clark
10ca2da2d6 Fix lint errors in crypt.js, messages.js, remote.js, message-test.js 2015-04-24 15:44:07 -07:00
Chris Clark
c7ba822320 Improve entropy security and increase CCM tag length 2015-04-24 15:44:06 -07:00
Geert Weening
ef3ce46d00 Bump dependency versions
bignumber 2.0.7
lodash 3.6.0
lru-cache 2.5.2
2015-04-24 15:44:06 -07:00
Geert Weening
c4595e03ce Bump version to 0.12.3 2015-04-24 15:44:06 -07:00
Geert Weening
26a7eb456b Bump version to 0.12.3-rc2 2015-04-24 15:44:06 -07:00
Geert Weening
142a85d6a7 Update release notes 2015-04-24 15:44:06 -07:00
Geert Weening
ef51490a1a Bump version to 0.12.3 2015-04-13 16:27:52 -07:00
Geert Weening
c40d643238 Bump version to 0.12.3-rc2 2015-04-10 14:36:47 -07:00
Geert Weening
6f23c88567 Update release notes 2015-04-10 14:36:15 -07:00
Geert Weening
56958a6242 Merge pull request #324 from clark800/develop
Add Amount.scale
2015-04-10 13:25:57 -07:00
Chris Clark
74dac97b36 Add Amount.scale 2015-04-10 12:56:50 -07:00
Geert Weening
2f2e41c781 Bump version to 0.12.3-rc1 2015-04-08 13:36:23 -07:00
Geert Weening
8c872f71c6 Update release notes 2015-04-08 13:36:01 -07:00
wltsmrz
b40b496866 Merge pull request #322 from clark800/rest1.7
Add getLedgerSequence method
2015-04-08 11:20:46 -07:00
Geert Weening
569fec296e Merge pull request #321 from clark800/paranoia
[FIX] Fail if PRNG has not been seeded with at least 256 bits of entr…
2015-04-08 11:16:47 -07:00
Chris Clark
56d8aa797a Fix lint errors in transaction-test.js and transaction-manager-test.js 2015-04-07 18:08:07 -07:00
Chris Clark
fe7e30b737 [FIX] Fail if PRNG has not been seeded with at least 256 bits of entropy before generating ECDSA signatures 2015-04-07 18:08:02 -07:00
Chris Clark
a114281c60 Update comment about the fee base reference constant 2015-04-07 14:34:26 -07:00
Chris Clark
d09548d04d Add getLedgerSequence 2015-04-07 14:34:22 -07:00
Geert Weening
a02b8e3e5c Merge branch 'release' into develop 2015-04-06 13:00:36 -07:00
Geert Weening
2c3f9ca202 bump version to 0.12.2 2015-04-06 12:57:47 -07:00
Geert Weening
587782820d Merge pull request #318 from sublimator/serial-offender
Fix SerializedObject.append performance issue
2015-04-06 12:54:48 -07:00
Nicholas Dudfield
8fad048569 Update verify_ledger_json.js script:
* Set `Amount.strict_mode = false` in verify_ledger_json.js script
* Don't try and calculate hash of non present accountState
* Fix lint issues
2015-04-04 14:51:35 +07:00
Nicholas Dudfield
f7c35b118e Fix SerializedObject.append perf issue:
* Replace array.concat(array2) with Array.prototype.push.apply
2015-04-04 14:51:12 +07:00
Geert Weening
65a669bbb2 Merge pull request #319 from sublimator/lints
eslint fixes
2015-04-04 00:35:57 -07:00
Nicholas Dudfield
9985acc539 Fix linting issues:
* Fix lint violations in various files
* Use per-file comment directives to make new-cap a warning instead of error
  * sjcl.* don't conform to our standards and eslint exemptions are unweildy
2015-04-04 14:23:41 +07:00
Geert Weening
f1f0a43f21 Bump version to 0.12.1-rc1 2015-03-27 16:56:27 -07:00
Geert Weening
6b856c3cc5 Update release notes 2015-03-27 16:55:44 -07:00
Geert Weening
d92888ed73 Merge pull request #317 from ripple/fix-browser-log
Check that Error.stack is available, fixes logging in browser
2015-03-27 16:52:18 -07:00
wltsmrz
0357840654 Lint 2015-03-27 15:54:26 -07:00
wltsmrz
53cae3a66d Check that stack trace is available, fixes logging in browser 2015-03-27 15:49:13 -07:00
Geert Weening
949a1ca4ae Bump version to 0.12.1 2015-03-26 15:12:14 -07:00
Geert Weening
e667536a5b Bump version to 0.12.1-rc6 2015-03-25 12:15:09 -07:00
Geert Weening
dde000a4bb Update release notes 2015-03-25 12:14:30 -07:00
Geert Weening
aa1f5a8e7d Merge pull request #316 from clark800/fix/to_human_small_number
[FIX] Make Amount.to_human return correct results for very small numbers
2015-03-25 12:09:13 -07:00
Chris Clark
bfbfcc2894 Fix lint errors in amount-test.js 2015-03-25 11:28:13 -07:00
Chris Clark
6abfa759aa [FIX] Make Amount.to_human return correct results for very small numbers 2015-03-25 11:19:25 -07:00
Alan Cohen
7cbac2e757 Merge pull request #315 from ripple/readme-link-fix
Fix link in GUIDES.md
2015-03-24 11:04:31 -07:00
Alan Cohen
1012381d3d Update GUIDES.md 2015-03-24 09:17:43 -07:00
Geert Weening
6de96f62df Merge pull request #314 from lumberj/addflags
[TASK] Add GlobalFreeze and NoFreeze flags
2015-03-23 12:23:05 -07:00
Alan Cohen
e2ed2bdbf6 [TASK] Add GlobalFreeze and NoFreeze flags
See: https://wiki.ripple.com/Ledger_Format#AccountRoot
2015-03-20 14:31:47 -07:00
Geert Weening
e248c54aa5 Merge pull request #312 from clark800/mocha-timeout
Increase timeout for mocha tests because sometimes tests fail on travis ...
2015-03-19 09:09:46 -07:00
Chris Clark
1c9635edad Increase timeout for mocha tests because sometimes tests fail on travis due to taking too long 2015-03-18 17:27:06 -07:00
Geert Weening
25cf6c52e4 Bump version to 0.12.1-rc5 2015-03-18 17:24:24 -07:00
Geert Weening
7859ef6145 Update release notes 2015-03-18 17:23:46 -07:00
Geert Weening
6efaa4ac7e Merge pull request #311 from vhpoet/patch-4
[FIX] Amount: clone in ratio_human, product_human
2015-03-18 17:21:21 -07:00
Vahe Hovhannisyan
19e17a8431 [FIX] Amount: clone in ratio_human, product_human
Amount.ratio_human and Amount.product_human should change and return the cloned Amount object.
2015-03-18 17:09:17 -07:00
Geert Weening
c865ae9734 Bump version to 0.12.1-rc4 2015-03-17 11:19:34 -07:00
Geert Weening
6959f74073 Update release notes 2015-03-17 11:14:36 -07:00
Chris Clark
9f4d21e976 Merge pull request #310 from darkdarkdragon/release-base-decode-fix
[FIX] fix Base:decode
2015-03-17 10:12:07 -07:00
Ivan Tivonenko
719f39c01c [FIX] fix Base:decode
check for invalid input in decoded data
2015-03-17 05:53:02 +02:00
Geert Weening
25bb9c7320 Bump version to 0.12.1-rc3 2015-03-11 11:49:30 -07:00
Geert Weening
a160e16abd Update release notes 2015-03-11 11:48:46 -07:00
Geert Weening
ec31841aa5 Merge pull request #305 from geertweening/add-default-rippling-flag
Add DefaultRipple account flag
2015-03-11 11:41:49 -07:00
wltsmrz
3e249902c4 Add DefaultRipple account flag 2015-03-10 11:44:50 -07:00
Geert Weening
21bb766f06 Update release notes 2015-03-10 11:30:34 -07:00
Geert Weening
a883151400 Bump version to 0.12.1-rc2 2015-03-09 10:06:04 -07:00
Geert Weening
3c7fe82cbd Regenerate shrinkwrap 2015-03-09 10:05:49 -07:00
Geert Weening
899fc09704 Update release notes 2015-03-09 10:03:32 -07:00
Geert Weening
daa45a44b9 Merge pull request #297 from ripple/add-log-info
Add filename and line number to log, use log.warn() for deprecations
2015-03-09 09:45:18 -07:00
Geert Weening
52494628c3 Merge pull request #301 from ripple/amount-strict-mode
Add Amount.strict_mode for toggling range validation
2015-03-06 19:54:58 -08:00
Geert Weening
dbf5d21b72 Merge pull request #303 from clark800/morebasetest
[TASK] Fix issues in base.js and add more tests
2015-03-06 19:53:27 -08:00
wltsmrz
441bd4dfbf Lint remote.js 2015-03-06 19:06:24 -08:00
wltsmrz
8452f05dda Lint log.js 2015-03-06 18:36:27 -08:00
Chris Clark
0d2325e646 [TASK] Fix seed-test.js lint errors 2015-03-06 17:41:42 -08:00
wltsmrz
90329d3d73 Add filename and line number to log, use log.warn() for deprecations 2015-03-06 17:35:24 -08:00
Chris Clark
ca83a142f8 [TASK] Fix issues in base.js and add more tests 2015-03-06 17:33:52 -08:00
wltsmrz
d3b2d3d5c5 Merge pull request #304 from clark800/fix/travis_eslint2
[FIX] Fix bug in .travis.yml generation of list of modified files in pul...
2015-03-06 17:18:55 -08:00
Chris Clark
255177487c [FIX] Fix bug in .travis.yml generation of list of modified files in pull request 2015-03-06 15:53:52 -08:00
wltsmrz
ed0b75bcde Use new Amount(NaN) rather than Amount.NaN() 2015-03-06 15:38:44 -08:00
Chris Clark
06500a7909 Merge pull request #302 from clark800/fix/travis_eslint
[FIX] Don't lint deleted files and pull eslintrc from javascript-style-g...
2015-03-06 13:32:47 -08:00
wltsmrz
6e16bf68ae Lint serializedobject test 2015-03-06 13:01:57 -08:00
Chris Clark
ad22480117 [FIX] Don't lint deleted files and pull eslintrc from javascript-style-guide repo to ensure that it is up to date 2015-03-06 11:20:20 -08:00
wltsmrz
2fcd09072f Lint serializedtypes.js 2015-03-06 01:10:22 -08:00
wltsmrz
f0c785b196 Lint amount.js 2015-03-06 01:06:58 -08:00
Geert Weening
84fe76bada Merge pull request #296 from ripple/fix-currency-parsing
Fix currency parsing of non-alphanumeric and no-currency currencies
2015-03-05 22:32:04 -08:00
wltsmrz
b5ed8f59a7 Add Amount.strict_mode for toggling range validation 2015-03-05 22:21:35 -08:00
Geert Weening
52526f90d7 Merge pull request #298 from clark800/basetest
[TEST] Add unit tests for Base
2015-03-05 13:08:56 -08:00
wltsmrz
99e6e81e65 Merge pull request #299 from boxbag/fix-taker-pays-funded
[FIX] fix taker pays funded calculation
2015-03-05 11:17:43 -08:00
Bo Chen
5af824f5cf [FIX] fix taker pays funded calculation
When calling `parseInt` on a string with scientific notation, it ignores the exponents.
2015-03-05 09:43:27 -08:00
wltsmrz
2166bb2e88 Fix currency parsing of non-alphanumeric and no-currency currencies 2015-03-04 19:10:39 -08:00
Chris Clark
ae884c0200 [TEST] Add unit tests for Base 2015-03-04 13:02:36 -08:00
wltsmrz
423ec7d08a Merge pull request #294 from clark800/baseconverter
Refactor base conversion
2015-03-03 20:23:08 -08:00
Chris Clark
914cd6ecb2 Add unit tests for convertBase 2015-03-03 12:22:42 -08:00
Geert Weening
f221c82859 Merge pull request #293 from boxbag/offer-quality
Offer quality
2015-03-03 10:52:38 -08:00
wltsmrz
d57be723e6 Merge pull request #295 from stevenzeiler/ripple-wallet-version
[TASK] Bump ripple-wallet-generator patch version
2015-03-02 16:58:40 -08:00
Steven Zeiler
777554809a [TASK] Bump ripple-wallet-generator patch version 2015-03-02 16:53:52 -08:00
Chris Clark
f2b63fa4a8 Refactor base conversion 2015-02-27 21:28:23 -08:00
Bo Chen
4d06ce7454 [FIX] fix eslint errors 2015-02-27 16:12:06 -08:00
Bo Chen
8da6ec5fa3 [TASK] reverse skip of order book tests 2015-02-27 09:18:01 -08:00
Bo Chen
2a5a8b498d [FIX] fix handling of quality in order book 2015-02-27 09:18:01 -08:00
Bo Chen
a9b7d7d793 Merge pull request #292 from ripple/revert-286-set-user-agent
Revert "Set User-Agent Header with ripple-lib/{version}"
2015-02-27 09:17:27 -08:00
Bo Chen
6578cf5dd7 Revert "Set User-Agent Header with ripple-lib/{version}" 2015-02-26 14:40:07 -08:00
Geert Weening
2e21e8a43c Merge pull request #290 from ripple/transactionmanager-tests
Add TransactionManager test
2015-02-26 12:48:42 -08:00
Geert Weening
176e1fd9d4 Merge pull request #291 from ripple/update-binary-format
Update binary format
2015-02-26 11:41:34 -08:00
wltsmrz
c3b274b18f Add TransactionManager test 2015-02-25 18:49:59 -08:00
wltsmrz
8e134918fb Update binary format 2015-02-25 18:39:42 -08:00
wltsmrz
2b531d2a1f Disable breaking OrderBook tests 2015-02-25 01:36:12 -08:00
Geert Weening
87317dd54a Merge pull request #289 from ripple/fix-setregularkey-transaction-format
Fix RegularKey requirement in SetRegularKey transaction format
2015-02-24 09:23:48 -08:00
wltsmrz
618548c88d Merge pull request #273 from clark800/feature/eslint
[TASK] Add eslint enforcement to travis.yml
2015-02-23 17:50:41 -08:00
Chris Clark
b62f42006c [TASK] Add eslint enforcement to travis.yml 2015-02-23 16:34:22 -08:00
wltsmrz
c275174f27 Fix RegularKey requirement in SetRegularKey transaction format 2015-02-23 13:09:50 -08:00
Geert Weening
af4ed295e0 Bump version to 0.12.1-rc1 2015-02-23 09:25:42 -08:00
Geert Weening
7614a03ea8 Update release notes 2015-02-23 09:24:13 -08:00
Geert Weening
d9527726b6 Merge pull request #285 from boxbag/orderbook-fix
[FIX] fix order funded amount calculation
2015-02-23 09:16:44 -08:00
Geert Weening
05f4099709 Merge pull request #288 from ripple/fix-setfee-transaction-format
Fix Features field requirement in SetFee transaction format
2015-02-23 09:16:13 -08:00
wltsmrz
a20a649013 Fix Features field requirement in SetFee transaction format 2015-02-20 15:01:05 -08:00
Geert Weening
0e3e64105c Merge pull request #287 from clark800/bignumber_update
[TASK] Update bignumber.js and use new feature to simplify our code
2015-02-20 14:50:59 -08:00
Bo Chen
b2cdb1a6ae [FIX] fix order funded amount calculation 2015-02-20 14:49:47 -08:00
Alan Cohen
812432db96 Merge pull request #286 from lumberj/set-user-agent
Set User-Agent Header with ripple-lib/{version}
2015-02-20 10:23:32 -08:00
Alan Cohen
5b2c4aef2d Set User-Agent Header with ripple-lib/{version}
- This would be helpful for us to analyze usage of ripple-lib for anyone
  using the public rippled
2015-02-20 09:06:02 -08:00
Chris Clark
b7ccf424f4 [TASK] Update bignumber.js and use new feature to simplify our code 2015-02-19 19:51:05 -08:00
Geert Weening
77d5db168b Merge pull request #283 from clark800/feature/amount_sanity
[TASK] Disable parsing native amounts in foating point format
2015-02-17 14:29:38 -08:00
Chris Clark
e80cd1ff55 [TASK] Disable parsing native amounts in foating point format 2015-02-17 14:03:23 -08:00
Geert Weening
4ff25a21f6 Merge pull request #282 from boxbag/orderbook
[TEST] test and refactor orderbooks
2015-02-17 13:35:55 -08:00
Bo Chen
f184a71360 [TEST] test and refactor orderbooks 2015-02-13 13:37:57 -08:00
wltsmrz
fc38a9853d Merge pull request #281 from shekenahglory/develop
fix handling of false parameters in requestLedger
2015-02-11 14:24:27 -08:00
Matthew Fettig
6023efed41 fix handling of false parameters in requestLedger 2015-02-11 14:11:24 -08:00
60 changed files with 8515 additions and 4708 deletions

2
.gitignore vendored
View File

@@ -59,3 +59,5 @@ out/
# Ignore perf test cache
scripts/cache
eslintrc

View File

@@ -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

View File

@@ -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']);

View File

@@ -1,3 +1,77 @@
##0.12.15
+ [Add offer autobridging](https://github.com/ripple/ripple-lib/commit/c7bbce83719c1e8c6a4fae5ca850e7515db1a4a5)
+ [Fix empty order edgecase](https://github.com/ripple/ripple-lib/commit/64809d9ae23dc24f47accd4b4788b48f49880d3e)
+ [Remove unused float.js and wallet.js](https://github.com/ripple/ripple-lib/commit/d4a4b5f4fbbf09677a59ce81bace35c6426a2fda)
+ [Remove config singleton to reduce global state](https://github.com/ripple/ripple-lib/commit/c655c2a20ee5d150a4b5a1b6717b9fb81f636025)
##0.12.4
+ [Improve entropy security](https://github.com/ripple/ripple-lib/commit/c7ba822320880037796f57876d1abb4e525648ed)
+ [Remove unused crypt.js file](https://github.com/ripple/ripple-lib/commit/1f68eba1461bca03a4d22872450d15ae5a185334)
##0.12.3
+ [Add getLedgerSequence to Remote](https://github.com/ripple/ripple-lib/commit/d09548d04d3238fca653d482ec1d5faa7254559a)
+ [Improve randomness when generating ECDSA signatures](https://github.com/ripple/ripple-lib/commit/fe7e30b737ead6e71adfa466f5835ba546feab31)
+ [Improve SerializedObject.append performance](https://github.com/ripple/ripple-lib/commit/f7c35b118ebba549a64bcaa1a0629385ec6dbf6f)
+ [Add `Amount.scale`. Multiply an amounts value by a scale factor](https://github.com/ripple/ripple-lib/commit/74dac97b368493056474468520f05671f458a69f)
##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 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)
+ [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
**Breaking Changes**
@@ -14,8 +88,8 @@
- [Handle invalid input in parse_human - c8f18c8c](https://github.com/ripple/ripple-lib/commit/c8f18c8c8590b7b48e370e0325b6677b7720294f)
- [Check for null in isNumber - b86790c8](https://github.com/ripple/ripple-lib/commit/b86790c8543c239a532fd7697d4652829019d385)
- [Cleanup amount.js - d0fb291c](https://github.com/ripple/ripple-lib/commit/d0fb291c4e330193a244902156f1d74730da357d)
**Changes**
+ [Add deprecation warnings to request constructors. The first argument to request constructor functions should be an object containing request properties](https://github.com/ripple/ripple-lib/commit/35d76b3520934285f80059c1badd6c522539104c)
@@ -30,13 +104,13 @@
+ [Bumped dependencies](https://github.com/ripple/ripple-lib/commit/f9bc7cc746b44b24b61bbe260ae2e9d9617286da)
##0.11.0
+ [Track the funded status of an order based on cumulative account orders](https://github.com/ripple/ripple-lib/commit/67d39737a4d5e0fcd9d9b47b9083ee00e5a9e652) and [67d3973](https://github.com/ripple/ripple-lib/commit/b6b99dde022e1e14c4797e454b1d7fca50e49482)
+ Remove blobvault client from ripple-lib, use the [`ripple-vault-client`](https://github.com/ripple/ripple-vault-client) instead [9b3d62b7](https://github.com/ripple/ripple-lib/commit/9b3d62b765c4c25beae6eb0fa57ef3a07f2581b1)
+ Remove blobvault client from ripple-lib, use the [`ripple-vault-client`](https://github.com/ripple/ripple-vault-client) instead [9b3d62b7](https://github.com/ripple/ripple-lib/commit/9b3d62b765c4c25beae6eb0fa57ef3a07f2581b1)
+ [Add support for `ledger` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/34c0677c453c409ef0a5b351959abdc176d3bacb)
@@ -75,13 +149,13 @@ are locally determined to have expired: `tejMaxLedger`.
+ [Improve memo support](https://github.com/ripple/ripple-lib/commit/1704ac4ae144c0ce54afad86f644c75a632080b1)
- Add `MemoFormat` property for memo
- Enforce `MemoFormat` and `MemoType` to be valid ASCII
- Support `text` and `json` MemoFormat
- Support `text` and `json` MemoFormat
+ [Update jscl library](https://github.com/ripple/ripple-lib/commit/3204998fcb6f31d6c90532a737a4adb8a1e420f6)
- Improved entropy by taking advantage of platform crypto
- Use jscl's k256 curve instead of altering the c256 curve with k256 configuration
- **deprecated:** the c256 curve is linked to the k256 curve to provide backwards compatibility, this link will be removed in the future
+ [Fix empty queue check on reconnect](https://github.com/ripple/ripple-lib/commit/3c21994adcf72d1fbd87d453ceb917f9ad6df4ec)
##0.9.4
@@ -110,7 +184,7 @@ are locally determined to have expired: `tejMaxLedger`.
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add max_fee setter to transactions to set max fee the submitter is willing to pay] (https://github.com/ripple/ripple-lib/commit/24587fab9c8ad3840d7aa345a7037b48839e09d7)
@@ -126,7 +200,7 @@ var options = {
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;

View File

@@ -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 */

View File

@@ -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" ]
}
}

161
npm-shrinkwrap.json generated
View File

@@ -1,181 +1,70 @@
{
"name": "ripple-lib",
"version": "0.12.0",
"version": "0.12.5-rc2",
"npm-shrinkwrap-version": "5.4.0",
"node-version": "v0.10.38",
"dependencies": {
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
},
"bignumber.js": {
"version": "2.0.0",
"from": "bignumber.js@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.0.tgz"
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"lodash": {
"version": "3.1.0",
"from": "lodash@>=3.1.0 <4.0.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.1.0.tgz"
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.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"
},
"superagent": {
"version": "0.18.2",
"from": "superagent@>=0.18.0 <0.19.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-0.18.2.tgz",
"dependencies": {
"qs": {
"version": "0.6.6",
"from": "qs@0.6.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz"
},
"formidable": {
"version": "1.0.14",
"from": "formidable@1.0.14",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz"
},
"mime": {
"version": "1.2.11",
"from": "mime@1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"component-emitter": {
"version": "1.1.2",
"from": "component-emitter@1.1.2",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz"
},
"methods": {
"version": "1.0.1",
"from": "methods@1.0.1",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz"
},
"cookiejar": {
"version": "2.0.1",
"from": "cookiejar@2.0.1",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz"
},
"debug": {
"version": "1.0.4",
"from": "debug@>=1.0.1 <1.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
}
}
},
"reduce-component": {
"version": "1.0.1",
"from": "reduce-component@1.0.1",
"resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz"
},
"form-data": {
"version": "0.1.3",
"from": "form-data@0.1.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.7",
"from": "combined-stream@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
"from": "delayed-stream@0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
}
}
},
"readable-stream": {
"version": "1.0.27-1",
"from": "readable-stream@1.0.27-1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
}
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
},
"ws": {
"version": "0.7.1",
"from": "ws@>=0.7.1 <0.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.1.tgz",
"dependencies": {
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.1",
"from": "ultron@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.1.tgz"
},
"bufferutil": {
"version": "1.0.1",
"from": "bufferutil@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.0.1.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0"
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.6.1",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.1.tgz"
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
}
}
},
"options": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.1.tgz"
},
"utf-8-validate": {
"version": "1.0.1",
"from": "utf-8-validate@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.0.1.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0"
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.6.1",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.1.tgz"
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.12.0",
"version": "0.12.5-rc2",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
@@ -16,18 +16,17 @@
},
"dependencies": {
"async": "~0.9.0",
"bignumber.js": "^2.0.0",
"bignumber.js": "^2.0.3",
"extend": "~1.2.1",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"ripple-wallet-generator": "1.0.1",
"ws": "~0.7.1",
"superagent": "^0.18.0"
"ripple-wallet-generator": "^1.0.3",
"ws": "~0.7.1"
},
"devDependencies": {
"assert-diff": "0.0.4",
"assert-diff": "^1.0.1",
"coveralls": "~2.10.0",
"eslint": "^0.13.0",
"eslint": "^0.18.0",
"gulp": "~3.8.10",
"gulp-bump": "~0.1.13",
"gulp-clean-dest": "^0.1.0",
@@ -50,9 +49,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": {

View File

@@ -1,31 +1,34 @@
'use strict';
var fs = require('fs');
var Amount = require('../src/js/ripple').Amount;
var Ledger = require('../src/js/ripple/ledger').Ledger;
function parse_options(from, flags) {
var argv = from.slice(),
opts = {argv:argv};
opts_ = {argv: argv};
flags.forEach(function(f) {
// Do we have the flag?
var flag_index = argv.indexOf('--' + f);
// normalize the name of the flag
f = f.replace('-', '_');
// opts has Boolean value for normalized flag key
opts[f] = !!~flag_index;
if (opts[f]) {
// opts_ has Boolean value for normalized flag key
opts_[f] = flag_index !== -1;
if (opts_[f]) {
// remove the flag from the argv
argv.splice(flag_index, 1);
}
});
return opts;
return opts_;
}
var opts = parse_options(process.argv.slice(2), // remove `node` and `this.js`
['sanity-test']);
if (opts.argv.length < 1) {
console.error("Usage: scripts/verify_ledger_json path/to/ledger.json");
console.error(" optional: --sanity-test (json>binary>json>binary)");
console.error('Usage: scripts/verify_ledger_json path/to/ledger.json');
console.error(' optional: --sanity-test (json>binary>json>binary)');
process.exit(1);
}
@@ -36,14 +39,27 @@ var ledger = Ledger.from_json(JSON.parse(json));
// before finally serializing for hashing. This is mostly to expose any issues
// with ripple-libs binary <--> json codecs.
if (opts.sanity_test) {
console.log("All accountState nodes will be processed from " +
"json->binary->json->binary. This may take some time " +
"with large ledgers.");
console.log('All accountState nodes will be processed from ' +
'json->binary->json->binary. This may take some time ' +
'with large ledgers.');
}
console.log("Transaction hash in header: " + ledger.ledger_json.transaction_hash);
console.log("Calculated transaction hash: " + ledger.calc_tx_hash().to_hex());
console.log("Account state hash in header: " + ledger.ledger_json.account_hash);
console.log("Calculated account state hash: " + ledger.calc_account_hash(
{sanity_test:opts.sanity_test})
.to_hex());
// To recompute the hashes of some ledgers, we must allow values that slipped in
// before strong policies were in place.
Amount.strict_mode = false;
console.log('Transaction hash in header: ' +
ledger.ledger_json.transaction_hash);
console.log('Calculated transaction hash: ' +
ledger.calc_tx_hash().to_hex());
console.log('Account state hash in header: ' +
ledger.ledger_json.account_hash);
if (ledger.ledger_json.accountState) {
console.log('Calculated account state hash: ' +
ledger.calc_account_hash({sanity_test: opts.sanity_test})
.to_hex());
} else {
console.log('Ledger has no accountState');
}

View File

@@ -1,22 +1,21 @@
'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 BigNumber = require('./bignumber');
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({
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
DECIMAL_PLACES: 40
});
function isInteger(number) {
return parseInt(number) === number;
}
function ensureDecimalPoint(value) {
return isInteger(value) ? String(value) + '.0' : value;
}
function inverse(number) {
return (new BigNumber(number)).toPower(-1);
@@ -27,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);
@@ -130,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));
@@ -151,6 +156,10 @@ Amount.prototype.multiply = function(multiplicand) {
return this._copy(this._value.times(multiplyBy));
};
Amount.prototype.scale = function(scaleFactor) {
return this._copy(this._value.times(scaleFactor));
};
Amount.prototype.divide = function(divisor) {
var divisorAmount = Amount.from_json(divisor);
if (!this.is_valid()) {
@@ -183,24 +192,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;
denominator = Amount.from_json(ensureDecimalPoint(denominator));
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
@@ -242,21 +253,21 @@ 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) {
opts = opts || {};
factor = Amount.from_json(ensureDecimalPoint(factor));
factor = Amount.from_json(factor);
// 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
@@ -283,6 +294,7 @@ Amount.prototype.product_human = function(factor, opts) {
/**
* Turn this amount into its inverse.
*
* @return {Amount} self
* @private
*/
Amount.prototype._invert = function() {
@@ -304,7 +316,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
@@ -325,25 +338,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) {
var value = this._value;
this._value = BigNumber.withRoundingMode(roundingMode, function() {
return new BigNumber(value.toPrecision(16));
});
} 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;
}
@@ -376,7 +388,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);
};
@@ -384,10 +396,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;
};
@@ -430,7 +442,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() {
@@ -457,11 +471,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) {
@@ -473,7 +491,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;
@@ -487,7 +507,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) {
@@ -501,10 +521,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();
@@ -522,7 +542,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;
};
@@ -539,41 +559,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');
@@ -614,7 +636,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()));
}
@@ -623,9 +646,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;
};
@@ -634,21 +657,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;
@@ -687,14 +711,13 @@ 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' && j.match(/^-?\d*(\.\d{0,6})?$/)) {
if (j && typeof j === 'string' && !isNaN(j)) {
if (j.indexOf('.') >= 0) {
throw new Error('Native amounts must be specified in integer drops');
}
var value = new BigNumber(j);
this._is_native = true;
if (j.indexOf('.') >= 0) {
this._set_value(value);
} else {
this._set_value(value.dividedBy(Amount.bi_xns_unit));
}
this._set_value(value.dividedBy(Amount.bi_xns_unit));
} else {
this._set_value(new BigNumber(NaN));
}
@@ -711,7 +734,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;
};
@@ -748,18 +771,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) : '');
};
/**
@@ -770,7 +793,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.
*/
@@ -788,21 +811,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 || {};
@@ -811,16 +836,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*/, '');
@@ -854,7 +882,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
@@ -889,7 +919,7 @@ Amount.prototype.to_human = function(opts) {
}
var formatted = '';
if(isNegative && opts.signed !== false) {
if (isNegative && opts.signed !== false) {
formatted += '-';
}
@@ -911,17 +941,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) {
@@ -961,16 +993,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

View File

@@ -0,0 +1,435 @@
'use strict';
var _ = require('lodash');
var assert = require('assert');
var UInt160 = require('./uint160').UInt160;
var Amount = require('./amount').Amount;
var Utils = require('./orderbookutils');
function assertValidNumber(number, message) {
assert(!_.isNull(number) && !isNaN(number), message);
}
function assertValidLegOneOffer(legOneOffer, message) {
assert(legOneOffer);
assert.strictEqual(typeof legOneOffer, 'object', message);
assert.strictEqual(typeof legOneOffer.TakerPays, 'object', message);
assertValidNumber(legOneOffer.TakerGets, message);
}
function AutobridgeCalculator(currencyGets, currencyPays,
legOneOffers, legTwoOffers) {
this._currencyGets = currencyGets;
this._currencyPays = currencyPays;
this.legOneOffers = _.cloneDeep(legOneOffers);
this.legTwoOffers = _.cloneDeep(legTwoOffers);
this._ownerFundsLeftover = {};
}
/**
* Calculates an ordered array of autobridged offers by quality
*
* @return {Array}
*/
AutobridgeCalculator.prototype.calculate = function() {
var legOnePointer = 0;
var legTwoPointer = 0;
var offersAutobridged = [];
this.clearOwnerFundsLeftover();
while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) {
var legOneOffer = this.legOneOffers[legOnePointer];
var legTwoOffer = this.legTwoOffers[legTwoPointer];
var leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account);
var autobridgedOffer;
if (legOneOffer.Account === legTwoOffer.Account) {
this.unclampLegOneOwnerFunds(legOneOffer);
} else if (!legOneOffer.is_fully_funded && !leftoverFunds.is_zero()) {
this.adjustLegOneFundedAmount(legOneOffer);
}
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
if (legOneTakerGetsFunded.is_zero()) {
legOnePointer++;
continue;
}
if (legTwoTakerPaysFunded.is_zero()) {
legTwoPointer++;
continue;
}
if (legOneTakerGetsFunded.compareTo(legTwoTakerPaysFunded) > 0) {
autobridgedOffer = this.getAutobridgedOfferWithClampedLegOne(
legOneOffer,
legTwoOffer
);
legTwoPointer++;
} else if (legTwoTakerPaysFunded.compareTo(legOneTakerGetsFunded) > 0) {
autobridgedOffer = this.getAutobridgedOfferWithClampedLegTwo(
legOneOffer,
legTwoOffer
);
legOnePointer++;
} else {
autobridgedOffer = this.getAutobridgedOfferWithoutClamps(
legOneOffer,
legTwoOffer
);
legOnePointer++;
legTwoPointer++;
}
offersAutobridged.push(autobridgedOffer);
}
return offersAutobridged;
};
/**
* In this case, the output from leg one is greater than the input to leg two.
* Therefore, we must effectively clamp leg one output to leg two input.
*
* @param {Object} legOneOffer
* @param {Object} legTwoOffer
*
* @return {Object}
*/
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne =
function(legOneOffer, legTwoOffer) {
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
var legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
var autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
var autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality);
if (legOneOffer.Account === legTwoOffer.Account) {
var legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
var updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded);
this.setLegOneTakerGets(legOneOffer, updatedTakerGets);
this.clampLegOneOwnerFunds(legOneOffer);
} else {
// Update funded amount since leg one offer was not completely consumed
var updatedTakerGetsFunded = legOneTakerGetsFunded
.subtract(legTwoTakerPaysFunded);
this.setLegOneTakerGetsFunded(legOneOffer, updatedTakerGetsFunded);
}
return this.formatAutobridgedOffer(
autobridgedTakerGets,
autobridgedTakerPays
);
};
/**
* In this case, the input from leg two is greater than the output to leg one.
* Therefore, we must effectively clamp leg two input to leg one output.
*
* @param {Object} legOneOffer
* @param {Object} legTwoOffer
*
* @return {Object}
*/
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo =
function(legOneOffer, legTwoOffer) {
var legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
var legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
var legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets);
var autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality);
var autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
// Update funded amount since leg two offer was not completely consumed
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer)
.subtract(autobridgedTakerGets)
.to_text();
legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded
.subtract(legOneTakerGetsFunded)
.to_text();
return this.formatAutobridgedOffer(
autobridgedTakerGets,
autobridgedTakerPays
);
};
/**
* In this case, the output from leg one and the input to leg two are the same.
* We do not need to clamp either.
* @param {Object} legOneOffer
* @param {Object} legTwoOffer
*
* @return {Object}
*/
AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps =
function(legOneOffer, legTwoOffer) {
var autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
var autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
return this.formatAutobridgedOffer(
autobridgedTakerGets,
autobridgedTakerPays
);
};
/**
* Clear owner funds leftovers
*/
AutobridgeCalculator.prototype.clearOwnerFundsLeftover = function() {
this._ownerFundsLeftover = {};
};
/**
* Reset owner funds leftovers for an account to 0
*
* @param {String} account
*
* @return {Amount}
*/
AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
this._ownerFundsLeftover[account] = Utils.normalizeAmount('0');
return this._ownerFundsLeftover[account];
};
/**
* Retrieve leftover funds found after clamping leg one by account
*
* @param {String} account
*
* @return {Amount}
*/
AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
var amount = this._ownerFundsLeftover[account];
if (!amount) {
amount = Utils.normalizeAmount('0');
}
return amount;
};
/**
* Add funds to account's leftover funds
*
* @param {String} account
* @param {Amount} amount
*
* @return {Amount}
*/
AutobridgeCalculator.prototype.addLeftoverOwnerFunds =
function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(amount instanceof Amount, 'Amount is invalid');
this._ownerFundsLeftover[account] = this.getLeftoverOwnerFunds(account)
.add(amount);
return this._ownerFundsLeftover[account];
};
/**
* Set account's leftover funds
*
* @param {String} account
* @param {Amount} amount
*/
AutobridgeCalculator.prototype.setLeftoverOwnerFunds =
function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(amount instanceof Amount, 'Amount is invalid');
this._ownerFundsLeftover[account] = amount;
};
/**
* Format an autobridged offer and compute synthetic values (e.g. quality)
*
* @param {Amount} takerGets
* @param {Amount} takerPays
*
* @return {Object}
*/
AutobridgeCalculator.prototype.formatAutobridgedOffer =
function(takerGets, takerPays) {
assert(takerGets instanceof Amount, 'Autobridged taker gets is invalid');
assert(takerPays instanceof Amount, 'Autobridged taker pays is invalid');
var autobridgedOffer = {};
var quality = takerPays.divide(takerGets);
autobridgedOffer.TakerGets = {
value: takerGets.to_text(),
currency: this._currencyGets.to_hex(),
issuer: this._issuerGets
};
autobridgedOffer.TakerPays = {
value: takerPays.to_text(),
currency: this._currencyPays.to_hex(),
issuer: this._issuerPays
};
autobridgedOffer.quality = quality.to_text();
autobridgedOffer.taker_gets_funded = autobridgedOffer.TakerGets.value;
autobridgedOffer.taker_pays_funded = autobridgedOffer.TakerPays.value;
autobridgedOffer.autobridged = true;
autobridgedOffer.BookDirectory = Utils.convertOfferQualityToHex(quality);
return autobridgedOffer;
};
/**
* Remove funds clamp on leg one offer. This is necessary when the two offers
* are owned by the same account. In this case, it doesn't matter if offer one
* is not fully funded. Leg one out goes to leg two in and since its the same
* account, an infinite amount can flow.
*
* @param {Object} legOneOffer - IOU:XRP offer
*/
AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
this.setLegOneTakerGetsFunded(
legOneOffer,
Utils.getOfferTakerGets(legOneOffer)
);
};
/**
* Apply clamp back on leg one offer after a round of autobridge calculation
* completes. We must reapply clamps that have been removed because we cannot
* guarantee that the next offer from leg two will also be from the same
* account.
*
* When we reapply, it could happen that the amount of TakerGets left after
* the autobridge calculation is less than the original funded amount. In this
* case, we have extra funds we can use towards unfunded offers with worse
* quality by the same owner.
*
* @param {Object} legOneOffer - IOU:XRP offer
*/
AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
var takerGets = Utils.getOfferTakerGets(legOneOffer);
if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) {
// After clamping, TakerGets is still greater than initial funded amount
this.setLegOneTakerGetsFunded(legOneOffer, legOneOffer.initTakerGetsFunded);
} else {
var updatedLeftover = legOneOffer.initTakerGetsFunded.subtract(takerGets);
this.setLegOneTakerGetsFunded(legOneOffer, takerGets);
this.addLeftoverOwnerFunds(legOneOffer.Account, updatedLeftover);
}
};
/**
* Increase leg one offer funded amount with extra funds found after applying
* clamp.
*
* @param {Object} legOneOffer - IOU:XRP offer
*/
AutobridgeCalculator.prototype.adjustLegOneFundedAmount =
function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded');
var fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer)
.add(this.getLeftoverOwnerFunds(legOneOffer.Account));
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer)) >= 0) {
// There are enough extra funds to fully fund the offer
var legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
var updatedLeftover = fundedSum.subtract(legOneTakerGets);
this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets);
this.setLeftoverOwnerFunds(legOneOffer.Account, updatedLeftover);
} else {
// There are not enough extra funds to fully fund the offer
this.setLegOneTakerGetsFunded(legOneOffer, fundedSum);
this.resetOwnerFundsLeftover(legOneOffer.Account);
}
};
/**
* Set taker gets funded amount for a IOU:XRP offer. Also calculates taker
* pays funded using offer quality and updates is_fully_funded flag
*
* @param {Object} legOneOffer - IOU:XRP offer
* @param {Amount} takerGetsFunded
*/
AutobridgeCalculator.prototype.setLegOneTakerGetsFunded =
function setLegOneTakerGetsFunded(legOneOffer, takerGetsFunded) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(takerGetsFunded instanceof Amount, 'Taker gets funded is invalid');
legOneOffer.taker_gets_funded = takerGetsFunded.to_text();
legOneOffer.taker_pays_funded = takerGetsFunded
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets))
.to_text();
if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) {
legOneOffer.is_fully_funded = true;
}
};
/**
* Set taker gets amount for a IOU:XRP offer. Also calculates taker pays
* using offer quality
*
* @param {Object} legOneOffer - IOU:XRP offer
* @param {Amount} takerGets
*/
AutobridgeCalculator.prototype.setLegOneTakerGets =
function(legOneOffer, takerGets) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(takerGets instanceof Amount, 'Taker gets funded is invalid');
var legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
legOneOffer.TakerGets = takerGets.to_text();
legOneOffer.TakerPays = takerGets.multiply(legOneQuality);
};
module.exports = AutobridgeCalculator;

View File

@@ -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)));
};

View 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;

View File

@@ -1,33 +0,0 @@
var BigNumber = require('bignumber.js');
var extend = require('extend');
function BigNumberWrapper(value, base) {
// reset config every time a BigNumber is instantiated so that
// these global settings won't be overridden if another file tries
// to set them at require-time.
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_HALF_UP,
DECIMAL_PLACES: 40 });
BigNumber.call(this, value, base);
}
extend(BigNumberWrapper, BigNumber); // copy class static properties
BigNumberWrapper.prototype = BigNumber.prototype;
BigNumberWrapper.config = function() {
throw new Error('BigNumber.config may only be called from bignumber.js');
};
BigNumberWrapper.withRoundingMode = function(roundingMode, func) {
var config = BigNumber.config();
var oldRoundingMode = config.ROUNDING_MODE;
config.ROUNDING_MODE = roundingMode;
BigNumber.config(config);
try {
return func();
} finally {
config.ROUNDING_MODE = oldRoundingMode;
BigNumber.config(config);
}
};
module.exports = BigNumberWrapper;

View File

@@ -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,12 +279,21 @@ exports.tx = {
EnableFeature: [100].concat(base, [
[ 'Feature' , REQUIRED ]
]),
EnableAmendment: [100].concat(base, [
[ 'Amendment' , REQUIRED ]
]),
SetFee: [101].concat(base, [
[ 'Features' , REQUIRED ],
[ 'BaseFee' , REQUIRED ],
[ 'ReferenceFeeUnits' , REQUIRED ],
[ 'ReserveBase' , REQUIRED ],
[ 'ReserveIncrement' , REQUIRED ]
]),
TicketCreate: [10].concat(base, [
[ 'Target' , OPTIONAL ],
[ 'Expiration' , OPTIONAL ]
]),
TicketCancel: [11].concat(base, [
[ 'TicketID' , REQUIRED ]
])
};
@@ -414,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
};

View File

@@ -1,10 +0,0 @@
// This object serves as a singleton to store config options
var extend = require('extend');
var config = module.exports = {
load: function (newOpts) {
extend(config, newOpts);
return config;
}
};

View File

@@ -1,333 +0,0 @@
var sjcl = require('./utils').sjcl;
var base = require('./base').Base;
var Seed = require('./seed').Seed;
var UInt160 = require('./uint160').UInt160;
var UInt256 = require('./uint256').UInt256;
var request = require('superagent');
var querystring = require('querystring');
var extend = require("extend");
var parser = require("url");
var Crypt = { };
var cryptConfig = {
cipher : 'aes',
mode : 'ccm',
ts : 64, // tag length
ks : 256, // key size
iter : 1000 // iterations (key derivation)
};
/**
* Full domain hash based on SHA512
*/
function fdh(data, bytelen) {
var bitlen = bytelen << 3;
if (typeof data === 'string') {
data = sjcl.codec.utf8String.toBits(data);
}
// Add hashing rounds until we exceed desired length in bits
var counter = 0, output = [];
while (sjcl.bitArray.bitLength(output) < bitlen) {
var hash = sjcl.hash.sha512.hash(sjcl.bitArray.concat([counter], data));
output = sjcl.bitArray.concat(output, hash);
counter++;
}
// Truncate to desired length
output = sjcl.bitArray.clamp(output, bitlen);
return output;
};
/**
* This is a function to derive different hashes from the same key.
* Each hash is derived as HMAC-SHA512HALF(key, token).
*
* @param {string} key
* @param {string} hash
*/
function keyHash(key, token) {
var hmac = new sjcl.misc.hmac(key, sjcl.hash.sha512);
return sjcl.codec.hex.fromBits(sjcl.bitArray.bitSlice(hmac.encrypt(token), 0, 256));
};
/**
* add entropy at each call to get random words
* @param {number} nWords
*/
function randomWords (nWords) {
for (var i = 0; i < 8; i++) {
sjcl.random.addEntropy(Math.random(), 32, "Math.random()");
}
return sjcl.random.randomWords(nWords);
}
/****** exposed functions ******/
/**
* KEY DERIVATION FUNCTION
*
* This service takes care of the key derivation, i.e. converting low-entropy
* secret into higher entropy secret via either computationally expensive
* processes or peer-assisted key derivation (PAKDF).
*
* @param {object} opts
* @param {string} purpose - Key type/purpose
* @param {string} username
* @param {string} secret - Also known as passphrase/password
* @param {function} fn
*/
Crypt.derive = function(opts, purpose, username, secret, fn) {
var tokens;
if (purpose === 'login') {
tokens = ['id', 'crypt'];
} else {
tokens = ['unlock'];
}
var iExponent = new sjcl.bn(String(opts.exponent));
var iModulus = new sjcl.bn(String(opts.modulus));
var iAlpha = new sjcl.bn(String(opts.alpha));
var publicInfo = [ 'PAKDF_1_0_0', opts.host.length, opts.host, username.length, username, purpose.length, purpose ].join(':') + ':';
var publicSize = Math.ceil(Math.min((7 + iModulus.bitLength()) >>> 3, 256) / 8);
var publicHash = fdh(publicInfo, publicSize);
var publicHex = sjcl.codec.hex.fromBits(publicHash);
var iPublic = new sjcl.bn(String(publicHex)).setBitM(0);
var secretInfo = [ publicInfo, secret.length, secret ].join(':') + ':';
var secretSize = (7 + iModulus.bitLength()) >>> 3;
var secretHash = fdh(secretInfo, secretSize);
var secretHex = sjcl.codec.hex.fromBits(secretHash);
var iSecret = new sjcl.bn(String(secretHex)).mod(iModulus);
if (iSecret.jacobi(iModulus) !== 1) {
iSecret = iSecret.mul(iAlpha).mod(iModulus);
}
var iRandom;
for (;;) {
iRandom = sjcl.bn.random(iModulus, 0);
if (iRandom.jacobi(iModulus) === 1) {
break;
}
}
var iBlind = iRandom.powermodMontgomery(iPublic.mul(iExponent), iModulus);
var iSignreq = iSecret.mulmod(iBlind, iModulus);
var signreq = sjcl.codec.hex.fromBits(iSignreq.toBits());
request.post(opts.url)
.send({ info: publicInfo, signreq: signreq })
.end(function(err, resp) {
if (err || !resp) {
return fn(new Error('Could not query PAKDF server ' + opts.host));
}
var data = resp.body || resp.text ? JSON.parse(resp.text) : {};
if (data.result !== 'success') {
return fn(new Error('Could not query PAKDF server '+opts.host));
}
var iSignres = new sjcl.bn(String(data.signres));
var iRandomInv = iRandom.inverseMod(iModulus);
var iSigned = iSignres.mulmod(iRandomInv, iModulus);
var key = iSigned.toBits();
var result = { };
tokens.forEach(function(token) {
result[token] = keyHash(key, token);
});
fn(null, result);
});
};
/**
* Imported from ripple-client
*/
/**
* Encrypt data
*
* @param {string} key
* @param {string} data
*/
Crypt.encrypt = function(key, data) {
key = sjcl.codec.hex.toBits(key);
var opts = extend(true, {}, cryptConfig);
var encryptedObj = JSON.parse(sjcl.encrypt(key, data, opts));
var version = [sjcl.bitArray.partial(8, 0)];
var initVector = sjcl.codec.base64.toBits(encryptedObj.iv);
var ciphertext = sjcl.codec.base64.toBits(encryptedObj.ct);
var encryptedBits = sjcl.bitArray.concat(version, initVector);
encryptedBits = sjcl.bitArray.concat(encryptedBits, ciphertext);
return sjcl.codec.base64.fromBits(encryptedBits);
};
/**
* Decrypt data
*
* @param {string} key
* @param {string} data
*/
Crypt.decrypt = function (key, data) {
key = sjcl.codec.hex.toBits(key);
var encryptedBits = sjcl.codec.base64.toBits(data);
var version = sjcl.bitArray.extract(encryptedBits, 0, 8);
if (version !== 0) {
throw new Error('Unsupported encryption version: '+version);
}
var encrypted = extend(true, {}, cryptConfig, {
iv: sjcl.codec.base64.fromBits(sjcl.bitArray.bitSlice(encryptedBits, 8, 8+128)),
ct: sjcl.codec.base64.fromBits(sjcl.bitArray.bitSlice(encryptedBits, 8+128))
});
return sjcl.decrypt(key, JSON.stringify(encrypted));
};
/**
* Validate a ripple address
*
* @param {string} address
*/
Crypt.isValidAddress = function (address) {
return UInt160.is_valid(address);
};
/**
* Create an encryption key
*
* @param {integer} nWords - number of words
*/
Crypt.createSecret = function (nWords) {
return sjcl.codec.hex.fromBits(randomWords(nWords));
};
/**
* Create a new master key
*/
Crypt.createMaster = function () {
return base.encode_check(33, sjcl.codec.bytes.fromBits(randomWords(4)));
};
/**
* Create a ripple address from a master key
*
* @param {string} masterkey
*/
Crypt.getAddress = function (masterkey) {
return Seed.from_json(masterkey).get_key().get_address().to_json();
};
/**
* Hash data using SHA-512.
*
* @param {string|bitArray} data
* @return {string} Hash of the data
*/
Crypt.hashSha512 = function (data) {
// XXX Should return a UInt512
return sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(data));
};
/**
* Hash data using SHA-512 and return the first 256 bits.
*
* @param {string|bitArray} data
* @return {UInt256} Hash of the data
*/
Crypt.hashSha512Half = function (data) {
return UInt256.from_hex(Crypt.hashSha512(data).substr(0, 64));
};
/**
* Sign a data string with a secret key
*
* @param {string} secret
* @param {string} data
*/
Crypt.signString = function(secret, data) {
var hmac = new sjcl.misc.hmac(sjcl.codec.hex.toBits(secret), sjcl.hash.sha512);
return sjcl.codec.hex.fromBits(hmac.mac(data));
};
/**
* Create an an accout recovery key
*
* @param {string} secret
*/
Crypt.deriveRecoveryEncryptionKeyFromSecret = function(secret) {
var seed = Seed.from_json(secret).to_bits();
var hmac = new sjcl.misc.hmac(seed, sjcl.hash.sha512);
var key = hmac.mac('ripple/hmac/recovery_encryption_key/v1');
key = sjcl.bitArray.bitSlice(key, 0, 256);
return sjcl.codec.hex.fromBits(key);
};
/**
* Convert base64 encoded data into base64url encoded data.
*
* @param {String} base64 Data
*/
Crypt.base64ToBase64Url = function(encodedData) {
return encodedData.replace(/\+/g, '-').replace(/\//g, '_').replace(/[=]+$/, '');
};
/**
* Convert base64url encoded data into base64 encoded data.
*
* @param {String} base64 Data
*/
Crypt.base64UrlToBase64 = function(encodedData) {
encodedData = encodedData.replace(/-/g, '+').replace(/_/g, '/');
while (encodedData.length % 4) {
encodedData += '=';
}
return encodedData;
};
/**
* base64 to UTF8
*/
Crypt.decodeBase64 = function (data) {
return sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(data));
}
exports.Crypt = Crypt;

View File

@@ -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

View File

@@ -1,59 +0,0 @@
/**
* IEEE 754 floating-point.
*
* Supports single- or double-precision
*/
var Float = exports.Float = {};
var allZeros = /^0+$/;
var allOnes = /^1+$/;
Float.fromBytes = function(bytes) {
// Render in binary. Hackish.
var b = '';
for (var i = 0, n = bytes.length; i < n; i++) {
var bits = (bytes[i] & 0xff).toString(2);
while (bits.length < 8) {
bits = '0' + bits;
}
b += bits;
}
// Determine configuration. This could have all been precomputed but it is fast enough.
var exponentBits = bytes.length === 4 ? 4 : 11;
var mantissaBits = (bytes.length * 8) - exponentBits - 1;
var bias = Math.pow(2, exponentBits - 1) - 1;
var minExponent = 1 - bias - mantissaBits;
// Break up the binary representation into its pieces for easier processing.
var s = b[0];
var e = b.substring(1, exponentBits + 1);
var m = b.substring(exponentBits + 1);
var value = 0;
var multiplier = (s === '0' ? 1 : -1);
if (allZeros.test(e)) {
// Zero or denormalized
if (!allZeros.test(m)) {
value = parseInt(m, 2) * Math.pow(2, minExponent);
}
} else if (allOnes.test(e)) {
// Infinity or NaN
if (allZeros.test(m)) {
value = Infinity;
} else {
value = NaN;
}
} else {
// Normalized
var exponent = parseInt(e, 2) - bias;
var mantissa = parseInt(m, 2);
value = (1 + (mantissa * Math.pow(2, -mantissaBits))) * Math.pow(2, exponent);
}
return value * multiplier;
};

View File

@@ -1,25 +1,26 @@
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.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,14 +29,13 @@ 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.config = require('./config');
exports.sjcl = require('./utils').sjcl;
exports.Wallet = require('ripple-wallet-generator')({sjcl: exports.sjcl});
exports.types = require('./serializedtypes');
// 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;
@@ -50,13 +50,13 @@ function attachUnderscored(c) {
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

View File

@@ -1,14 +1,18 @@
var sjcl = require('./utils').sjcl;
'use strict';
/*eslint new-cap: 1*/
var sjcl = require('./utils').sjcl;
var UInt160 = require('./uint160').UInt160;
var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
var Base = require('./base').Base;
function KeyPair() {
this._curve = sjcl.ecc.curves.k256;
this._curve = sjcl.ecc.curves.k256;
this._secret = null;
this._pubkey = null;
};
}
KeyPair.from_bn_secret = function(j) {
return (j instanceof this) ? j.clone() : (new this()).parse_bn_secret(j);
@@ -20,15 +24,16 @@ KeyPair.prototype.parse_bn_secret = function(j) {
};
/**
* Returns public key as sjcl public key.
*
* @private
*
* @return {sjcl.ecc.ecdsa.publicKey} public key
*/
KeyPair.prototype._pub = function() {
var curve = this._curve;
if (!this._pubkey && this._secret) {
var exponent = this._secret._exponent;
this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent));
}
@@ -36,9 +41,9 @@ KeyPair.prototype._pub = function() {
};
/**
* Returns public key in compressed format as bit array.
*
* @private
*
* @return {sjcl.bitArray} public key bits in compressed form
*/
KeyPair.prototype._pub_bits = function() {
var pub = this._pub();
@@ -56,9 +61,7 @@ KeyPair.prototype._pub_bits = function() {
};
/**
* Returns public key as hex.
*
* Key will be returned as a compressed pubkey - 33 bytes converted to hex.
* @return {String} public key bytes in compressed form, hex encoded.
*/
KeyPair.prototype.to_hex_pub = function() {
var bits = this._pub_bits();
@@ -70,7 +73,7 @@ KeyPair.prototype.to_hex_pub = function() {
return sjcl.codec.hex.fromBits(bits).toUpperCase();
};
function SHA256_RIPEMD160(bits) {
function sha256_ripemd160(bits) {
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
}
@@ -81,7 +84,7 @@ KeyPair.prototype.get_address = function() {
return null;
}
var hash = SHA256_RIPEMD160(bits);
var hash = sha256_ripemd160(bits);
var address = UInt160.from_bits(hash);
address.set_version(Base.VER_ACCOUNT_ID);
@@ -89,8 +92,9 @@ KeyPair.prototype.get_address = function() {
};
KeyPair.prototype.sign = function(hash) {
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
hash = UInt256.from_json(hash);
var sig = this._secret.sign(hash.to_bits(), 0);
var sig = this._secret.sign(hash.to_bits(), PARANOIA_256_BITS);
sig = this._secret.canonicalizeSignature(sig);
return this._secret.encodeDER(sig);
};

View File

@@ -1,5 +1,5 @@
// Ledger
/* eslint-disable valid-jsdoc */
'use strict';
var Transaction = require('./transaction').Transaction;
var SHAMap = require('./shamap').SHAMap;
var SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
@@ -7,16 +7,12 @@ var SerializedObject = require('./serializedobject').SerializedObject;
var stypes = require('./serializedtypes');
var UInt160 = require('./uint160').UInt160;
var Currency = require('./currency').Currency;
var stypes = require('./serializedtypes');
var sjcl = require('./utils').sjcl;
var Crypt = require('./crypt').Crypt;
function Ledger()
{
function Ledger() {
this.ledger_json = {};
}
Ledger.from_json = function (v) {
Ledger.from_json = function(v) {
var ledger = new Ledger();
ledger.parse_json(v);
return ledger;
@@ -31,7 +27,7 @@ Ledger.space = require('./ledgerspaces');
* @return {UInt256}
*/
Ledger.calcAccountRootEntryHash =
Ledger.prototype.calcAccountRootEntryHash = function (account) {
Ledger.prototype.calcAccountRootEntryHash = function(account) {
account = UInt160.from_json(account);
var index = new SerializedObject();
@@ -51,9 +47,9 @@ Ledger.prototype.calcAccountRootEntryHash = function (account) {
* @return {UInt256}
*/
Ledger.calcOfferEntryHash =
Ledger.prototype.calcOfferEntryHash = function (account, sequence) {
Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
account = UInt160.from_json(account);
sequence = parseInt(sequence);
sequence = parseInt(sequence, 10);
var index = new SerializedObject();
@@ -75,19 +71,20 @@ Ledger.prototype.calcOfferEntryHash = function (account, sequence) {
* @return {UInt256}
*/
Ledger.calcRippleStateEntryHash =
Ledger.prototype.calcRippleStateEntryHash = function (account1, account2, currency) {
Ledger.prototype.calcRippleStateEntryHash = function(
account1, account2, currency) {
currency = Currency.from_json(currency);
account1 = UInt160.from_json(account1);
account2 = UInt160.from_json(account2);
if (!account1.is_valid()) {
throw new Error("Invalid first account");
throw new Error('Invalid first account');
}
if (!account2.is_valid()) {
throw new Error("Invalid second account");
throw new Error('Invalid second account');
}
if (!currency.is_valid()) {
throw new Error("Invalid currency");
throw new Error('Invalid currency');
}
// The lower ID has to come first
@@ -107,14 +104,14 @@ Ledger.prototype.calcRippleStateEntryHash = function (account1, account2, curren
return index.hash();
};
Ledger.prototype.parse_json = function (v) {
Ledger.prototype.parse_json = function(v) {
this.ledger_json = v;
};
Ledger.prototype.calc_tx_hash = function () {
Ledger.prototype.calc_tx_hash = function() {
var tx_map = new SHAMap();
this.ledger_json.transactions.forEach(function (tx_json) {
this.ledger_json.transactions.forEach(function(tx_json) {
var tx = Transaction.from_json(tx_json);
var meta = SerializedObject.from_json(tx_json.metaData);
@@ -128,37 +125,39 @@ Ledger.prototype.calc_tx_hash = function () {
};
/**
* @param options.sanity_test {Boolean}
* @param options .sanity_test {Boolean}
* @return hash of shamap
*
* If `true`, will serialize each accountState item to binary and then back to
* json before finally serializing for hashing. This is mostly to expose any
* issues with ripple-lib's binary <--> json codecs.
*
*/
Ledger.prototype.calc_account_hash = function (options) {
Ledger.prototype.calc_account_hash = function(options) {
var account_map = new SHAMap();
var erred;
this.ledger_json.accountState.forEach(function (le) {
this.ledger_json.accountState.forEach(function(le) {
var data = SerializedObject.from_json(le);
if (options != null && options.sanity_test) {
var json;
if (options && options.sanity_test) {
try {
var json = data.to_json();
json = data.to_json();
data = SerializedObject.from_json(json);
} catch (e) {
console.log("account state item: ", le);
console.log("to_json() ",json);
console.log("exception: ", e);
console.log('account state item: ', le);
console.log('to_json() ', json);
console.log('exception: ', e);
erred = true;
}
};
}
account_map.add_item(le.index, data, SHAMapTreeNode.TYPE_ACCOUNT_STATE);
});
if (erred) {
throw new Error("There were errors with sanity_test"); // all logged above
throw new Error('There were errors with sanity_test'); // all logged above
}
return account_map.hash();

View File

@@ -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;
}
/**

View File

@@ -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;
}

View File

@@ -1,20 +1,22 @@
var async = require('async');
var crypto = require('crypto');
var sjcl = require('./utils').sjcl;
var Remote = require('./remote').Remote;
var Seed = require('./seed').Seed;
var KeyPair = require('./keypair').KeyPair;
var Account = require('./account').Account;
var UInt160 = require('./uint160').UInt160;
/* eslint-disable valid-jsdoc */
'use strict';
var async = require('async');
var sjcl = require('./utils').sjcl;
var Remote = require('./remote').Remote;
var Seed = require('./seed').Seed;
var KeyPair = require('./keypair').KeyPair;
var Account = require('./account').Account;
var UInt160 = require('./uint160').UInt160;
// Message class (static)
var Message = {};
Message.HASH_FUNCTION = sjcl.hash.sha512.hash;
Message.MAGIC_BYTES = 'Ripple Signed Message:\n';
Message.hashFunction = sjcl.hash.sha512.hash;
Message.MAGIC_BYTES = 'Ripple Signed Message:\n';
var REGEX_HEX = /^[0-9a-fA-F]+$/;
var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
var REGEX_BASE64 =
/^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
/**
* Produce a Base64-encoded signature on the given message with
@@ -27,15 +29,16 @@ var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)
* @static
*
* @param {String} message
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json} secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing account.
* If this is omitted the first account produced by the secret generator will be used.
* @returns {Base64-encoded String} signature
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json}
* secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing
* account. If this is omitted the first account produced by the secret
* generator will be used.
* @return {Base64-encoded String} signature
*/
Message.signMessage = function(message, secret_key, account) {
return Message.signHash(Message.HASH_FUNCTION(Message.MAGIC_BYTES + message), secret_key, account);
return Message.signHash(Message.hashFunction(Message.MAGIC_BYTES + message),
secret_key, account);
};
/**
@@ -48,9 +51,11 @@ Message.signMessage = function(message, secret_key, account) {
* @static
*
* @param {bitArray|Hex-encoded String} hash
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json} secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing account.
* If this is omitted the first account produced by the secret generator will be used.
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json}
* secret_key
* @param {RippleAddress} [The first key] account Field to specify the
* signing account. If this is omitted the first account produced by
* the secret generator will be used.
* @returns {Base64-encoded String} signature
*/
Message.signHash = function(hash, secret_key, account) {
@@ -59,7 +64,8 @@ Message.signHash = function(hash, secret_key, account) {
hash = sjcl.codec.hex.toBits(hash);
}
if (typeof hash !== 'object' || hash.length <= 0 || typeof hash[0] !== 'number') {
if (typeof hash !== 'object' || hash.length <= 0
|| typeof hash[0] !== 'number') {
throw new Error('Hash must be a bitArray or hex-encoded string');
}
@@ -67,7 +73,9 @@ Message.signHash = function(hash, secret_key, account) {
secret_key = Seed.from_json(secret_key).get_key(account)._secret;
}
var signature_bits = secret_key.signWithRecoverablePublicKey(hash);
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
var signature_bits = secret_key.signWithRecoverablePublicKey(hash,
PARANOIA_256_BITS);
var signature_base64 = sjcl.codec.base64.fromBits(signature_bits);
return signature_base64;
@@ -78,7 +86,7 @@ Message.signHash = function(hash, secret_key, account) {
/**
* Verify the signature on a given message.
*
* Note that this function is asynchronous.
* Note that this function is asynchronous.
* The ripple-lib remote is used to check that the public
* key extracted from the signature corresponds to one that is currently
* active for the given account.
@@ -98,9 +106,10 @@ Message.signHash = function(hash, secret_key, account) {
Message.verifyMessageSignature = function(data, remote, callback) {
if (typeof data.message === 'string') {
data.hash = Message.HASH_FUNCTION(Message.MAGIC_BYTES + data.message);
data.hash = Message.hashFunction(Message.MAGIC_BYTES + data.message);
} else {
return callback(new Error('Data object must contain message field to verify signature'));
return callback(new Error(
'Data object must contain message field to verify signature'));
}
return Message.verifyHashSignature(data, remote, callback);
@@ -111,7 +120,7 @@ Message.verifyMessageSignature = function(data, remote, callback) {
/**
* Verify the signature on a given hash.
*
* Note that this function is asynchronous.
* Note that this function is asynchronous.
* The ripple-lib remote is used to check that the public
* key extracted from the signature corresponds to one that is currently
* active for the given account.
@@ -134,7 +143,7 @@ Message.verifyHashSignature = function(data, remote, callback) {
account,
signature;
if(typeof callback !== 'function') {
if (typeof callback !== 'function') {
throw new Error('Must supply callback function');
}
@@ -143,7 +152,8 @@ Message.verifyHashSignature = function(data, remote, callback) {
hash = sjcl.codec.hex.toBits(hash);
}
if (typeof hash !== 'object' || hash.length <= 0 || typeof hash[0] !== 'number') {
if (typeof hash !== 'object' || hash.length <= 0
|| typeof hash[0] !== 'number') {
return callback(new Error('Hash must be a bitArray or hex-encoded string'));
}
@@ -159,14 +169,16 @@ Message.verifyHashSignature = function(data, remote, callback) {
signature = sjcl.codec.base64.toBits(signature);
if (!(remote instanceof Remote) || remote.state !== 'online') {
return callback(new Error('Must supply connected Remote to verify signature'));
return callback(new Error(
'Must supply connected Remote to verify signature'));
}
function recoverPublicKey (async_callback) {
function recoverPublicKey(async_callback) {
var public_key;
try {
public_key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
public_key =
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
} catch (err) {
return async_callback(err);
}
@@ -177,9 +189,9 @@ Message.verifyHashSignature = function(data, remote, callback) {
async_callback(new Error('Could not recover public key from signature'));
}
};
}
function checkPublicKeyIsValid (public_key, async_callback) {
function checkPublicKeyIsValid(public_key, async_callback) {
// Get hex-encoded public key
var key_pair = new KeyPair();
@@ -189,7 +201,7 @@ Message.verifyHashSignature = function(data, remote, callback) {
var account_class_instance = new Account(remote, account);
account_class_instance.publicKeyIsActive(public_key_hex, async_callback);
};
}
var steps = [
recoverPublicKey,

View File

@@ -131,7 +131,6 @@ Meta.prototype.getNodes = function(options) {
}
};
Meta.prototype.getAffectedAccounts = function(from) {
if (this._affectedAccounts) {
return this._affectedAccounts;
@@ -164,7 +163,7 @@ Meta.prototype.getAffectedAccounts = function(from) {
this._affectedAccounts = utils.arrayUnique(accounts);
return this._affectedAccounts;
return this._affectedAccounts;
};
Meta.prototype.getAffectedBooks = function() {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
'use strict';
var _ = require('lodash');
var assert = require('assert');
var SerializedObject = require('./serializedobject').SerializedObject;
var Types = require('./serializedtypes');
var Amount = require('./amount').Amount;
var IOU_SUFFIX = '/000/rrrrrrrrrrrrrrrrrrrrrhoLvTp';
var OrderBookUtils = {};
function assertValidNumber(number, message) {
assert(!_.isNull(number) && !isNaN(number), message);
}
/**
* Casts and returns offer's taker gets funded amount as a default IOU amount
*
* @param {Object} offer
* @return {Amount}
*/
OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
return Amount.from_json(offer.taker_gets_funded + IOU_SUFFIX);
};
/**
* Casts and returns offer's taker pays funded amount as a default IOU amount
*
* @param {Object} offer
* @return {Amount}
*/
OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid');
return Amount.from_json(offer.taker_pays_funded + IOU_SUFFIX);
};
/**
* Get offer taker gets amount
*
* @param {Object} offer
*
* @return {Amount}
*/
OrderBookUtils.getOfferTakerGets = function(offer) {
assert(typeof offer, 'object', 'Offer is invalid');
return Amount.from_json(offer.TakerGets + IOU_SUFFIX);
};
/**
* Retrieve offer quality
*
* @param {Object} offer
* @param {Currency} currencyGets
*/
OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
var amount;
if (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 + IOU_SUFFIX);
}
return amount;
};
/**
* Formats an offer quality amount to a hex that can be parsed by
* Amount.parse_quality
*
* @param {Amount} quality
*
* @return {String}
*/
OrderBookUtils.convertOfferQualityToHex = function(quality) {
assert(quality instanceof Amount, 'Quality is not an amount');
var so = new SerializedObject();
Types.Quality.serialize(so, quality.to_text() + IOU_SUFFIX);
return so.to_hex();
};
/**
*
*/
OrderBookUtils.normalizeAmount = function(value) {
return Amount.from_json(value + IOU_SUFFIX);
};
module.exports = OrderBookUtils;

View File

@@ -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;
@@ -32,9 +33,7 @@ var PathFind = require('./pathfind').PathFind;
var SerializedObject = require('./serializedobject').SerializedObject;
var RippleError = require('./rippleerror').RippleError;
var utils = require('./utils');
var sjcl = require('./utils').sjcl;
var hashprefixes = require('./hashprefixes');
var config = require('./config');
var log = require('./log').internal.sub('remote');
/**
@@ -47,31 +46,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 +80,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 +173,7 @@ function Remote(opts) {
break;
}
}
};
}
this.on('newListener', function(event) {
listenersModified('add', event);
@@ -183,7 +182,7 @@ function Remote(opts) {
this.on('removeListener', function(event) {
listenersModified('remove', event);
});
};
}
util.inherits(Remote, EventEmitter);
@@ -217,7 +216,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: {
@@ -235,29 +237,6 @@ Remote.flags = {
}
};
Remote.from_config = function(obj, trace) {
var serverConfig = (typeof obj === 'string') ? config.servers[obj] : obj;
var remote = new Remote(serverConfig, trace);
function initializeAccount(account) {
var accountInfo = config.accounts[account];
if (typeof accountInfo === 'object') {
if (accountInfo.secret) {
// Index by nickname
remote.setSecret(account, accountInfo.secret);
// Index by account ID
remote.setSecret(accountInfo.account, accountInfo.secret);
}
}
};
if (config.accounts) {
Object.keys(config.accounts).forEach(initializeAccount);
}
return remote;
};
/**
* Check that server message is valid
*
@@ -361,7 +340,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 +369,7 @@ Remote.prototype.addServer = function(opts) {
function serverMessage(data) {
self._handleMessage(data, server);
};
}
server.on('message', serverMessage);
@@ -406,7 +385,7 @@ Remote.prototype.addServer = function(opts) {
if (self._connection_count === self._servers.length) {
self.emit('ready');
}
};
}
server.on('connect', serverConnect);
@@ -415,7 +394,7 @@ Remote.prototype.addServer = function(opts) {
if (self._connection_count === 0) {
self._setState('offline');
}
};
}
server.on('disconnect', serverDisconnect);
@@ -438,8 +417,6 @@ Remote.prototype.reconnect = function() {
this._servers.forEach(function(server) {
server.reconnect();
});
return this;
};
/**
@@ -479,7 +456,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;
@@ -513,8 +492,13 @@ Remote.prototype.disconnect = function(callback) {
Remote.prototype._handleMessage = function(message, server) {
try {
message = JSON.parse(message);
/*eslint-disable no-empty*/
} catch (e) {
// TODO: why are we suppressing this?
console.error(e);
}
/*eslint-enable no-empty*/
if (!Remote.isValidMessage(message)) {
// Unexpected response from remote.
@@ -544,6 +528,15 @@ Remote.prototype._handleMessage = function(message, server) {
}
};
Remote.prototype.getLedgerSequence = function() {
if (!this._ledger_current_index) {
throw new Error('Ledger sequence has not yet been initialized');
}
return this._ledger_current_index;
};
/**
* Handle server ledger_closed event
*
@@ -633,7 +626,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);
}
@@ -862,7 +856,7 @@ Remote.prototype.requestLedger = function(options, callback) {
case 'expand':
case 'transactions':
case 'accounts':
request.message[o] = true;
request.message[o] = options[o] ? true : false;
break;
case 'ledger':
request.selectLedger(options.ledger);
@@ -936,9 +930,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 +962,7 @@ Remote.prototype.requestLedgerData = function(options, callback) {
async.setImmediate(function() {
next(null, Remote.parseBinaryLedgerData(ledgerData));
});
};
}
function complete(err, state) {
if (err) {
@@ -974,7 +971,7 @@ Remote.prototype.requestLedgerData = function(options, callback) {
res.state = state;
request.emit('state', res);
}
};
}
async.mapSeries(res.state, iterator, complete);
});
@@ -1028,7 +1025,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 +1070,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 +1090,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 +1107,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 +1117,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 +1149,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 +1160,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 +1218,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 +1386,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 +1427,7 @@ Remote.prototype.requestAccountTx = function(options, callback) {
async.setImmediate(function() {
next(null, Remote.parseBinaryAccountTransaction(transaction));
});
};
}
function complete(err, transactions) {
if (err) {
@@ -1437,7 +1436,7 @@ Remote.prototype.requestAccountTx = function(options, callback) {
res.transactions = transactions;
request.emit('transactions', res);
}
};
}
async.mapSeries(res.transactions, iterator, complete);
});
@@ -1494,15 +1493,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 +1550,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 +1590,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 +1656,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 +1684,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 +1720,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;
@@ -1733,21 +1735,9 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback) {
function serverSubscribed(message) {
self._stand_alone = !!message.stand_alone;
self._testnet = !!message.testnet;
if (typeof message.random === 'string') {
var rand = message.random.match(/[0-9A-F]{8}/ig);
while (rand && rand.length) {
sjcl.random.addEntropy(parseInt(rand.pop(), 16));
}
self.emit('random', utils.hexToArray(message.random));
}
self._handleLedgerClosed(message, server);
self.emit('subscribed');
};
}
request.on('error', function(err) {
if (self.trace) {
@@ -1775,13 +1765,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 +1782,7 @@ Remote.prototype.requestLedgerAccept = function(callback) {
request.request();
return request;
/* eslint-enable consistent-return */
};
/**
@@ -1799,13 +1792,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 +1835,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 +1858,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 +1881,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 +1943,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 +1952,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 +1985,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 +1994,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 +2028,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 +2052,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 +2077,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 +2095,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 +2125,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 +2136,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 +2178,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 +2188,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 +2236,7 @@ Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledg
? node.HighQualityOut
: node.LowQualityOut)
});
};
}
request.once('success', rippleState);
request.callback(callback, 'ripple_state');
@@ -2270,7 +2268,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 +2278,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 +2306,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 +2316,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 +2380,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);

View File

@@ -1,18 +1,19 @@
'use strict';
//
// Seed support
//
var extend = require('extend');
var utils = require('./utils');
var sjcl = utils.sjcl;
var utils = require('./utils');
var sjcl = utils.sjcl;
var Base = require('./base').Base;
var UInt = require('./uint').UInt;
var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
var UInt = require('./uint').UInt;
var UInt160 = require('./uint160').UInt160;
var KeyPair = require('./keypair').KeyPair;
var Seed = extend(function () {
var Seed = extend(function() {
this._curve = sjcl.ecc.curves.k256;
this._value = NaN;
}, UInt);
@@ -23,7 +24,7 @@ Seed.prototype.constructor = Seed;
// value = NaN on error.
// One day this will support rfc1751 too.
Seed.prototype.parse_json = function (j) {
Seed.prototype.parse_json = function(j) {
if (typeof j === 'string') {
if (!j.length) {
this._value = NaN;
@@ -43,7 +44,7 @@ Seed.prototype.parse_json = function (j) {
return this;
};
Seed.prototype.parse_passphrase = function (j) {
Seed.prototype.parse_passphrase = function(j) {
if (typeof j !== 'string') {
throw new Error('Passphrase must be a string');
}
@@ -56,7 +57,7 @@ Seed.prototype.parse_passphrase = function (j) {
return this;
};
Seed.prototype.to_json = function () {
Seed.prototype.to_json = function() {
if (!(this.is_valid())) {
return NaN;
}
@@ -68,20 +69,18 @@ Seed.prototype.to_json = function () {
function append_int(a, i) {
return [].concat(a, i >> 24, (i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff);
};
}
function firstHalfOfSHA512(bytes) {
return sjcl.bitArray.bitSlice(
sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)),
0, 256
);
};
}
function SHA256_RIPEMD160(bits) {
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
};
/**
// Removed a `*` so this JSDoc-ish syntax is ignored.
// This will soon all change anyway.
/*
* @param account
* {undefined} take first, default, KeyPair
*
@@ -92,10 +91,11 @@ function SHA256_RIPEMD160(bits) {
* that is desired.
*
* @param maxLoops (optional)
* {Number} specifies the amount of attempts taken to generate
* a matching KeyPair
* {Number} specifies the amount of attempts taken
* to generate a matching KeyPair
*
*/
Seed.prototype.get_key = function (account, maxLoops) {
Seed.prototype.get_key = function(account, maxLoops) {
var account_number = 0, address;
var max_loops = maxLoops || 1;
@@ -105,7 +105,7 @@ Seed.prototype.get_key = function (account, maxLoops) {
if (account) {
if (typeof account === 'number') {
account_number = account;
max_loops = account_number+1;
max_loops = account_number + 1;
} else {
address = UInt160.from_json(account);
}
@@ -116,7 +116,8 @@ Seed.prototype.get_key = function (account, maxLoops) {
var i = 0;
do {
private_gen = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(this.to_bytes(), i)));
private_gen = sjcl.bn.fromBits(
firstHalfOfSHA512(append_int(this.to_bytes(), i)));
i++;
} while (!curve.r.greaterEquals(private_gen));
@@ -130,7 +131,13 @@ Seed.prototype.get_key = function (account, maxLoops) {
i = 0;
do {
sec = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(append_int(public_gen.toBytesCompressed(), account_number), i)));
sec = sjcl.bn.fromBits(
firstHalfOfSHA512(
append_int(
append_int(public_gen.toBytesCompressed(), account_number)
,
i
)));
i++;
} while (!curve.r.greaterEquals(sec));
@@ -141,12 +148,13 @@ Seed.prototype.get_key = function (account, maxLoops) {
if (max_loops-- <= 0) {
// We are almost certainly looking for an account that would take same
// value of $too_long {forever, ...}
throw new Error('Too many loops looking for KeyPair yielding '+
address.to_json() +' from ' + this.to_json());
throw new Error('Too many loops looking for KeyPair yielding ' +
address.to_json() + ' from ' + this.to_json());
}
} while (address && !key_pair.get_address().equals(address));
return key_pair;
return key_pair;
};
exports.Seed = Seed;

View File

@@ -1,10 +1,12 @@
var _ = require('lodash');
var assert = require('assert');
var extend = require('extend');
'use strict';
var _ = require('lodash');
var assert = require('assert');
var extend = require('extend');
var binformat = require('./binformat');
var stypes = require('./serializedtypes');
var Crypt = require('./crypt').Crypt;
var utils = require('./utils');
var stypes = require('./serializedtypes');
var utils = require('./utils');
var UInt256 = require('./uint256').UInt256;
var sjcl = utils.sjcl;
@@ -29,12 +31,14 @@ Object.keys(binformat.ter).forEach(function(key) {
function normalize_sjcl_bn_hex(string) {
var hex = string.slice(2); // remove '0x' prefix
// now strip leading zeros
var i = _.findIndex(hex, function(c) { return c !== '0'; });
var i = _.findIndex(hex, function(c) {
return c !== '0';
});
return i >= 0 ? hex.slice(i) : '0';
}
function SerializedObject(buf) {
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf)) ) {
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf))) {
this.buffer = buf;
} else if (typeof buf === 'string') {
this.buffer = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(buf));
@@ -49,7 +53,7 @@ function SerializedObject(buf) {
SerializedObject.from_json = function(obj) {
// Create a copy of the object so we don't modify it
obj = extend(true, {}, obj);
var so = new SerializedObject();
var so = new SerializedObject();
var typedef;
if (typeof obj.TransactionType === 'number') {
@@ -107,7 +111,7 @@ SerializedObject.check_no_missing_fields = function(typedef, obj) {
var field = spec[0];
var requirement = spec[1];
if (binformat.REQUIRED === requirement && obj[field] === void(0)) {
if (binformat.REQUIRED === requirement && obj[field] === undefined) {
missing_fields.push(field);
}
}
@@ -115,9 +119,9 @@ SerializedObject.check_no_missing_fields = function(typedef, obj) {
if (missing_fields.length > 0) {
var object_name;
if (obj.TransactionType !== void(0)) {
if (obj.TransactionType !== undefined) {
object_name = SerializedObject.lookup_type_tx(obj.TransactionType);
} else if (obj.LedgerEntryType != null){
} else if (obj.LedgerEntryType !== undefined) {
object_name = SerializedObject.lookup_type_le(obj.LedgerEntryType);
} else {
object_name = 'TransactionMetaData';
@@ -133,7 +137,17 @@ SerializedObject.prototype.append = function(bytes) {
bytes = bytes.buffer;
}
this.buffer = this.buffer.concat(bytes);
// Make sure both buffer and bytes are Array. Either could potentially be a
// Buffer.
if (Array.isArray(this.buffer) && Array.isArray(bytes)) {
// Array::concat is horribly slow where buffer length is 100 kbytes + One
// transaction with 1100 affected nodes took around 23 seconds to convert
// from json to bytes.
Array.prototype.push.apply(this.buffer, bytes);
} else {
this.buffer = this.buffer.concat(bytes);
}
this.pointer += bytes.length;
};
@@ -216,14 +230,17 @@ SerializedObject.jsonify_structure = function(structure, field_name) {
if (typeof structure.to_json === 'function') {
output = structure.to_json();
} else if (structure instanceof sjcl.bn) {
output = ('0000000000000000' + normalize_sjcl_bn_hex(structure.toString()).toUpperCase()).slice(-16);
output = ('0000000000000000' +
normalize_sjcl_bn_hex(structure.toString())
.toUpperCase()
).slice(-16);
} else {
//new Array or Object
// new Array or Object
output = new structure.constructor();
var keys = Object.keys(structure);
for (var i=0, l=keys.length; i<l; i++) {
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
output[key] = SerializedObject.jsonify_structure(structure[key], key);
}
@@ -256,33 +273,27 @@ SerializedObject.prototype.hash = function(prefix) {
var sign_buffer = new SerializedObject();
// Add hashing prefix
if ('undefined' !== typeof prefix) {
if (typeof prefix !== 'undefined') {
stypes.Int32.serialize(sign_buffer, prefix);
}
// Copy buffer to temporary buffer
sign_buffer.append(this.buffer);
// XXX We need a proper Buffer class then Crypt could accept that
var bits = sjcl.codec.bytes.toBits(sign_buffer.buffer);
return Crypt.hashSha512Half(bits);
var sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits));
return UInt256.from_hex(sha512hex.substr(0, 64));
};
// DEPRECATED
SerializedObject.prototype.signing_hash = SerializedObject.prototype.hash;
SerializedObject.prototype.serialize_field = function(spec, obj) {
var name = spec[0];
var name = spec[0];
var presence = spec[1];
var field_id = spec[2];
var Type = stypes[spec[3]];
if (typeof obj[name] !== 'undefined') {
// ST: Old serialization code
//this.append(SerializedObject.get_field_header(Type.id, field_id));
try {
// ST: Old serialization code
//Type.serialize(this, obj[name]);
stypes.serialize(this, name, obj[name]);
} catch (e) {
// Add field name to message and rethrow
@@ -295,7 +306,7 @@ SerializedObject.prototype.serialize_field = function(spec, obj) {
};
SerializedObject.get_field_header = function(type_id, field_id) {
var buffer = [ 0 ];
var buffer = [0];
if (type_id > 0xF) {
buffer.push(type_id & 0xFF);
@@ -328,7 +339,7 @@ SerializedObject.lookup_type_tx = function(id) {
return TRANSACTION_TYPES[id];
};
SerializedObject.lookup_type_le = function (id) {
SerializedObject.lookup_type_le = function(id) {
assert(typeof id === 'number');
return LEDGER_ENTRY_TYPES[id];
};

View File

@@ -1,3 +1,5 @@
'use strict';
/**
* Type definitions for binary format.
*
@@ -6,25 +8,29 @@
* SerializedObject.parse() or SerializedObject.serialize().
*/
var assert = require('assert');
var extend = require('extend');
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 BigNumber = require('./bignumber');
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 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 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);
@@ -52,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) {
@@ -62,7 +72,25 @@ function convertHexToString(hexString) {
return sjcl.codec.utf8String.fromBits(bits);
}
SerializedType.serialize_varint = function (so, val) {
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.');
}
@@ -74,13 +102,13 @@ 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.');
}
};
SerializedType.prototype.parse_varint = function (so) {
SerializedType.prototype.parse_varint = function(so) {
var b1 = so.read(1)[0], b2, b3;
var result;
@@ -102,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)) {
@@ -121,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;
@@ -136,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)));
}
@@ -146,21 +180,104 @@ function readAndSum(so, bytes) {
}
var STInt8 = exports.Int8 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
so.append(convertIntegerToByteArray(val, 1));
},
parse: function (so) {
parse: function(so) {
return readAndSum(so, 1);
}
});
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) {
serialize: function(so, val) {
so.append(convertIntegerToByteArray(val, 2));
},
parse: function (so) {
parse: function(so) {
return readAndSum(so, 2);
}
});
@@ -168,10 +285,10 @@ var STInt16 = exports.Int16 = new SerializedType({
STInt16.id = 1;
var STInt32 = exports.Int32 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
so.append(convertIntegerToByteArray(val, 4));
},
parse: function (so) {
parse: function(so) {
return readAndSum(so, 4);
}
});
@@ -179,7 +296,7 @@ var STInt32 = exports.Int32 = new SerializedType({
STInt32.id = 2;
var STInt64 = exports.Int64 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
var bigNumObject;
if (isNumber(val)) {
@@ -187,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.');
}
@@ -201,25 +318,25 @@ 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) {
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));
}
});
STInt64.id = 3;
var STHash128 = exports.Hash128 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
var hash = UInt128.from_json(val);
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) {
parse: function(so) {
return UInt128.from_bytes(so.read(16));
}
});
@@ -227,14 +344,14 @@ var STHash128 = exports.Hash128 = new SerializedType({
STHash128.id = 4;
var STHash256 = exports.Hash256 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
var hash = UInt256.from_json(val);
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) {
parse: function(so) {
return UInt256.from_bytes(so.read(32));
}
});
@@ -242,14 +359,14 @@ var STHash256 = exports.Hash256 = new SerializedType({
STHash256.id = 5;
var STHash160 = exports.Hash160 = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
var hash = UInt160.from_json(val);
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) {
parse: function(so) {
return UInt160.from_bytes(so.read(20));
}
});
@@ -258,31 +375,77 @@ STHash160.id = 17;
// Internal
var STCurrency = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
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);
},
parse: function (so) {
parse: function(so) {
var bytes = so.read(20);
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;
}
});
var STAmount = exports.Amount = new SerializedType({
serialize: function (so, val) {
/**
* Quality is encoded into 64 bits:
* (8 bits offset) (56 bits mantissa)
*
* Quality differs from Amount because it does not need the first two bits
* to represent non-native and non-negative
*/
exports.Quality = new SerializedType({
serialize: function(so, val) {
var amount = Amount.from_json(val);
if (!amount.is_valid()) {
throw new Error('Not a valid Amount object.');
}
var hi = 0, lo = 0;
var value = new BigNumber(amount.to_text());
var offset = value.e - 15;
if (!amount.is_zero()) {
// First eight bits: offset/exponent
hi |= ((100 + offset) & 0xff) << 24;
// Remaining 56 bits: mantissa
var mantissaDecimal = utils.getMantissaDecimalString(value.abs());
var mantissaHex = (new BigNumber(mantissaDecimal)).toString(16);
assert(mantissaHex.length <= 16,
'Mantissa hex representation ' + mantissaHex +
' exceeds the maximum length of 16');
hi |= parseInt(mantissaHex.slice(0, -8), 16) & 0xffffff;
lo = parseInt(mantissaHex.slice(-8), 16);
}
var valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
so.append(valueBytes);
}
});
/*
* Amount is encoded into 64 bits:
* (1 bit non-native) (1 bit non-negative) (8 bits offset) (54 bits mantissa)
*/
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.');
}
@@ -296,12 +459,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');
}
@@ -357,18 +520,18 @@ var STAmount = exports.Amount = new SerializedType({
so.append(amount.issuer().to_bytes());
}
},
parse: function (so) {
parse: function(so) {
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);
@@ -385,29 +548,28 @@ 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());
}
});
STAmount.id = 6;
var STVL = exports.VariableLength = exports.VL = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
if (typeof val === 'string') {
serializeHex(so, val);
} else {
throw new Error('Unknown datatype.');
}
},
parse: function (so) {
parse: function(so) {
var len = this.parse_varint(so);
return convertByteArrayToHex(so.read(len));
}
@@ -416,14 +578,14 @@ var STVL = exports.VariableLength = exports.VL = new SerializedType({
STVL.id = 7;
var STAccount = exports.Account = new SerializedType({
serialize: function (so, val) {
serialize: function(so, val) {
var account = UInt160.from_json(val);
if (!account.is_valid()) {
throw new Error('Invalid account!');
}
serializeBits(so, account.to_bits());
},
parse: function (so) {
parse: function(so) {
var len = this.parse_varint(so);
if (len !== 20) {
@@ -444,21 +606,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,
serialize: function (so, val) {
for (var i=0, l=val.length; i<l; i++) {
typeBoundary: 0xff,
typeEnd: 0x00,
typeAccount: 0x01,
typeCurrency: 0x10,
typeIssuer: 0x20,
serialize: function(so, val) {
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) {
@@ -490,7 +652,7 @@ var STPathSet = exports.PathSet = new SerializedType({
STInt8.serialize(so, this.typeEnd);
},
parse: function (so) {
parse: function(so) {
// should return a list of lists:
/*
[
@@ -500,41 +662,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;
@@ -542,27 +702,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);
}
@@ -573,17 +730,18 @@ 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]);
}
},
parse: function (so) {
parse: function(so) {
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;
@@ -595,10 +753,9 @@ STVector256.id = 19;
// Internal
exports.STMemo = new SerializedType({
serialize: function(so, val, no_marker) {
var keys = [];
Object.keys(val).forEach(function (key) {
Object.keys(val).forEach(function(key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
@@ -615,26 +772,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);
@@ -642,34 +797,42 @@ 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
// empty
// 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
// empty
// 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
// empty
// 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
}
}
@@ -679,103 +842,11 @@ 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) {
serialize: function(so, val, no_marker) {
var keys = [];
Object.keys(val).forEach(function (key) {
Object.keys(val).forEach(function(key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
@@ -792,17 +863,17 @@ 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);
}
},
parse: function (so) {
parse: function(so) {
var output = {};
while (so.peek(1)[0] !== 0xe1) {
var keyval = parse(so);
@@ -816,12 +887,13 @@ var STObject = exports.Object = new SerializedType({
STObject.id = 14;
var STArray = exports.Array = new SerializedType({
serialize: function (so, val) {
for (var i=0, l=val.length; i<l; i++) {
serialize: function(so, val) {
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];
@@ -829,11 +901,11 @@ var STArray = exports.Array = new SerializedType({
serialize(so, field_name, value);
}
//Array ending marker
// Array ending marker
STInt8.serialize(so, 0xf1);
},
parse: function (so) {
parse: function(so) {
var output = [ ];
while (so.peek(1)[0] !== 0xf1) {

View File

@@ -1,30 +1,19 @@
'use strict';
var util = require('util');
var sjcl = require('./utils').sjcl;
var stypes = require('./serializedtypes');
var hashprefixes = require('./hashprefixes');
var UInt256 = require('./uint256').UInt256;
var SerializedObject = require('./serializedobject').SerializedObject;
function SHAMap() {
this.root = new SHAMapTreeNodeInner(0);
};
SHAMap.prototype.add_item = function(tag, node, type) {
var node = new SHAMapTreeNodeLeaf(tag, node, type);
this.root.add_item(tag, node);
};
SHAMap.prototype.hash = function() {
return this.root.hash();
};
/**
* Abstract class representing a node in a SHAMap tree.
*
* Can be either SHAMapTreeNodeInner or SHAMapTreeNodeLeaf.
*
* @class
*/
function SHAMapTreeNode() { };
function SHAMapTreeNode() { }
SHAMapTreeNode.TYPE_INNER = 1;
SHAMapTreeNode.TYPE_TRANSACTION_NM = 2;
@@ -32,11 +21,17 @@ SHAMapTreeNode.TYPE_TRANSACTION_MD = 3;
SHAMapTreeNode.TYPE_ACCOUNT_STATE = 4;
/**
* @param tag {String} 64 hexadecimal characters
* @param {String} tag (64 hexadecimal characters)
* @param {SHAMapTreeNode} node
* @return {void}
* @virtual
*/
/*eslint-disable no-unused-vars*/
SHAMapTreeNode.prototype.add_item = function(tag, node) {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#add_item.');
throw new Error(
'Called unimplemented virtual method SHAMapTreeNode#add_item.');
};
/*eslint-enable no-unused-vars*/
SHAMapTreeNode.prototype.hash = function() {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#hash.');
@@ -44,6 +39,8 @@ SHAMapTreeNode.prototype.hash = function() {
/**
* Inner (non-leaf) node in a SHAMap tree.
* @param {Number} depth (i.e. how many parent inner nodes)
* @class
*/
function SHAMapTreeNodeInner(depth) {
SHAMapTreeNode.call(this);
@@ -51,7 +48,7 @@ function SHAMapTreeNodeInner(depth) {
this.leaves = {};
this.type = SHAMapTreeNode.INNER;
this.depth = depth == null ? 0 : depth;
this.depth = depth === undefined ? 0 : depth;
this.empty = true;
}
@@ -59,9 +56,11 @@ function SHAMapTreeNodeInner(depth) {
util.inherits(SHAMapTreeNodeInner, SHAMapTreeNode);
/**
* @param tag {String} (equates to a ledger entries `index`)
* @param {String} tag (equates to a ledger entry `index`)
* @param {SHAMapTreeNode} node (to add)
* @return {void}
*/
SHAMapTreeNodeInner.prototype.add_item = function (tag, node) {
SHAMapTreeNodeInner.prototype.add_item = function(tag, node) {
var depth = this.depth;
var existing_node = this.get_node(tag[depth]);
@@ -72,7 +71,8 @@ SHAMapTreeNodeInner.prototype.add_item = function (tag, node) {
existing_node.add_item(tag, node);
} else if (existing_node.tag === tag) {
// Collision
throw new Error('Tried to add a node to a SHAMap that was already in there.');
throw new Error(
'Tried to add a node to a SHAMap that was already in there.');
} else {
// Turn it into an inner node
var new_inner_node = new SHAMapTreeNodeInner(depth + 1);
@@ -92,6 +92,9 @@ SHAMapTreeNodeInner.prototype.add_item = function (tag, node) {
/**
* Overwrite the node that is currently in a given slot.
* @param {String} slot (a character 0-F)
* @param {SHAMapTreeNode} node (to place)
* @return {void}
*/
SHAMapTreeNodeInner.prototype.set_node = function(slot, node) {
this.leaves[slot] = node;
@@ -108,9 +111,8 @@ SHAMapTreeNodeInner.prototype.hash = function() {
}
var hash_buffer = new SerializedObject();
var buffer = [ ];
for (var i=0; i<16; i++) {
for (var i = 0; i < 16; i++) {
var leafHash = UInt256.from_hex(UInt256.HEX_ZERO);
var slot = i.toString(16).toUpperCase();
@@ -128,6 +130,10 @@ SHAMapTreeNodeInner.prototype.hash = function() {
/**
* Leaf node in a SHAMap tree.
* @param {String} tag (equates to a ledger entry `index`)
* @param {SerializedObject} node (bytes of account state, transaction etc)
* @param {Number} type (one of TYPE_ACCOUNT_STATE, TYPE_TRANSACTION_MD etc)
* @class
*/
function SHAMapTreeNodeLeaf(tag, node, type) {
SHAMapTreeNode.call(this);
@@ -140,11 +146,11 @@ function SHAMapTreeNodeLeaf(tag, node, type) {
this.tag_bytes = UInt256.from_hex(this.tag).to_bytes();
this.type = type;
this.node = node;
};
}
util.inherits(SHAMapTreeNodeLeaf, SHAMapTreeNode);
SHAMapTreeNodeLeaf.prototype.hash = function () {
SHAMapTreeNodeLeaf.prototype.hash = function() {
var buffer = new SerializedObject();
switch (this.type) {
case SHAMapTreeNode.TYPE_ACCOUNT_STATE:
@@ -162,6 +168,19 @@ SHAMapTreeNodeLeaf.prototype.hash = function () {
}
};
function SHAMap() {
this.root = new SHAMapTreeNodeInner(0);
}
SHAMap.prototype.add_item = function(tag, node, type) {
node = new SHAMapTreeNodeLeaf(tag, node, type);
this.root.add_item(tag, node);
};
SHAMap.prototype.hash = function() {
return this.root.hash();
};
exports.SHAMap = SHAMap;
exports.SHAMapTreeNode = SHAMapTreeNode;
exports.SHAMapTreeNodeInner = SHAMapTreeNodeInner;

View File

@@ -1,17 +1,17 @@
var util = require('util');
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var utils = require('./utils');
var sjcl = require('./utils').sjcl;
var Amount = require('./amount').Amount;
var Currency = require('./amount').Currency;
var UInt160 = require('./amount').UInt160;
var Seed = require('./seed').Seed;
'use strict';
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var utils = require('./utils');
var sjcl = require('./utils').sjcl;
var Amount = require('./amount').Amount;
var Currency = require('./amount').Currency;
var UInt160 = require('./amount').UInt160;
var Seed = require('./seed').Seed;
var SerializedObject = require('./serializedobject').SerializedObject;
var RippleError = require('./rippleerror').RippleError;
var hashprefixes = require('./hashprefixes');
var config = require('./config');
var log = require('./log').internal.sub('transaction');
var RippleError = require('./rippleerror').RippleError;
var hashprefixes = require('./hashprefixes');
var log = require('./log').internal.sub('transaction');
/**
* @constructor Transaction
@@ -35,14 +35,14 @@ function Transaction(remote) {
var remoteExists = (typeof remote === 'object');
this.remote = remote;
this.tx_json = { Flags: 0 };
this._secret = void(0);
this.tx_json = {Flags: 0};
this._secret = undefined;
this._build_path = false;
this._maxFee = remoteExists ? this.remote.max_fee : void(0);
this._maxFee = remoteExists ? this.remote.max_fee : undefined;
this.state = 'unsubmitted';
this.finalized = false;
this.previousSigningHash = void(0);
this.submitIndex = void(0);
this.previousSigningHash = undefined;
this.submitIndex = undefined;
this.canonical = remoteExists ? this.remote.canonical_signing : true;
this.submittedIDs = [ ];
this.attempts = 0;
@@ -76,11 +76,12 @@ function Transaction(remote) {
// Transaction was submitted successfully to the network
self.setState('pending');
});
};
}
util.inherits(Transaction, EventEmitter);
// XXX This needs to be determined from the network.
// This is currently a constant in rippled known as the "base reference"
// https://wiki.ripple.com/Transaction_Fee#Base_Fees
Transaction.fee_units = {
default: 10
};
@@ -88,38 +89,38 @@ Transaction.fee_units = {
Transaction.flags = {
// Universal flags can apply to any transaction type
Universal: {
FullyCanonicalSig: 0x80000000
FullyCanonicalSig: 0x80000000
},
AccountSet: {
RequireDestTag: 0x00010000,
OptionalDestTag: 0x00020000,
RequireAuth: 0x00040000,
OptionalAuth: 0x00080000,
DisallowXRP: 0x00100000,
AllowXRP: 0x00200000
RequireDestTag: 0x00010000,
OptionalDestTag: 0x00020000,
RequireAuth: 0x00040000,
OptionalAuth: 0x00080000,
DisallowXRP: 0x00100000,
AllowXRP: 0x00200000
},
TrustSet: {
SetAuth: 0x00010000,
NoRipple: 0x00020000,
SetNoRipple: 0x00020000,
ClearNoRipple: 0x00040000,
SetFreeze: 0x00100000,
ClearFreeze: 0x00200000
SetAuth: 0x00010000,
NoRipple: 0x00020000,
SetNoRipple: 0x00020000,
ClearNoRipple: 0x00040000,
SetFreeze: 0x00100000,
ClearFreeze: 0x00200000
},
OfferCreate: {
Passive: 0x00010000,
ImmediateOrCancel: 0x00020000,
FillOrKill: 0x00040000,
Sell: 0x00080000
Passive: 0x00010000,
ImmediateOrCancel: 0x00020000,
FillOrKill: 0x00040000,
Sell: 0x00080000
},
Payment: {
NoRippleDirect: 0x00010000,
PartialPayment: 0x00020000,
LimitQuality: 0x00040000
NoRippleDirect: 0x00010000,
PartialPayment: 0x00020000,
LimitQuality: 0x00040000
}
};
@@ -128,13 +129,14 @@ Transaction.flags = {
// SetFlag or ClearFlag field
Transaction.set_clear_flags = {
AccountSet: {
asfRequireDest: 1,
asfRequireAuth: 2,
asfDisallowXRP: 3,
asfDisableMaster: 4,
asfAccountTxnID: 5,
asfNoFreeze: 6,
asfGlobalFreeze: 7
asfRequireDest: 1,
asfRequireAuth: 2,
asfDisallowXRP: 3,
asfDisableMaster: 4,
asfAccountTxnID: 5,
asfNoFreeze: 6,
asfGlobalFreeze: 7,
asfDefaultRipple: 8
}
};
@@ -146,12 +148,12 @@ Transaction.ASCII_REGEX = /^[\x00-\x7F]*$/;
Transaction.formats = require('./binformat').tx;
Transaction.prototype.consts = {
telLOCAL_ERROR: -399,
temMALFORMED: -299,
tefFAILURE: -199,
terRETRY: -99,
tesSUCCESS: 0,
tecCLAIMED: 100
telLOCAL_ERROR: -399,
temMALFORMED: -299,
tefFAILURE: -199,
terRETRY: -99,
tesSUCCESS: 0,
tecCLAIMED: 100
};
Transaction.prototype.isTelLocal = function(ter) {
@@ -179,7 +181,9 @@ Transaction.prototype.isTecClaimed = function(ter) {
};
Transaction.prototype.isRejected = function(ter) {
return this.isTelLocal(ter) || this.isTemMalformed(ter) || this.isTefFailure(ter);
return this.isTelLocal(ter) ||
this.isTemMalformed(ter) ||
this.isTefFailure(ter);
};
Transaction.from_json = function(j) {
@@ -216,7 +220,7 @@ Transaction.prototype.finalize = function(message) {
if (this.result) {
this.result.ledger_index = message.ledger_index;
this.result.ledger_hash = message.ledger_hash;
this.result.ledger_hash = message.ledger_hash;
} else {
this.result = message;
this.result.tx_json = this.tx_json;
@@ -262,7 +266,7 @@ Transaction.prototype.getTransactionType = function() {
Transaction.prototype.getManager = function(account) {
if (!this.remote) {
return void(0);
return undefined;
}
if (!account) {
@@ -281,7 +285,7 @@ Transaction.prototype.getManager = function(account) {
Transaction.prototype.getSecret =
Transaction.prototype._accountSecret = function(account) {
if (!this.remote) {
return void(0);
return undefined;
}
if (!account) {
@@ -305,7 +309,7 @@ Transaction.prototype._accountSecret = function(account) {
Transaction.prototype._getFeeUnits =
Transaction.prototype.feeUnits = function() {
return Transaction.fee_units['default'];
return Transaction.fee_units.default;
};
/**
@@ -316,13 +320,13 @@ Transaction.prototype.feeUnits = function() {
Transaction.prototype._computeFee = function() {
if (!this.remote) {
return void(0);
return undefined;
}
var servers = this.remote._servers;
var fees = [ ];
for (var i=0; i<servers.length; i++) {
for (var i = 0; i < servers.length; i++) {
var server = servers[i];
if (server._connected) {
fees.push(Number(server._computeFee(this._getFeeUnits())));
@@ -330,7 +334,7 @@ Transaction.prototype._computeFee = function() {
}
switch (fees.length) {
case 0: return;
case 0: return undefined;
case 1: return String(fees[0]);
}
@@ -339,9 +343,8 @@ Transaction.prototype._computeFee = function() {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
return 0;
});
var midInd = Math.floor(fees.length / 2);
@@ -373,8 +376,12 @@ Transaction.prototype.complete = function() {
}
}
if (!this._secret) {
this._secret = this.getSecret();
}
// Try to auto-fill the secret
if (!this._secret && !(this._secret = this.getSecret())) {
if (!this._secret) {
this.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
return false;
}
@@ -382,7 +389,7 @@ Transaction.prototype.complete = function() {
if (typeof this.tx_json.SigningPubKey === 'undefined') {
try {
var seed = Seed.from_json(this._secret);
var key = seed.get_key(this.tx_json.Account);
var key = seed.get_key(this.tx_json.Account);
this.tx_json.SigningPubKey = key.to_hex_pub();
} catch(e) {
this.emit('error', new RippleError(
@@ -395,7 +402,8 @@ Transaction.prototype.complete = function() {
// an assigned server
if (this.remote && typeof this.tx_json.Fee === 'undefined') {
if (this.remote.local_fee || !this.remote.trusted) {
if (!(this.tx_json.Fee = this._computeFee())) {
this.tx_json.Fee = this._computeFee();
if (!this.tx_json.Fee) {
this.emit('error', new RippleError('tejUnconnected'));
return false;
}
@@ -424,8 +432,8 @@ Transaction.prototype.serialize = function() {
return SerializedObject.from_json(this.tx_json);
};
Transaction.prototype.signingHash = function() {
return this.hash(config.testnet ? 'HASH_TX_SIGN_TESTNET' : 'HASH_TX_SIGN');
Transaction.prototype.signingHash = function(testnet) {
return this.hash(testnet ? 'HASH_TX_SIGN_TESTNET' : 'HASH_TX_SIGN');
};
Transaction.prototype.hash = function(prefix, asUINT256, serialized) {
@@ -443,13 +451,13 @@ Transaction.prototype.hash = function(prefix, asUINT256, serialized) {
return asUINT256 ? hash : hash.to_hex();
};
Transaction.prototype.sign = function() {
Transaction.prototype.sign = function(testnet) {
var seed = Seed.from_json(this._secret);
var prev_sig = this.tx_json.TxnSignature;
delete this.tx_json.TxnSignature;
var hash = this.signingHash();
var hash = this.signingHash(testnet);
// If the hash is the same, we can re-use the previous signature
if (prev_sig && hash === this.previousSigningHash) {
@@ -458,7 +466,7 @@ Transaction.prototype.sign = function() {
}
var key = seed.get_key(this.tx_json.Account);
var sig = key.sign(hash, 0);
var sig = key.sign(hash);
var hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
this.tx_json.TxnSignature = hex;
@@ -493,7 +501,7 @@ Transaction.prototype.addId = function(id) {
Transaction.prototype.findId = function(cache) {
var result;
for (var i=0; i<this.submittedIDs.length; i++) {
for (var i = 0; i < this.submittedIDs.length; i++) {
var hash = this.submittedIDs[i];
if ((result = cache[hash])) {
break;
@@ -628,7 +636,7 @@ Transaction.prototype.maxFee = function(fee) {
};
/*
* Set the fee user will pay to the network for submitting this transaction.
* Set the fee user will pay to the network for submitting this transaction.
* Specified fee must be >= 0.
*
* @param {Number} fee The proposed fee
@@ -715,7 +723,7 @@ Transaction.prototype.pathAdd = function(path) {
Transaction.prototype.setPaths =
Transaction.prototype.paths = function(paths) {
if (Array.isArray(paths)) {
for (var i=0, l=paths.length; i<l; i++) {
for (var i = 0, l = paths.length; i < l; i++) {
this.addPath(paths[i]);
}
}
@@ -777,7 +785,7 @@ Transaction.prototype.transferRate = function(rate) {
*/
Transaction.prototype.setFlags = function(flags) {
if (flags === void(0)) {
if (flags === undefined) {
return this;
}
@@ -789,7 +797,7 @@ Transaction.prototype.setFlags = function(flags) {
var transaction_flags = Transaction.flags[this.getTransactionType()] || { };
var flag_set = Array.isArray(flags) ? flags : [].slice.call(arguments);
for (var i=0, l=flag_set.length; i<l; i++) {
for (var i = 0, l = flag_set.length; i < l; i++) {
var flag = flag_set[i];
if (transaction_flags.hasOwnProperty(flag)) {
@@ -844,8 +852,8 @@ Transaction.prototype.addMemo = function(memoType, memoFormat, memoData) {
if (memoType) {
if (Transaction.MEMO_TYPES[memoType]) {
//XXX Maybe in the future we want a schema validator for
//memo types
// XXX Maybe in the future we want a schema validator for
// memo types
memoType = Transaction.MEMO_TYPES[memoType];
}
memo.MemoType = convertStringToHex(memoType);
@@ -861,17 +869,19 @@ Transaction.prototype.addMemo = function(memoType, memoFormat, memoData) {
try {
memoData = JSON.stringify(memoData);
} catch (e) {
throw new Error('MemoFormat json with invalid JSON in MemoData field');
throw new Error(
'MemoFormat json with invalid JSON in MemoData field');
}
} else {
throw new Error('MemoData can only be a JSON object with a valid json MemoFormat');
throw new Error(
'MemoData can only be a JSON object with a valid json MemoFormat');
}
}
memo.MemoData = convertStringToHex(memoData);
}
this.tx_json.Memos = (this.tx_json.Memos || []).concat({ Memo: memo });
this.tx_json.Memos = (this.tx_json.Memos || []).concat({Memo: memo});
return this;
};
@@ -916,23 +926,33 @@ Transaction.prototype.accountSet = function(src, set_flag, clear_flag) {
throw new Error('Source address invalid');
}
this.tx_json.TransactionType= 'AccountSet';
this.tx_json.TransactionType = 'AccountSet';
this.tx_json.Account = UInt160.json_rewrite(src);
var SetClearFlags = Transaction.set_clear_flags.AccountSet;
function prepareFlag(flag) {
return (typeof flag === 'number')
? flag
? flag
: (SetClearFlags[flag] || SetClearFlags['asf' + flag]);
};
if (set_flag && (set_flag = prepareFlag(set_flag))) {
this.tx_json.SetFlag = set_flag;
}
if (clear_flag && (clear_flag = prepareFlag(clear_flag))) {
this.tx_json.ClearFlag = clear_flag;
var prepared;
if (set_flag) {
prepared = prepareFlag(set_flag);
if (prepared) {
this.tx_json.SetFlag = prepared;
}
}
if (clear_flag) {
prepared = prepareFlag(clear_flag);
if (prepared) {
this.tx_json.ClearFlag = prepared;
}
}
return this;
@@ -994,7 +1014,8 @@ Transaction.prototype.offerCancel = function(src, sequence) {
* @param [Number] sequence of an existing offer to replace
*/
Transaction.prototype.offerCreate = function(src, taker_pays, taker_gets, expiration, cancel_sequence) {
Transaction.prototype.offerCreate = function(src, taker_pays, taker_gets,
expiration, cancel_sequence) {
if (typeof src === 'object') {
var options = src;
cancel_sequence = options.cancel_sequence || options.sequence;
@@ -1110,7 +1131,10 @@ Transaction.prototype.payment = function(src, dst, amount) {
*/
Transaction.prototype.trustSet =
Transaction.prototype.rippleLineSet = function(src, limit, quality_in, quality_out) {
Transaction.prototype.rippleLineSet = function(src,
limit,
quality_in,
quality_out) {
if (typeof src === 'object') {
var options = src;
quality_out = options.quality_out;
@@ -1126,7 +1150,7 @@ Transaction.prototype.rippleLineSet = function(src, limit, quality_in, quality_o
this.tx_json.TransactionType = 'TrustSet';
this.tx_json.Account = UInt160.json_rewrite(src);
if (limit !== void(0)) {
if (limit !== undefined) {
this.tx_json.LimitAmount = Amount.json_rewrite(limit);
}
@@ -1152,7 +1176,7 @@ Transaction.prototype.rippleLineSet = function(src, limit, quality_in, quality_o
Transaction.prototype.submit = function(callback) {
var self = this;
this.callback = (typeof callback === 'function') ? callback : function(){};
this.callback = (typeof callback === 'function') ? callback : function() {};
this._errorHandler = function transactionError(error, message) {
if (!(error instanceof RippleError)) {
@@ -1167,7 +1191,7 @@ Transaction.prototype.submit = function(callback) {
if (!this.remote) {
this.emit('error', new Error('No remote found'));
return;
return this;
}
this.getManager().submit(this);
@@ -1197,7 +1221,7 @@ Transaction.prototype.summary = function() {
initialSubmitIndex: this.initialSubmitIndex,
lastLedgerSequence: this.lastLedgerSequence,
state: this.state,
server: this._server ? this._server._opts.url : void(0),
server: this._server ? this._server._opts.url : undefined,
finalized: this.finalized
};

View File

@@ -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.

View File

@@ -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;

View File

@@ -1,6 +1,9 @@
var utils = require('./utils');
var sjcl = utils.sjcl;
var config = require('./config');
'use strict';
/*eslint new-cap: 1*/
var utils = require('./utils');
var sjcl = utils.sjcl;
//
// Abstract UInt class
@@ -8,11 +11,11 @@ var config = require('./config');
// Base class for UInt classes
//
var UInt = function() {
function UInt() {
// Internal form: NaN or sjcl.bn
this._value = NaN;
this._value = NaN;
this._update();
};
}
UInt.json_rewrite = function(j, opts) {
return this.from_json(j).to_json(opts);
@@ -22,63 +25,56 @@ UInt.json_rewrite = function(j, opts) {
UInt.from_generic = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_generic(j);
}
return (new this()).parse_generic(j);
};
// Return a new UInt from j.
UInt.from_hex = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_hex(j);
}
return (new this()).parse_hex(j);
};
// Return a new UInt from j.
UInt.from_json = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_json(j);
}
return (new this()).parse_json(j);
};
// Return a new UInt from j.
UInt.from_bits = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_bits(j);
}
return (new this()).parse_bits(j);
};
// Return a new UInt from j.
UInt.from_bytes = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_bytes(j);
}
return (new this()).parse_bytes(j);
};
// Return a new UInt from j.
UInt.from_bn = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_bn(j);
}
return (new this()).parse_bn(j);
};
// Return a new UInt from j.
UInt.from_number = function(j) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_number(j);
}
return (new this()).parse_number(j);
};
UInt.is_valid = function(j) {
@@ -93,7 +89,7 @@ UInt.prototype.clone = function() {
UInt.prototype.copyTo = function(d) {
d._value = this._value;
if (this._version_byte !== void(0)) {
if (this._version_byte !== undefined) {
d._version_byte = this._version_byte;
}
@@ -125,6 +121,8 @@ UInt.prototype.is_zero = function() {
*
* The reason for keeping this mechanism in this class is so every subclass can
* call it whenever it modifies the internal state.
*
* @return {void}
*/
UInt.prototype._update = function() {
// Nothing to do by default. Subclasses will override this.
@@ -132,38 +130,33 @@ UInt.prototype._update = function() {
// value = NaN on error.
UInt.prototype.parse_generic = function(j) {
// Canonicalize and validate
if (config.accounts && (j in config.accounts)) {
j = config.accounts[j].account;
}
switch (j) {
case undefined:
case '0':
case this.constructor.STR_ZERO:
case this.constructor.ACCOUNT_ZERO:
case this.constructor.HEX_ZERO:
this._value = new sjcl.bn(0);
case '0':
case this.constructor.STR_ZERO:
case this.constructor.ACCOUNT_ZERO:
case this.constructor.HEX_ZERO:
this._value = new sjcl.bn(0);
break;
case '1':
case this.constructor.STR_ONE:
case this.constructor.ACCOUNT_ONE:
case this.constructor.HEX_ONE:
this._value = new sjcl.bn(1);
case this.constructor.STR_ONE:
case this.constructor.ACCOUNT_ONE:
case this.constructor.HEX_ONE:
this._value = new sjcl.bn(1);
break;
default:
if (typeof j !== 'string') {
this._value = NaN;
this._value = NaN;
} else if (this.constructor.width === j.length) {
var hex = utils.arrayToHex(utils.stringToArray(j));
this._value = new sjcl.bn(hex, 16);
this._value = new sjcl.bn(hex, 16);
} else if ((this.constructor.width * 2) === j.length) {
// XXX Check char set!
this._value = new sjcl.bn(j, 16);
this._value = new sjcl.bn(j, 16);
} else {
this._value = NaN;
this._value = NaN;
}
}
@@ -204,7 +197,7 @@ UInt.prototype.parse_bytes = function(j) {
this._value = NaN;
} else {
var bits = sjcl.codec.bytes.toBits(j);
this._value = sjcl.bn.fromBits(bits);
this._value = sjcl.bn.fromBits(bits);
}
this._update();
@@ -216,9 +209,8 @@ UInt.prototype.parse_bytes = function(j) {
UInt.prototype.parse_json = UInt.prototype.parse_hex;
UInt.prototype.parse_bn = function(j) {
if ((j instanceof sjcl.bn) && j.bitLength() <= this.constructor.width * 8) {
// var bytes = sjcl.codec.bytes.fromBits(j.toBits());
// this._value = new sjcl.bn(utils.arrayToHex(bytes), 16);
if ((j instanceof sjcl.bn) &&
j.bitLength() <= this.constructor.width * 8) {
this._value = new sjcl.bn(j);
} else {
this._value = NaN;

View File

@@ -1,13 +1,15 @@
var utils = require('./utils');
'use strict';
var utils = require('./utils');
var extend = require('extend');
var UInt = require('./uint').UInt;
var UInt = require('./uint').UInt;
//
// UInt128 support
//
var UInt128 = extend(function () {
this._value = NaN;
var UInt128 = extend(function() {
this._value = NaN;
}, UInt);
UInt128.width = 16;
@@ -15,8 +17,9 @@ UInt128.prototype = extend({}, UInt.prototype);
UInt128.prototype.constructor = UInt128;
var HEX_ZERO = UInt128.HEX_ZERO = '00000000000000000000000000000000';
var HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
var STR_ZERO = UInt128.STR_ZERO = utils.hexToString(HEX_ZERO);
var STR_ONE = UInt128.STR_ONE = utils.hexToString(HEX_ONE);
var HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
UInt128.STR_ZERO = utils.hexToString(HEX_ZERO);
UInt128.STR_ONE = utils.hexToString(HEX_ONE);
exports.UInt128 = UInt128;

View File

@@ -1,6 +1,7 @@
var utils = require('./utils');
var config = require('./config');
var extend = require('extend');
'use strict';
var utils = require('./utils');
var extend = require('extend');
var UInt = require('./uint').UInt;
var Base = require('./base').Base;
@@ -10,8 +11,8 @@ var Base = require('./base').Base;
//
var UInt160 = extend(function() {
this._value = NaN;
this._version_byte = void(0);
this._value = NaN;
this._version_byte = undefined;
this._update();
}, UInt);
@@ -19,12 +20,13 @@ UInt160.width = 20;
UInt160.prototype = extend({}, UInt.prototype);
UInt160.prototype.constructor = UInt160;
var ACCOUNT_ZERO = UInt160.ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
var ACCOUNT_ONE = UInt160.ACCOUNT_ONE = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
var HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);
var STR_ONE = UInt160.STR_ONE = utils.hexToString(HEX_ONE);
var HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
UInt160.ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
UInt160.ACCOUNT_ONE = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);
UInt160.STR_ONE = utils.hexToString(HEX_ONE);
UInt160.prototype.set_version = function(j) {
this._version_byte = j;
@@ -37,11 +39,6 @@ UInt160.prototype.get_version = function() {
// value = NaN on error.
UInt160.prototype.parse_json = function(j) {
// Canonicalize and validate
if (config.accounts && (j in config.accounts)) {
j = config.accounts[j].account;
}
if (typeof j === 'number' && !isNaN(j)) {
// Allow raw numbers - DEPRECATED
// This is used mostly by the test suite and is supported
@@ -78,7 +75,7 @@ UInt160.prototype.parse_generic = function(j) {
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
UInt160.prototype.to_json = function(opts) {
opts = opts || {};
opts = opts || {};
if (this.is_valid()) {
// If this value has a type, return a Base58 encoded string.
@@ -90,9 +87,8 @@ UInt160.prototype.to_json = function(opts) {
}
return output;
} else {
return this.to_hex();
}
return this.to_hex();
}
return NaN;
};

View File

@@ -1,6 +1,8 @@
var utils = require('./utils');
'use strict';
var utils = require('./utils');
var extend = require('extend');
var UInt = require('./uint').UInt;
var UInt = require('./uint').UInt;
//
// UInt256 support
@@ -14,9 +16,13 @@ UInt256.width = 32;
UInt256.prototype = extend({}, UInt.prototype);
UInt256.prototype.constructor = UInt256;
var HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' + '00000000000000000000000000000000';
var HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' + '00000000000000000000000000000001';
var STR_ZERO = UInt256.STR_ZERO = utils.hexToString(HEX_ZERO);
var STR_ONE = UInt256.STR_ONE = utils.hexToString(HEX_ONE);
var HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' +
'00000000000000000000000000000000';
var HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' +
'00000000000000000000000000000001';
UInt256.STR_ZERO = utils.hexToString(HEX_ZERO);
UInt256.STR_ONE = utils.hexToString(HEX_ONE);
exports.UInt256 = UInt256;

View File

@@ -1,3 +1,4 @@
'use strict';
function getMantissaDecimalString(bignum) {
var mantissa = bignum.toPrecision(16)
@@ -10,37 +11,22 @@ function getMantissaDecimalString(bignum) {
return mantissa;
}
function filterErr(code, done) {
return function(e) {
done(e.code !== code ? e : void(0));
};
};
function throwErr(done) {
return function(e) {
if (e) {
throw e;
}
done();
};
};
function trace(comment, func) {
return function() {
console.log('%s: %s', trace, arguments.toString);
func(arguments);
};
};
}
function arraySet(count, value) {
var a = new Array(count);
for (var i=0; i<count; i++) {
for (var i = 0; i < count; i++) {
a[i] = value;
}
return a;
};
}
function hexToString(h) {
var a = [];
@@ -51,35 +37,35 @@ function hexToString(h) {
i = 1;
}
for (; i<h.length; i+=2) {
a.push(String.fromCharCode(parseInt(h.substring(i, i+2), 16)));
for (; i < h.length; i += 2) {
a.push(String.fromCharCode(parseInt(h.substring(i, i + 2), 16)));
}
return a.join('');
};
}
function stringToHex(s) {
var result = '';
for (var i=0; i<s.length; i++) {
for (var i = 0; i < s.length; i++) {
var b = s.charCodeAt(i);
result += b < 16 ? '0' + b.toString(16) : b.toString(16);
}
return result;
};
}
function stringToArray(s) {
var a = new Array(s.length);
for (var i=0; i<a.length; i+=1) {
for (var i = 0; i < a.length; i += 1) {
a[i] = s.charCodeAt(i);
}
return a;
};
}
function hexToArray(h) {
return stringToArray(hexToString(h));
};
}
function arrayToHex(a) {
return a.map(function(byteValue) {
@@ -90,7 +76,7 @@ function arrayToHex(a) {
function chunkString(str, n, leftAlign) {
var ret = [];
var i=0, len=str.length;
var i = 0, len = str.length;
if (leftAlign) {
i = str.length % n;
@@ -99,26 +85,27 @@ function chunkString(str, n, leftAlign) {
}
}
for(; i<len; i+=n) {
for (; i < len; i += n) {
ret.push(str.slice(i, n + i));
}
return ret;
};
}
function assert(assertion, msg) {
if (!assertion) {
throw new Error('Assertion failed' + (msg ? ': ' + msg : '.'));
}
};
}
/**
* Return unique values in array.
* @param {Array} arr (values)
* @return {Array} unique values (for string representation of value) in `arr`
*/
function arrayUnique(arr) {
var u = {}, a = [];
for (var i=0, l=arr.length; i<l; i++){
for (var i = 0, l = arr.length; i < l; i++) {
var k = arr[i];
if (u[k]) {
continue;
@@ -128,46 +115,45 @@ function arrayUnique(arr) {
}
return a;
};
}
/**
* Convert a ripple epoch to a JavaScript timestamp.
* @param {Number} rpepoch (seconds since 1/1/2000 GMT)
* @return {Number} ms since unix epoch
*
* JavaScript timestamps are unix epoch in milliseconds.
*/
function toTimestamp(rpepoch) {
return (rpepoch + 0x386D4380) * 1000;
};
}
/**
* Convert a JavaScript timestamp or Date to a Ripple epoch.
*
* JavaScript timestamps are unix epoch in milliseconds.
* @param {Number|Date} timestamp (ms since unix epoch)
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function fromTimestamp(rpepoch) {
if (rpepoch instanceof Date) {
rpepoch = rpepoch.getTime();
function fromTimestamp(timestamp) {
if (timestamp instanceof Date) {
timestamp = timestamp.getTime();
}
return Math.round(rpepoch / 1000) - 0x386D4380;
};
return Math.round(timestamp / 1000) - 0x386D4380;
}
exports.time = {
fromRipple: toTimestamp,
toRipple: fromTimestamp
};
exports.trace = trace;
exports.arraySet = arraySet;
exports.hexToString = hexToString;
exports.hexToArray = hexToArray;
exports.trace = trace;
exports.arraySet = arraySet;
exports.hexToString = hexToString;
exports.hexToArray = hexToArray;
exports.stringToArray = stringToArray;
exports.stringToHex = stringToHex;
exports.arrayToHex = arrayToHex;
exports.chunkString = chunkString;
exports.assert = assert;
exports.arrayUnique = arrayUnique;
exports.toTimestamp = toTimestamp;
exports.stringToHex = stringToHex;
exports.arrayToHex = arrayToHex;
exports.chunkString = chunkString;
exports.assert = assert;
exports.arrayUnique = arrayUnique;
exports.toTimestamp = toTimestamp;
exports.fromTimestamp = fromTimestamp;
exports.getMantissaDecimalString = getMantissaDecimalString;

View File

@@ -1,8 +0,0 @@
var sjcl = require('./utils').sjcl;
var WalletGenerator = require('ripple-wallet-generator')({
sjcl: sjcl
});
module.exports = WalletGenerator;

File diff suppressed because it is too large Load Diff

66
test/base-test.js Normal file
View 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));
}
});
});
});

View File

@@ -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

View 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);
});
});

View File

@@ -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}));

5
test/fixtures/addresses.js vendored Normal file
View File

@@ -0,0 +1,5 @@
module.exports.ACCOUNT = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
module.exports.OTHER_ACCOUNT = 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo';
module.exports.THIRD_ACCOUNT = 'rwBYyfufTzk77zUSKEu4MvixfarC35av1J';
module.exports.FOURTH_ACCOUNT = 'rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr';
module.exports.ISSUER = 'rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM';

141
test/fixtures/base58.json vendored Normal file
View 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"
}
]
}

969
test/fixtures/orderbook.js vendored Normal file
View File

@@ -0,0 +1,969 @@
/*eslint-disable max-len */
'use strict';
var _ = require('lodash');
var addresses = require('./addresses');
var Meta = require('ripple-lib').Meta;
module.exports.FIAT_BALANCE = '10';
module.exports.NATIVE_BALANCE = '55';
module.exports.NATIVE_BALANCE_PREVIOUS = '100';
module.exports.TAKER_GETS = '19.84580331';
module.exports.TAKER_GETS_FINAL = '18.84580331';
module.exports.TAKER_PAYS = '3878342440';
module.exports.TAKER_PAYS_FINAL = '3078342440';
module.exports.OTHER_TAKER_GETS = '4.9656112525';
module.exports.OTHER_TAKER_GETS_FINAL = '3.9656112525';
module.exports.OTHER_TAKER_PAYS = '972251352';
module.exports.OTHER_TAKER_PAYS_FINAL = '902251352';
module.exports.LEDGER_INDEX = '06AFB03237286C1566CD649CFD5388C2C1F5BEFC5C3302A1962682803A9946FA';
module.exports.OTHER_LEDGER_INDEX = 'D3338DA77BA23122FB5647B74B53636AB54BE246D4B21707C9D6887DEB334252';
module.exports.TRANSFER_RATE = 1002000000;
module.exports.fiatOffers = function(options) {
options = options || {};
_.defaults(options, {
account_funds: '318.3643710638508',
other_account_funds: '235.0194163432668'
});
return [
{
Account: addresses.ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5D06F15E821839FB',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000001897',
PreviousTxnID: '11BA57676711A42C2FC2191EAEE98023B04627DFA84926B0C8E9D61A9CAF13AD',
PreviousTxnLgrSeq: 8265601,
Sequence: 531927,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.TAKER_GETS
},
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: module.exports.TAKER_PAYS,
index: module.exports.LEDGER_INDEX,
owner_funds: options.account_funds,
quality: '195423807.2109563'
},
{
Account: addresses.ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5D06F4C3362FE1D0',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CC',
PreviousTxnID: 'CD77500EF28984BFC123E8A257C10E44FF486EA8FC43E1356C42BD6DB853A602',
PreviousTxnLgrSeq: 8265523,
Sequence: 1139002,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '4.9656112525'
},
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: '972251352',
index: 'X2K98DB77BA23122FB5647B74B53636AB54BE246D4B21707C9D6887DEB334252',
owner_funds: options.account_funds,
quality: '195796912.5171664'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5D06F4C3362FE1D0',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CC',
PreviousTxnID: 'CD77500EF28984BFC123E8A257C10E44FF486EA8FC43E1356C42BD6DB853A602',
PreviousTxnLgrSeq: 8265523,
Sequence: 1139002,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.OTHER_TAKER_GETS
},
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: module.exports.OTHER_TAKER_PAYS,
index: module.exports.OTHER_LEDGER_INDEX,
owner_funds: options.other_account_funds,
quality: '195796912.5171664'
}
];
};
module.exports.NATIVE_OFFERS = [
{
Account: addresses.ACCOUNT,
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124AF94ED1781B',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CA',
PreviousTxnID: '51C64E0B300E9C0E877BA3E79B4ED1DBD5FDDCE58FA1A8FDA5F8DDF139787A24',
PreviousTxnLgrSeq: 8265275,
Sequence: 1138918,
TakerGets: '50',
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '5'
},
index: 'DC003E09AD1306FBBD1957C955EE668E429CC85B0EC0EC17297F6676E6108DE7',
owner_funds: '162110617177',
quality: '0.000000005148984210454555'
},
{
Account: addresses.ACCOUNT,
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124B054BAD1D79',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000001896',
PreviousTxnID: '9B21C7A4B66DC1CD5FC9D85C821C4CAA8F80E437582BAD11E88A1E9E6C7AA59C',
PreviousTxnLgrSeq: 8265118,
Sequence: 531856,
TakerGets: '10',
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '20'
},
index: '7AC0458676A54E99FAA5ED0A56CD0CB814D3DEFE1C7874F0BB39875D60668E41',
owner_funds: '430527438338',
quality: '0.000000005149035697347961'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5D06F4C3362FE1D0',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CC',
PreviousTxnID: 'CD77500EF28984BFC123E8A257C10E44FF486EA8FC43E1356C42BD6DB853A602',
PreviousTxnLgrSeq: 8265523,
Sequence: 1139002,
TakerGets: '972251352',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '4.9656112525'
},
index: 'D3338DA77BA23122FB5647B74B53636AB54BE246D4B21707C9D6887DEB334252',
owner_funds: '235.0194163432668',
quality: '195796912.5171664'
}
];
module.exports.REQUEST_OFFERS = [
{
Account: addresses.ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
PreviousTxnLgrSeq: 8342933,
Sequence: 195,
TakerGets: {
currency: 'BTC',
issuer: addresses.ISSUER,
value: '0.1129232560043778'
},
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '0.1129267125000245',
quality: '496.4999999999999'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: addresses.ISSUER,
value: '0.2'
},
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29356,
TakerGets: {
currency: 'BTC',
issuer: addresses.ISSUER,
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: addresses.ISSUER,
value: '0.5'
},
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '199.4446703295291'
}
];
module.exports.REQUEST_OFFERS_NATIVE = [
{
Account: addresses.ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
Sequence: 195,
TakerGets: '1000',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '600',
quality: '.0560663966061735'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '4000',
quality: '0.049861167582382'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
Sequence: 29356,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '3900',
quality: '0.049861167582382'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131078,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: '2000',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
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'
}
];
module.exports.LEG_ONE_OFFERS = [
{
Account: addresses.ACCOUNT,
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D043654A0DBD245',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000078',
PreviousTxnID: '27723DCE3E6DB324DBCE9F0C9110352DBBC04DD6BEFE2A57C4E524FD215144C9',
PreviousTxnLgrSeq: 12024847,
Sequence: 14532890,
TakerGets: '31461561812',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '373.019921005'
},
index: '7EEE980B0BD43C15504B9A89164D29EF02DBBD3807DA7936F51EA2CE3D0C6324',
owner_funds: '210586312936',
quality: '0.00000001185637010756165'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D043676B9DEA2FC',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000002',
PreviousTxnID: '1B36F7DE44C96FBDB50F8F80D24D3FA11454CB837BA4E4D667C92E01AE9225F5',
PreviousTxnLgrSeq: 12024788,
Sequence: 244399,
TakerGets: '25299728855',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '300'
},
index: '5F8BDA3343CB792FA0DD55740F5827C5E050A287C96FDE4F7DFF548693420744',
owner_funds: '1291056089559',
quality: '0.00000001185783459259132'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D0437FF40E6F02A',
BookNode: '0000000000000000',
Expiration: 478636633,
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000165',
PreviousTxnID: 'D42D81273BDC3ED611ED84DF07EA55E31703F4E05BC70CC12871715FCB58E160',
PreviousTxnLgrSeq: 12024847,
Sequence: 3858033,
TakerGets: '18189943147',
TakerPays: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '216'
},
index: 'FD5E66163DFE67919E64F31D506A8F3E94802E6A0FFEBE7A6FD40A2F1135EDD4',
owner_funds: '490342145233',
quality: '0.0000000118746935190737'
}
];
module.exports.LEG_TWO_OFFERS = [
{
Account: addresses.FOURTH_ACCOUNT,
BookDirectory: 'DA36FDE1B8CE294B214BE4E4C958DAAF9C1F46DE1FCB44115D0A4929E095B160',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000003',
PreviousTxnID: '97A8D6B2135231363EC1B3B509DF052D481A0045684464948E6DF2C2B9FC1E64',
PreviousTxnLgrSeq: 12004045,
Sequence: 384,
TakerGets: {
currency: 'EUR',
issuer: addresses.ISSUER,
value: '17.07639524223001'
},
TakerPays: '4943947661',
index: '5B00ACF35041983F070EAE2219C274D24A11D6FD6FE4306A4C72E7B769D4F914',
owner_funds: '36.40299530003982',
quality: '289519397.75'
},
{
Account: addresses.FOURTH_ACCOUNT,
BookDirectory: 'DA36FDE1B8CE294B214BE4E4C958DAAF9C1F46DE1FCB44115E12B2D070B5DBE0',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000006',
PreviousTxnID: '425EBA467DD335602BAFBAB5329B1E7FC1ABB325AA5CD4495A5085860D09F2BE',
PreviousTxnLgrSeq: 11802828,
Sequence: 605,
TakerGets: {
currency: 'EUR',
issuer: addresses.ISSUER,
value: '19.99999999954904'
},
TakerPays: '105263157889',
index: '8715E674302D446EBD520FF11B48A0F64822F4F9266D62544987223CA16EDBB1',
quality: '5263157894.7',
taker_gets_funded: {
currency: 'EUR',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '19.25393938854825'
},
taker_pays_funded: '101336523096'
}
];
module.exports.bookOffersResponse = function(options) {
options = options || {};
_.defaults(options, {
account_funds: '2010.027702881682',
other_account_funds: '24.06086596039299',
third_account_funds: '9071.40090264774',
fourth_account_funds: '7244.053477923128'
});
return {
offers: [
{
Account: addresses.ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C188F5B29EE1C14',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000006762',
PreviousTxnID: '5F08192C82CD3A598D29B51FCCDE29B6709EBCB454A3CD540C32F7A79EE7CB26',
PreviousTxnLgrSeq: 11558364,
Sequence: 1689777,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '79.39192374'
},
TakerPays: '5488380479',
index: 'D9F821C8687E0D0EDEFF05EBB53CFDC81C5F9C4C354DAACB11F6676B5E14AEF5',
owner_funds: options.account_funds,
quality: '69130211.4932226'
},
{
Account: addresses.OTHER_ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C18949C72B26C2A',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000001',
PreviousTxnID: '038ED9ACC10211A8F6768729F36B74729CECCD33057837E160131675B272E532',
PreviousTxnLgrSeq: 11558374,
Sequence: 931088,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '24.060765960393'
},
TakerPays: '1664716059',
index: '8845F212A8B53004A14C8C029FAF51B53266C66B49281A72F6A8F41CD92FDE99',
owner_funds: options.other_account_funds,
quality: '69187991.0116049',
taker_gets_funded: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '24.01284027983332'
},
taker_pays_funded: '1661400177'
},
{
Account: addresses.THIRD_ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C18949C764EA14E',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000009',
PreviousTxnID: '62B96C0E0D86827BCE59ABDCAD146CC0B09404FE5BC86E712FB6F4E473016C63',
PreviousTxnLgrSeq: 11558234,
Sequence: 617872,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '712.60995'
},
TakerPays: '49304051247',
index: '9E5C13908F67146AC35A711A17E5EB75771FDDA816C9532891DC90F29A6A4C10',
owner_funds: options.third_account_funds,
quality: '69187991.61729358'
},
{
Account: addresses.FOURTH_ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C18AA2E761B7EE6',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000511',
PreviousTxnID: 'F18AED5EC1E7529EF03AF23ADA85F7625AA308278BACE1851F336443AA3DAAEA',
PreviousTxnLgrSeq: 11558336,
Sequence: 662712,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '288.08'
},
TakerPays: '20000000000',
index: '606B3FC9199D5122F1DCC73EC1629E40C8A838D58AC67315A78D76699D960705',
owner_funds: options.fourth_account_funds,
quality: '69425159.67786726'
},
{
Account: addresses.ACCOUNT,
BookDirectory: '4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C18C3D9EF58005A',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000006762',
PreviousTxnID: 'E3A0240001B03E4F16C4BA6C2B0CB00C01413BE331ABE9E782B6A975DC936618',
PreviousTxnLgrSeq: 11558318,
Sequence: 1689755,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: '196.460002'
},
TakerPays: '13694716399',
index: '9A5D0AA37EA0889B876E9A3E552CACDB28BA5A3CD482A528992CD0CCFC09F6E8',
quality: '69707402.31897178'
}
]
};
};
module.exports.MODIFIED_NODES = [
{
ModifiedNode: {
FinalFields: {
Account: addresses.ACCOUNT,
BookDirectory: 'E2B91A0A170BCC2BEC5C44B492D9B672888D9267A900330F5C08953CAA35D973',
BookNode: '0000000000000000',
Flags: 131072,
OwnerNode: '0000000000000001',
Sequence: 538,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.TAKER_GETS_FINAL
},
TakerPays: module.exports.TAKER_PAYS_FINAL
},
LedgerEntryType: 'Offer',
LedgerIndex: module.exports.LEDGER_INDEX,
PreviousFields: {
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.TAKER_GETS
},
TakerPays: module.exports.TAKER_PAYS
},
PreviousTxnID: '5135DF8678727A70491DE512E5F0FE586E7C1E866492293B8898BF8191CFCAEB',
PreviousTxnLgrSeq: 11676651
}
},
{
ModifiedNode: {
FinalFields: {
Account: addresses.OTHER_ACCOUNT,
BookDirectory: 'E2B91A0A170BCC2BEC5C44B492D9B672888D9267A900330F5C08953CAA35D973',
BookNode: '0000000000000000',
Flags: 131072,
OwnerNode: '0000000000000001',
Sequence: 538,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.OTHER_TAKER_GETS_FINAL
},
TakerPays: module.exports.OTHER_TAKER_PAYS_FINAL
},
LedgerEntryType: 'Offer',
LedgerIndex: module.exports.OTHER_LEDGER_INDEX,
PreviousFields: {
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.OTHER_TAKER_GETS
},
TakerPays: module.exports.OTHER_TAKER_PAYS
},
PreviousTxnID: '5135DF8678727A70491DE512E5F0FE586E7C1E866492293B8898BF8191CFCAEB',
PreviousTxnLgrSeq: 11676651
}
}
];
module.exports.transactionWithRippleState = function(options) {
options = options || {};
_.defaults(options, {
issuer: addresses.ISSUER,
account: addresses.ACCOUNT,
balance: module.exports.FIAT_BALANCE
});
return {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: options.balance
},
Flags: 131072,
HighLimit: {
currency: 'USD',
issuer: options.issuer,
value: '100'
},
HighNode: '0000000000000000',
LowLimit: {
currency: 'USD',
issuer: options.account,
value: '0'
},
LowNode: '0000000000000000'
},
PreviousFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '0'
}
},
LedgerEntryType: 'RippleState',
LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959',
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
PreviousTxnLgrSeq: 343570
}
}]
})
};
};
module.exports.transactionWithAccountRoot = function(options) {
options = options || {};
_.defaults(options, {
account: addresses.ACCOUNT,
balance: module.exports.NATIVE_BALANCE,
previous_balance: module.exports.NATIVE_BALANCE_PREVIOUS
});
return {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Account: options.account,
Balance: options.balance,
Flags: 0,
OwnerCount: 1,
Sequence: 2
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05',
PreviousFields: {
Balance: options.previous_balance,
OwnerCount: 0,
Sequence: 1
},
PreviousTxnID: 'B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09',
PreviousTxnLgrSeq: 16154
}
}]
})
};
};
module.exports.transactionWithInvalidAccountRoot = function(options) {
options = options || {};
_.defaults(options, {
account: addresses.ACCOUNT,
balance: module.exports.NATIVE_BALANCE
});
return {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Account: options.account,
Balance: options.balance,
Flags: 0,
OwnerCount: 3,
Sequence: 188
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: 'B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A',
PreviousTxnID: 'E9E1988A0F061679E5D14DE77DB0163CE0BBDC00F29E396FFD1DA0366E7D8904',
PreviousTxnLgrSeq: 195455
}
}]
})
};
};
module.exports.transactionWithCreatedOffer = function(options) {
options = options || {};
_.defaults(options, {
account: addresses.ACCOUNT,
amount: '1.9951'
});
var meta = new Meta({
AffectedNodes: [
{
CreatedNode: {
LedgerEntryType: 'Offer',
LedgerIndex: 'AF3C702057C9C47DB9E809FD8C76CD22521012C5CC7AE95D914EC9E226F1D7E5',
NewFields: {
Account: options.account,
BookDirectory: '7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F211CEE1E0697A0',
Flags: 131072,
Sequence: 1404,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: options.amount
},
TakerPays: module.exports.TAKER_PAYS
}
}
}
]
});
meta.getAffectedBooks();
return {
mmeta: meta,
transaction: {
TransactionType: 'OfferCreate',
owner_funds: '2010.027702881682'
}
};
};
module.exports.transactionWithDeletedOffer = function(options) {
options = options || {};
_.defaults(options, {
transaction_type: 'OfferCreate'
});
var meta = new Meta({
AffectedNodes: [
{
DeletedNode: {
FinalFields: {
Account: addresses.ACCOUNT,
BookDirectory: '3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E570463F9CDB31517',
BookNode: '0000000000000000',
Expiration: 477086871,
Flags: 131072,
OwnerNode: '0000000000000979',
PreviousTxnID: 'DDD2AB60A2AA1690A6CB99B094BFD2E39A81AFF2AA91B5E4049D2C96A4BC8EBA',
PreviousTxnLgrSeq: 11674760,
Sequence: 85006,
TakerGets: {
currency: 'USD',
issuer: addresses.ISSUER,
value: module.exports.TAKER_GETS
},
TakerPays: module.exports.TAKER_PAYS
},
LedgerEntryType: 'Offer',
LedgerIndex: module.exports.LEDGER_INDEX
}
}
]
});
meta.getAffectedBooks();
return {
mmeta: meta,
transaction: {
TransactionType: options.transaction_type,
owner_funds: '2010.027702881682'
}
};
};
module.exports.transactionWithModifiedOffer = function() {
var meta = new Meta({
AffectedNodes: module.exports.MODIFIED_NODES.slice(0, 1)
});
meta.getAffectedBooks();
return {
mmeta: meta,
transaction: {
TransactionType: 'OfferCreate',
owner_funds: '2010.027702881682'
}
};
};
module.exports.transactionWithModifiedOffers = function() {
var meta = new Meta({
AffectedNodes: module.exports.MODIFIED_NODES
});
meta.getAffectedBooks();
return {
mmeta: meta,
transaction: {
TransactionType: 'OfferCreate',
owner_funds: '2010.027702881682'
}
};
};
module.exports.transactionWithNoNodes = function() {
var meta = new Meta({
AffectedNodes: []
});
meta.getAffectedBooks();
return {
mmeta: meta,
transaction: {
TransactionType: 'OfferCreate',
owner_funds: '2010.027702881682'
}
};
};
module.exports.accountInfoResponse = function(options) {
options = options || {};
_.defaults(options, {
account: addresses.ISSUER
});
return {
account_data: {
Account: options.account,
Balance: '6156166959471',
Domain: '6269747374616D702E6E6574',
EmailHash: '5B33B93C7FFE384D53450FC666BB11FB',
Flags: 131072,
LedgerEntryType: 'AccountRoot',
OwnerCount: 0,
PreviousTxnID: '6A7D0AB36CBA6884FDC398254BC67DE7E0B4887E9B0252568391102FBB854C09',
PreviousTxnLgrSeq: 8344426,
Sequence: 561,
TransferRate: module.exports.TRANSFER_RATE,
index: 'B7D526FDDF9E3B3F95C3DC97C353065B0482302500BBB8051A5C090B596C6133',
urlgravatar: 'http:www.gravatar.com/avatar/5b33b93c7ffe384d53450fc666bb11fb'
}
};
};

294
test/fixtures/transactionmanager.json vendored Normal file
View 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"
}
}

View File

@@ -1,14 +1,16 @@
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
/* eslint-disable max-len */
'use strict';
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
var Message = require('ripple-lib').Message;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
describe('Message', function(){
describe('Message', function() {
describe('signMessage', function(){
describe('signMessage', function() {
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call signHash', function(){
it('should prepend the MAGIC_BYTES, call the hashFunction, and then call signHash', function() {
var normal_signHash = Message.signHash;
@@ -17,7 +19,7 @@ describe('Message', function(){
var signHash_called = false;
Message.signHash = function(hash) {
signHash_called = true;
assert.deepEqual(hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + message_text));
assert.deepEqual(hash, Message.hashFunction(Message.MAGIC_BYTES + message_text));
};
Message.signMessage(message_text);
@@ -29,13 +31,13 @@ describe('Message', function(){
});
describe('signHash', function(){
describe('signHash', function() {
it('should accept the hash as either a hex string or a bitArray', function(){
it('should accept the hash as either a hex string or a bitArray', function() {
var normal_random = sjcl.random.randomWords;
sjcl.random.randomWords = function(num_words){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -56,11 +58,11 @@ describe('Message', function(){
});
it('should accept the secret as a string or scjl.ecc.ecdsa.secretKey object', function(){
it('should accept the secret as a string or scjl.ecc.ecdsa.secretKey object', function() {
var normal_random = sjcl.random.randomWords;
sjcl.random.randomWords = function(num_words){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -81,7 +83,7 @@ describe('Message', function(){
});
it('should throw an error if given an invalid secret key', function(){
it('should throw an error if given an invalid secret key', function() {
// Annoyingly non hex can be fed to the BigInteger(s, 16) constructor and
// it will parse as a number. Before the commit of this comment, this test
// involved a fixture of 32 chars, which was assumed to be hex. The test
@@ -95,41 +97,41 @@ describe('Message', function(){
var secret_string = 'sbadsafRpB5euNL52PZPTSqrE9gvuFwTC';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){
assert.throws(function() {
Message.signHash(hash, secret_string);
}, /Cannot\ generate\ keys\ from\ invalid\ seed/);
});
it('should throw an error if the parameters are reversed', function(){
it('should throw an error if the parameters are reversed', function() {
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){
assert.throws(function() {
Message.signHash(secret_string, hash);
}, Error);
assert.throws(function(){
assert.throws(function() {
Message.signHash(secret_string, sjcl.codec.hex.toBits(hash));
}, Error);
assert.throws(function(){
assert.throws(function() {
Message.signHash(Seed.from_json(secret_string).get_key()._secret, hash);
}, Error);
assert.throws(function(){
assert.throws(function() {
Message.signHash(Seed.from_json(secret_string).get_key()._secret, sjcl.codec.hex.toBits(hash));
}, Error);
});
it('should produce a base64-encoded signature', function(){
it('should produce a base64-encoded signature', function() {
var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
var normal_random = sjcl.random.randomWords;
sjcl.random.randomWords = function(num_words){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -150,9 +152,9 @@ describe('Message', function(){
});
describe('verifyMessageSignature', function(){
describe('verifyMessageSignature', function() {
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call verifyHashSignature', function(){
it('should prepend the MAGIC_BYTES, call the hashFunction, and then call verifyHashSignature', function() {
var normal_verifyHashSignature = Message.verifyHashSignature;
@@ -165,13 +167,13 @@ describe('Message', function(){
Message.verifyHashSignature = function(vhs_data, remote, callback) {
verifyHashSignature_called = true;
assert.deepEqual(vhs_data.hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + data.message));
assert.deepEqual(vhs_data.hash, Message.hashFunction(Message.MAGIC_BYTES + data.message));
assert.strictEqual(vhs_data.signature, data.signature);
callback();
};
Message.verifyMessageSignature(data, {}, function(err){
Message.verifyMessageSignature(data, {}, function(err) {
assert(!err);
});
assert(verifyHashSignature_called);
@@ -182,9 +184,9 @@ describe('Message', function(){
});
describe('verifyHashSignature', function(){
describe('verifyHashSignature', function() {
it('should throw an error if a callback function is not supplied', function(){
it('should throw an error if a callback function is not supplied', function() {
var data = {
message: 'Hello world!',
@@ -193,15 +195,12 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
assert.throws(function(){
assert.throws(function() {
Message.verifyHashSignature(data);
}, /(?=.*callback\ function).*/);
});
it('should respond with an error if the hash is missing or invalid', function(done){
it('should respond with an error if the hash is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -209,18 +208,17 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err) {
assert(/hash/i.test(err.message));
done();
});
});
it('should respond with an error if the account is missing or invalid', function(done){
it('should respond with an error if the account is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -228,18 +226,17 @@ describe('Message', function(){
signature: 'AAAAHOUJQzG/7BO82fGNt1TNE+GGVXKuQQ0N2nTO+iJETE69PiHnaAkkOzovM177OosxbKjpt3KvwuJflgUB2YGvgjk='
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err) {
assert(/account|address/i.test(err.message));
done();
});
});
it('should respond with an error if the signature is missing or invalid', function(done){
it('should respond with an error if the signature is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -247,18 +244,17 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err) {
assert(/signature/i.test(err.message));
done();
});
});
it('should respond true if the signature is valid and corresponds to an active public key for the account', function(done){
it('should respond true if the signature is valid and corresponds to an active public key for the account', function(done) {
var data = {
message: 'Hello world!',
@@ -267,18 +263,17 @@ describe('Message', function(){
signature: 'AAAAHMIPCQGLgdnpX1Ccv1wHb56H4NggxIM6U08Qkb9mUjN2Vn9pZ3CHvq1yWLBi6NqpW+7kedLnmfu4VG2+y43p4Xs='
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
"Flags": 1114112,
"LedgerEntryType": "AccountRoot",
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
'account_data': {
'Account': 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
'Flags': 1114112,
'LedgerEntryType': 'AccountRoot',
'RegularKey': 'rHq2wyUtLkAad3vURUk33q9gozd97skhSf'
}
});
} else {
@@ -286,7 +281,7 @@ describe('Message', function(){
}
};
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err, valid) {
assert(!err);
assert(valid);
done();
@@ -294,7 +289,7 @@ describe('Message', function(){
});
it('should respond false if a key can be recovered from the signature but it does not correspond to an active public key', function(done){
it('should respond false if a key can be recovered from the signature but it does not correspond to an active public key', function(done) {
// Signature created by disabled master key
var data = {
@@ -304,18 +299,17 @@ describe('Message', function(){
signature: 'AAAAG+dB/rAjZ5m8eQ/opcqQOJsFbKxOu9jq9KrOAlNO4OdcBDXyCBlkZqS9Xr8oZI2uh0boVsgYOS3pOLJz+Dh3Otk='
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
"Flags": 1114112,
"LedgerEntryType": "AccountRoot",
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
'account_data': {
'Account': 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
'Flags': 1114112,
'LedgerEntryType': 'AccountRoot',
'RegularKey': 'rHq2wyUtLkAad3vURUk33q9gozd97skhSf'
}
});
} else {
@@ -323,7 +317,7 @@ describe('Message', function(){
}
};
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err, valid) {
assert(!err);
assert(!valid);
done();

View File

@@ -33,5 +33,4 @@ describe('Meta', function() {
assert.strictEqual(meta.nodes[idx], curr);
}, []);
});
});
});

View File

@@ -0,0 +1,753 @@
/*eslint-disable max-len */
'use strict';
var _ = require('lodash');
var assert = require('assert-diff');
var Remote = require('ripple-lib').Remote;
var Currency = require('ripple-lib').Currency;
var addresses = require('./fixtures/addresses');
var fixtures = require('./fixtures/orderbook');
describe('OrderBook Autobridging', function() {
this.timeout(0);
it('Initialize IOU/IOU', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
assert.deepEqual(book._legOneBook._currencyGets.to_hex(), Currency.from_json('XRP').to_hex());
assert.deepEqual(book._legOneBook._currencyPays.to_hex(), Currency.from_json('USD').to_hex());
assert.deepEqual(book._legTwoBook._currencyGets.to_hex(), Currency.from_json('EUR').to_hex());
assert.deepEqual(book._legTwoBook._currencyPays.to_hex(), Currency.from_json('XRP').to_hex());
});
it('Compute autobridged offers', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert.strictEqual(book._offersAutobridged[0].taker_gets_funded, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg one partially funded', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg two partially funded', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].owner_funds = '10';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg two transfer rate', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].owner_funds = '10';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - taker funds < leg two in', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '33461561812';
legTwoOffers[0].owner_funds = '360';
legTwoOffers[0].TakerGets.value = '170.7639524223001';
legTwoOffers[0].TakerPays = '49439476610';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg one partially funded - owners equal', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
legTwoOffers[0].Account = legOneOffers[0].Account;
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legOneOffers[0].owner_funds = '2105863129';
legTwoOffers[0].Account = legOneOffers[0].Account;
legTwoOffers[0].owner_funds = '360';
legTwoOffers[0].TakerGets.value = '170.7639524223001';
legTwoOffers[0].TakerPays = '49439476610';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - leg one consumes leg two fully', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
assert(book._offersAutobridged[1].autobridged);
});
it('Compute autobridged offers - leg two consumes first leg one offer fully', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 2));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 1));
legTwoOffers[0].TakerGets.value = '170.7639524223001';
legTwoOffers[0].TakerPays = '49439476610';
legTwoOffers[0].owner_funds = '364.0299530003982';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '62.0957179050155');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838');
assert(book._offersAutobridged[1].autobridged);
});
it('Compute autobridged offers - owners equal', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
legOneOffers[0].owner_funds = '2105863129';
legTwoOffers[1].owner_funds = '19.32660005780981';
legTwoOffers[0].Account = legOneOffers[0].Account;
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '0.4001139945128008');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184');
assert(book._offersAutobridged[1].autobridged);
});
it('Compute autobridged offers - owners equal - leg one overfunded', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1002000000;
var legOneOffers = _.cloneDeep(fixtures.LEG_ONE_OFFERS.slice(0, 1));
var legTwoOffers = _.cloneDeep(fixtures.LEG_TWO_OFFERS.slice(0, 2));
legOneOffers[0].owner_funds = '41461561812';
legTwoOffers[0].Account = legOneOffers[0].Account;
legTwoOffers[1].owner_funds = '30';
book._legOneBook.setOffers(legOneOffers);
book._legTwoBook.setOffers(legTwoOffers);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
assert(book._offersAutobridged[1].autobridged);
});
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: '75',
TakerPays: {
value: '50',
issuer: addresses.ISSUER,
currency: 'USD'
},
owner_funds: '50',
quality: '1'
}
]);
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '90',
owner_funds: '150',
quality: '1'
}
]);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
assert(book._offersAutobridged[0].autobridged);
});
it('Compute autobridged offers - update funded amount', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: '100',
TakerPays: {
value: '100',
issuer: addresses.ISSUER,
currency: 'USD'
},
owner_funds: '50',
quality: '1'
},
{
Account: addresses.ACCOUNT,
TakerGets: '50',
TakerPays: {
value: '100',
issuer: addresses.ISSUER,
currency: 'USD'
},
quality: '2'
}
]);
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '90',
owner_funds: '150',
quality: '1'
},
{
Account: addresses.OTHER_ACCOUNT,
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '60',
owner_funds: '70',
quality: '2'
}
]);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 3);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
assert(book._offersAutobridged[1].autobridged);
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '20');
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80');
assert(book._offersAutobridged[2].autobridged);
});
it('Compute autobridged offers - update funded amount - owners equal', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: '100',
TakerPays: {
value: '100',
issuer: addresses.ISSUER,
currency: 'USD'
},
owner_funds: '50',
quality: '1'
},
{
Account: addresses.ACCOUNT,
TakerGets: '20',
TakerPays: {
value: '100',
issuer: addresses.ISSUER,
currency: 'USD'
},
quality: '5'
}
]);
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '90',
owner_funds: '150',
quality: '1'
},
{
Account: addresses.OTHER_ACCOUNT,
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '60',
owner_funds: '70',
quality: '2'
}
]);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 3);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
assert(book._offersAutobridged[1].autobridged);
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '10');
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
assert(book._offersAutobridged[2].autobridged);
});
it('Compute autobridged offers - update funded amount - first two owners equal', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: '100',
TakerPays: {
value: '100',
issuer: addresses.ISSUER,
currency: 'USD'
},
owner_funds: '50',
quality: '1'
},
{
Account: addresses.ACCOUNT,
TakerGets: '100',
TakerPays: {
value: '200',
issuer: addresses.ISSUER,
currency: 'USD'
},
quality: '2'
}
]);
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '90',
owner_funds: '150',
quality: '1'
},
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '30',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '60',
quality: '2'
},
{
Account: addresses.OTHER_ACCOUNT,
TakerGets: {
value: '20',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '40',
owner_funds: '70',
quality: '2'
}
]);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 4);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
assert(book._offersAutobridged[0].autobridged);
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
assert(book._offersAutobridged[1].autobridged);
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '25');
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
assert(book._offersAutobridged[2].autobridged);
assert.strictEqual(book._offersAutobridged[3].TakerGets.value, '20');
assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80');
assert(book._offersAutobridged[3].autobridged);
});
it('Compute autobridged offers - unfunded offer - owners equal', function() {
var book = new Remote().createOrderBook({
currency_gets: 'EUR',
issuer_gets: addresses.ISSUER,
currency_pays: 'USD',
issuer_pays: addresses.ISSUER
});
book._issuerTransferRate = 1000000000;
book._legOneBook._issuerTransferRate = 1000000000;
book._legTwoBook._issuerTransferRate = 1000000000;
book._legOneBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: '75',
TakerPays: {
value: '75',
issuer: addresses.ISSUER,
currency: 'USD'
},
owner_funds: '0',
quality: '1'
}
]);
book._legTwoBook.setOffers([
{
Account: addresses.ACCOUNT,
TakerGets: {
value: '90',
issuer: addresses.ISSUER,
currency: 'EUR'
},
TakerPays: '90',
owner_funds: '150',
quality: '1'
}
]);
book.computeAutobridgedOffers();
assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
assert(book._offersAutobridged[0].autobridged);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
});

View File

@@ -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',

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,637 @@
'use strict';
var ws = require('ws');
var lodash = require('lodash');
var assert = require('assert-diff');
var sjcl = require('ripple-lib').sjcl;
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;
before(function() {
sjcl.random.addEntropy(
'3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BD', 256);
});
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) {
// empty
}
};
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();
});
});
});

View File

@@ -1,9 +1,11 @@
var assert = require('assert');
var Amount = require('ripple-lib').Amount;
var Transaction = require('ripple-lib').Transaction;
/* eslint-disable max-len */
'use strict';
var assert = require('assert');
var Transaction = require('ripple-lib').Transaction;
var TransactionQueue = require('ripple-lib').TransactionQueue;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var sjcl = require('ripple-lib').sjcl;
var transactionResult = {
engine_result: 'tesSUCCESS',
@@ -17,24 +19,30 @@ var transactionResult = {
metadata: {
AffectedNodes: [ ],
TransactionIndex: 0,
TransactionResult: 'tesSUCCESS' },
tx_json: {
Account: 'rHPotLj3CNKaP4bQANcecEuT8hai3VpxfB',
Amount: '1000000',
Destination: 'rYtn3D1VGQyf1MTqcwLDepUKm22YEGXGJA',
Fee: '10',
Flags: 0,
LastLedgerSequence: 7106151,
Sequence: 2973,
SigningPubKey: '0306E9F38DF11402953A5B030C1AE8A88C47E348170C3B8EC6C8D775E797168462',
TransactionType: 'Payment',
TxnSignature: '3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BDEE06D51F530220408A3AA26988ABF18E16BE77B016F25018A2AA7C99FFE723FC8598471357DBCF',
date: 455660500,
hash: '61D60378AB70ACE630B20A81B50708A3DB5E7CEE35914292FF3761913DA61DEA'
}
TransactionResult: 'tesSUCCESS'
},
tx_json: {
Account: 'rHPotLj3CNKaP4bQANcecEuT8hai3VpxfB',
Amount: '1000000',
Destination: 'rYtn3D1VGQyf1MTqcwLDepUKm22YEGXGJA',
Fee: '10',
Flags: 0,
LastLedgerSequence: 7106151,
Sequence: 2973,
SigningPubKey: '0306E9F38DF11402953A5B030C1AE8A88C47E348170C3B8EC6C8D775E797168462',
TransactionType: 'Payment',
TxnSignature: '3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BDEE06D51F530220408A3AA26988ABF18E16BE77B016F25018A2AA7C99FFE723FC8598471357DBCF',
date: 455660500,
hash: '61D60378AB70ACE630B20A81B50708A3DB5E7CEE35914292FF3761913DA61DEA'
}
};
describe('Transaction', function() {
before(function() {
sjcl.random.addEntropy(
'3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BD', 256);
});
it('Success listener', function(done) {
var transaction = new Transaction();
@@ -189,7 +197,7 @@ describe('Transaction', function() {
assert.strictEqual(transaction._accountSecret('rpzT237Ctpaa58KieifoK8RyBmmRwEcfhK'), 'shY1njzHAXp8Qt3bpxYW6RpoZtMKP');
assert.strictEqual(transaction._accountSecret('rpdxPs9CR93eLAc5DTvAgv4S9XJ1CzKj1a'), 'ssboTJezioTq8obyvDU9tVo95NGGQ');
assert.strictEqual(transaction._accountSecret('rExistNot'), void(0));
assert.strictEqual(transaction._accountSecret('rExistNot'), undefined);
});
it('Get fee units', function() {
@@ -220,7 +228,7 @@ describe('Transaction', function() {
s5._connected = true;
s5._load_factor = 256 * 7;
remote._servers = [ s2, s3, s1, s4 ];
remote._servers = [s2, s3, s1, s4];
assert.strictEqual(s1._computeFee(10), '12');
assert.strictEqual(s2._computeFee(10), '48');
@@ -235,7 +243,7 @@ describe('Transaction', function() {
it('Compute fee, no remote', function() {
var transaction = new Transaction();
assert.strictEqual(transaction._computeFee(10), void(0));
assert.strictEqual(transaction._computeFee(10), undefined);
});
it('Compute fee - no connected server', function() {
@@ -252,7 +260,7 @@ describe('Transaction', function() {
s3._connected = false;
s3._load_factor = 256 * 8;
remote._servers = [ s1, s2, s3 ];
remote._servers = [s1, s2, s3];
assert.strictEqual(s1._computeFee(10), '12');
assert.strictEqual(s2._computeFee(10), '48');
@@ -260,7 +268,7 @@ describe('Transaction', function() {
var transaction = new Transaction(remote);
assert.strictEqual(transaction._computeFee(), void(0));
assert.strictEqual(transaction._computeFee(), undefined);
});
it('Compute fee - one connected server', function() {
@@ -277,7 +285,7 @@ describe('Transaction', function() {
s3._connected = true;
s3._load_factor = 256 * 8;
remote._servers = [ s1, s2, s3 ];
remote._servers = [s1, s2, s3];
assert.strictEqual(s1._computeFee(10), '12');
assert.strictEqual(s2._computeFee(10), '48');
@@ -307,7 +315,7 @@ describe('Transaction', function() {
s4._connected = true;
s4._load_factor = 256 * 16;
remote._servers = [ s1, s2, s3, s4 ];
remote._servers = [s1, s2, s3, s4];
assert.strictEqual(s1._computeFee(10), '12');
assert.strictEqual(s2._computeFee(10), '48');
@@ -319,7 +327,7 @@ describe('Transaction', function() {
transaction.tx_json.Sequence = 1;
var src = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh';
var dst = 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v';
transaction.payment(src, dst, '100');
remote.set_secret(src, 'masterpassphrase');
@@ -347,7 +355,7 @@ describe('Transaction', function() {
s4._connected = true;
s4._load_factor = 256 * 16;
remote._servers = [ s1, s2, s3, s4 ];
remote._servers = [s1, s2, s3, s4];
assert.strictEqual(s1._computeFee(10), '12');
assert.strictEqual(s2._computeFee(10), '48');
@@ -366,7 +374,7 @@ describe('Transaction', function() {
var s1 = new Server(remote, 'wss://s-west.ripple.com:443');
s1._connected = true;
remote._servers = [ s1 ];
remote._servers = [s1];
remote.trusted = true;
remote.local_signing = true;
@@ -427,7 +435,7 @@ describe('Transaction', function() {
remote.trusted = true;
remote.local_signing = true;
transaction.SigningPubKey = void(0);
transaction.SigningPubKey = undefined;
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoijX';
@@ -465,7 +473,7 @@ describe('Transaction', function() {
var s1 = new Server(remote, 'wss://s-west.ripple.com:443');
s1._connected = true;
remote._servers = [ s1 ];
remote._servers = [s1];
remote.trusted = true;
remote.local_signing = true;
@@ -473,7 +481,7 @@ describe('Transaction', function() {
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoij';
assert.strictEqual(transaction.tx_json.Fee, void(0));
assert.strictEqual(transaction.tx_json.Fee, undefined);
assert(transaction.complete());
@@ -483,13 +491,13 @@ describe('Transaction', function() {
});
it('Complete transaction - compute fee exceeds max fee', function(done) {
var remote = new Remote({ max_fee: 10 });
var remote = new Remote({max_fee: 10});
var s1 = new Server(remote, 'wss://s-west.ripple.com:443');
s1._connected = true;
s1._load_factor = 256 * 16;
remote._servers = [ s1 ];
remote._servers = [s1];
remote.trusted = true;
remote.local_signing = true;
@@ -513,7 +521,7 @@ describe('Transaction', function() {
s1._connected = true;
s1._load_factor = 256;
remote._servers = [ s1 ];
remote._servers = [s1];
remote.trusted = true;
remote.local_signing = true;
@@ -539,7 +547,7 @@ describe('Transaction', function() {
transaction.tx_json.Sequence = 1;
transaction.tx_json.TransactionType = 'AccountSet';
assert.strictEqual(transaction.signingHash(), 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE')
assert.strictEqual(transaction.signingHash(), 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
done();
});
@@ -554,7 +562,7 @@ describe('Transaction', function() {
transaction.tx_json.Sequence = 1;
transaction.tx_json.TransactionType = 'AccountSet';
assert.strictEqual(transaction.hash(), '1A860FC46D1DD9200560C64002418A4E8BBDE939957AC82D7B14D80A1C0E2EB5')
assert.strictEqual(transaction.hash(), '1A860FC46D1DD9200560C64002418A4E8BBDE939957AC82D7B14D80A1C0E2EB5');
done();
});
@@ -569,7 +577,7 @@ describe('Transaction', function() {
transaction.tx_json.Sequence = 1;
transaction.tx_json.TransactionType = 'AccountSet';
assert.strictEqual(transaction.hash('HASH_TX_SIGN'), 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE')
assert.strictEqual(transaction.hash('HASH_TX_SIGN'), 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
assert.strictEqual(transaction.hash('HASH_TX_SIGN_TESTNET'), '9FE7D27FC5B9891076B66591F99A683E01E0912986A629235459A3BD1961F341');
done();
@@ -594,51 +602,51 @@ describe('Transaction', function() {
it('Get hash - complex transaction', function() {
var input_json = {
Account : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount : {
currency : 'LTC',
issuer : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value : '9.985'
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: {
currency: 'LTC',
issuer: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value: '9.985'
},
Destination : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee : '15',
Flags : 0,
Paths : [
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee: '15',
Flags: 0,
Paths: [
[
{
account : 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
currency : 'USD',
issuer : 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
type : 49,
type_hex : '0000000000000031'
account: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
currency: 'USD',
issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
type: 49,
type_hex: '0000000000000031'
},
{
currency : 'LTC',
issuer : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type : 48,
type_hex : '0000000000000030'
currency: 'LTC',
issuer: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type: 48,
type_hex: '0000000000000030'
},
{
account : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
currency : 'LTC',
issuer : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type : 49,
type_hex : '0000000000000031'
account: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
currency: 'LTC',
issuer: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type: 49,
type_hex: '0000000000000031'
}
]
],
SendMax : {
currency : 'USD',
issuer : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value : '30.30993068'
SendMax: {
currency: 'USD',
issuer: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value: '30.30993068'
},
Sequence : 415,
SigningPubKey : '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A',
TransactionType : 'Payment',
TxnSignature : '304602210096C2F385530587DE573936CA51CB86B801A28F777C944E268212BE7341440B7F022100EBF0508A9145A56CDA7FAF314DF3BBE51C6EE450BA7E74D88516891A3608644E'
Sequence: 415,
SigningPubKey: '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A',
TransactionType: 'Payment',
TxnSignature: '304602210096C2F385530587DE573936CA51CB86B801A28F777C944E268212BE7341440B7F022100EBF0508A9145A56CDA7FAF314DF3BBE51C6EE450BA7E74D88516891A3608644E'
};
var expected_hash = "87366146D381AD971B97DD41CFAC1AE4670B0E996AB574B0CE18CE6467811868";
var expected_hash = '87366146D381AD971B97DD41CFAC1AE4670B0E996AB574B0CE18CE6467811868';
var transaction = Transaction.from_json(input_json);
assert.deepEqual(transaction.hash(), expected_hash);
@@ -723,48 +731,48 @@ describe('Transaction', function() {
it('Serialize transaction', function() {
var input_json = {
Account : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount : {
currency : 'LTC',
issuer : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value : '9.985'
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: {
currency: 'LTC',
issuer: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value: '9.985'
},
Destination : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee : '15',
Flags : 0,
Paths : [
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee: '15',
Flags: 0,
Paths: [
[
{
account : 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
currency : 'USD',
issuer : 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
type : 49,
type_hex : '0000000000000031'
account: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
currency: 'USD',
issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
type: 49,
type_hex: '0000000000000031'
},
{
currency : 'LTC',
issuer : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type : 48,
type_hex : '0000000000000030'
currency: 'LTC',
issuer: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type: 48,
type_hex: '0000000000000030'
},
{
account : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
currency : 'LTC',
issuer : 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type : 49,
type_hex : '0000000000000031'
account: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
currency: 'LTC',
issuer: 'rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX',
type: 49,
type_hex: '0000000000000031'
}
]
],
SendMax : {
currency : 'USD',
issuer : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value : '30.30993068'
SendMax: {
currency: 'USD',
issuer: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
value: '30.30993068'
},
Sequence : 415,
SigningPubKey : '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A',
TransactionType : 'Payment',
TxnSignature : '304602210096C2F385530587DE573936CA51CB86B801A28F777C944E268212BE7341440B7F022100EBF0508A9145A56CDA7FAF314DF3BBE51C6EE450BA7E74D88516891A3608644E'
Sequence: 415,
SigningPubKey: '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A',
TransactionType: 'Payment',
TxnSignature: '304602210096C2F385530587DE573936CA51CB86B801A28F777C944E268212BE7341440B7F022100EBF0508A9145A56CDA7FAF314DF3BBE51C6EE450BA7E74D88516891A3608644E'
};
var expected_hex = '1200002200000000240000019F61D4A3794DFA1510000000000000000000000000004C54430000000000EF7ED76B77750D79EC92A59389952E0E8054407668400000000000000F69D4CAC4AC112283000000000000000000000000005553440000000000EF7ED76B77750D79EC92A59389952E0E80544076732102854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A7448304602210096C2F385530587DE573936CA51CB86B801A28F777C944E268212BE7341440B7F022100EBF0508A9145A56CDA7FAF314DF3BBE51C6EE450BA7E74D88516891A3608644E8114EF7ED76B77750D79EC92A59389952E0E805440768314EF7ED76B77750D79EC92A59389952E0E80544076011231DD39C650A96EDA48334E70CC4A85B8B2E8502CD30000000000000000000000005553440000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD3300000000000000000000000004C5443000000000047DA9E2E00ECF224A52329793F1BB20FB1B5EA643147DA9E2E00ECF224A52329793F1BB20FB1B5EA640000000000000000000000004C5443000000000047DA9E2E00ECF224A52329793F1BB20FB1B5EA6400';
@@ -810,8 +818,8 @@ describe('Transaction', function() {
assert.deepEqual(
transaction.submittedIDs,
[ 'F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE' ]
['F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE']
);
done();
@@ -822,7 +830,7 @@ describe('Transaction', function() {
assert.deepEqual(transaction.findId({
F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE: transaction
}), void(0));
}), undefined);
transaction.addId('F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
@@ -832,7 +840,7 @@ describe('Transaction', function() {
assert.strictEqual(transaction.findId({
Z1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE: transaction
}), void(0));
}), undefined);
done();
});
@@ -846,7 +854,7 @@ describe('Transaction', function() {
it('Set DestinationTag', function() {
var transaction = new Transaction();
transaction.destinationTag('tag');
assert.strictEqual(transaction.tx_json.DestinationTag, void(0));
assert.strictEqual(transaction.tx_json.DestinationTag, undefined);
transaction.destinationTag(1);
assert.strictEqual(transaction.tx_json.DestinationTag, 1);
});
@@ -855,7 +863,7 @@ describe('Transaction', function() {
var transaction = new Transaction();
transaction.invoiceID(1);
assert.strictEqual(transaction.tx_json.InvoiceID, void(0));
assert.strictEqual(transaction.tx_json.InvoiceID, undefined);
transaction.invoiceID('DEADBEEF');
assert.strictEqual(transaction.tx_json.InvoiceID, 'DEADBEEF00000000000000000000000000000000000000000000000000000000');
@@ -868,7 +876,7 @@ describe('Transaction', function() {
var transaction = new Transaction();
transaction.clientID(1);
assert.strictEqual(transaction._clientID, void(0));
assert.strictEqual(transaction._clientID, undefined);
transaction.clientID('DEADBEEF');
assert.strictEqual(transaction._clientID, 'DEADBEEF');
@@ -878,11 +886,11 @@ describe('Transaction', function() {
var transaction = new Transaction();
transaction.lastLedger('a');
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
assert.strictEqual(transaction.tx_json.LastLedgerSequence, undefined);
assert(!transaction._setLastLedger);
transaction.lastLedger(NaN);
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
assert.strictEqual(transaction.tx_json.LastLedgerSequence, undefined);
assert(!transaction._setLastLedger);
transaction.lastLedger(12);
@@ -922,8 +930,6 @@ describe('Transaction', function() {
});
it('Rewrite transaction path', function() {
var transaction = new Transaction();
var path = [
{
account: 'rP51ycDJw5ZhgvdKiRjBYZKYjsyoCcHmnY',
@@ -965,7 +971,7 @@ describe('Transaction', function() {
it('Rewrite transaction path - invalid path', function() {
assert.throws(function() {
assert.strictEqual(Transaction._rewritePath(1), void(0));
assert.strictEqual(Transaction._rewritePath(1), undefined);
});
});
@@ -974,7 +980,7 @@ describe('Transaction', function() {
transaction.pathAdd(1);
assert.strictEqual(transaction.tx_json.Paths, void(0));
assert.strictEqual(transaction.tx_json.Paths, undefined);
var path = [
{
@@ -1025,7 +1031,7 @@ describe('Transaction', function() {
transaction.paths(1);
assert.strictEqual(transaction.tx_json.Paths, void(0));
assert.strictEqual(transaction.tx_json.Paths, undefined);
transaction.paths([
[{
@@ -1075,7 +1081,7 @@ describe('Transaction', function() {
it('Set SourceTag', function() {
var transaction = new Transaction();
transaction.sourceTag('tag');
assert.strictEqual(transaction.tx_json.SourceTag, void(0));
assert.strictEqual(transaction.tx_json.SourceTag, undefined);
transaction.sourceTag(1);
assert.strictEqual(transaction.tx_json.SourceTag, 1);
});
@@ -1083,7 +1089,7 @@ describe('Transaction', function() {
it('Set TransferRate', function() {
var transaction = new Transaction();
transaction.transferRate(1);
assert.strictEqual(transaction.tx_json.TransferRate, void(0));
assert.strictEqual(transaction.tx_json.TransferRate, undefined);
transaction.transferRate(1.5 * 1e9);
assert.strictEqual(transaction.tx_json.TransferRate, 1.5 * 1e9);
});
@@ -1094,33 +1100,33 @@ describe('Transaction', function() {
transaction.setFlags();
assert.strictEqual(transaction.tx_json.Flags, 0);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags(Transaction.flags.Payment.PartialPayment);
assert.strictEqual(transaction.tx_json.Flags, 131072);
var transaction2 = new Transaction();
transaction2.tx_json.TransactionType = 'Payment';
transaction2.setFlags(Transaction.flags.Payment.PartialPayment);
assert.strictEqual(transaction2.tx_json.Flags, 131072);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 65536);
var transaction3 = new Transaction();
transaction3.tx_json.TransactionType = 'Payment';
transaction3.setFlags('NoRippleDirect');
assert.strictEqual(transaction3.tx_json.Flags, 65536);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('PartialPayment', 'NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 196608);
var transaction4 = new Transaction();
transaction4.tx_json.TransactionType = 'Payment';
transaction4.setFlags('PartialPayment', 'NoRippleDirect');
assert.strictEqual(transaction4.tx_json.Flags, 196608);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags([ 'LimitQuality', 'PartialPayment' ]);
assert.strictEqual(transaction.tx_json.Flags, 393216);
var transaction5 = new Transaction();
transaction5.tx_json.TransactionType = 'Payment';
transaction5.setFlags(['LimitQuality', 'PartialPayment']);
assert.strictEqual(transaction5.tx_json.Flags, 393216);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.once('error', function(err) {
var transaction6 = new Transaction();
transaction6.tx_json.TransactionType = 'Payment';
transaction6.once('error', function(err) {
assert.strictEqual(err.result, 'tejInvalidFlag');
done();
});
transaction.setFlags('asdf');
transaction6.setFlags('asdf');
});
it('Add Memo', function() {
@@ -1179,10 +1185,10 @@ describe('Transaction', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', void(0), 'testvalue');
transaction.addMemo('testkey2', void(0), 'testvalue2');
transaction.addMemo('testkey', undefined, 'testvalue');
transaction.addMemo('testkey2', undefined, 'testvalue2');
transaction.addMemo('testkey3', 'text/html');
transaction.addMemo(void(0), void(0), 'testvalue4');
transaction.addMemo(undefined, undefined, 'testvalue4');
transaction.addMemo('testkey4', 'text/html', '<html>');
var expected = [
@@ -1244,7 +1250,7 @@ describe('Transaction', function() {
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 1);
transaction.addMemo(undefined, 1);
}, /^Error: MemoFormat must be a string$/);
});
@@ -1253,7 +1259,7 @@ describe('Transaction', function() {
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 'России');
transaction.addMemo(undefined, 'России');
}, /^Error: MemoFormat must be valid ASCII$/);
});
@@ -1261,7 +1267,7 @@ describe('Transaction', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo({memoData:'some_string'});
transaction.addMemo({memoData: 'some_string'});
assert.deepEqual(transaction.tx_json.Memos, [
{
@@ -1360,7 +1366,7 @@ describe('Transaction', function() {
it('Construct AccountSet transaction - invalid account', function() {
assert.throws(function() {
var transaction = new Transaction().accountSet('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
new Transaction().accountSet('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
});
});
@@ -1393,7 +1399,7 @@ describe('Transaction', function() {
it('Construct OfferCancel transaction - invalid account', function() {
assert.throws(function() {
var transaction = new Transaction().offerCancel('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 1);
new Transaction().offerCancel('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 1);
});
});
@@ -1595,7 +1601,7 @@ describe('Transaction', function() {
it('Construct Payment transaction - invalid account', function() {
assert.throws(function() {
var transaction = new Transaction().payment(
new Transaction().payment(
'xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
'1/USD/r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
@@ -1605,7 +1611,7 @@ describe('Transaction', function() {
it('Construct Payment transaction - invalid destination', function() {
assert.throws(function() {
var transaction = new Transaction().payment(
new Transaction().payment(
'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe',
'1/USD/r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe'
@@ -1674,7 +1680,7 @@ describe('Transaction', function() {
it('Construct TrustSet transaction - invalid account', function() {
assert.throws(function() {
var limit = '1/USD/r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe';
var transaction = new Transaction().trustSet('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', limit, 1.0, 1.0);
new Transaction().trustSet('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', limit, 1.0, 1.0);
});
});
@@ -1682,9 +1688,9 @@ describe('Transaction', function() {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
assert.strictEqual(transaction.callback, void(0));
assert.strictEqual(transaction._errorHandler, void(0));
assert.strictEqual(transaction._successHandler, void(0));
assert.strictEqual(transaction.callback, undefined);
assert.strictEqual(transaction._errorHandler, undefined);
assert.strictEqual(transaction._successHandler, undefined);
assert.strictEqual(transaction.listeners('error').length, 1);
var account = remote.addAccount('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
@@ -1705,13 +1711,13 @@ describe('Transaction', function() {
receivedSuccess = true;
});
function submitCallback(err, res) {
function submitCallback(err) {
setImmediate(function() {
assert.ifError(err);
assert(receivedSuccess);
done();
});
};
}
transaction.submit(submitCallback);
@@ -1744,14 +1750,14 @@ describe('Transaction', function() {
receivedError = true;
});
function submitCallback(err, res) {
function submitCallback(err) {
setImmediate(function() {
assert(err);
assert.strictEqual(err.constructor.name, 'RippleError');
assert(receivedError);
done();
});
};
}
transaction.submit(submitCallback);
});
@@ -1771,7 +1777,7 @@ describe('Transaction', function() {
it('Submit transaction - invalid account', function(done) {
var remote = new Remote();
assert.throws(function() {
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWeZ');
new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWeZ');
});
done();
});
@@ -1781,7 +1787,9 @@ describe('Transaction', function() {
remote.setSecret('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79', 'snPwFATthTkKnGjEW73q3TL4yci1Q');
var server = new Server(remote, 'wss://s1.ripple.com:443');
server._computeFee = function() { return '12'; };
server._computeFee = function() {
return '12';
};
server._connected = true;
remote._servers.push(server);
@@ -1797,7 +1805,7 @@ describe('Transaction', function() {
transaction.abort();
});
transaction.submit(function(err, res) {
transaction.submit(function(err) {
setImmediate(function() {
assert(err);
assert.strictEqual(err.result, 'tejAbort');
@@ -1822,7 +1830,7 @@ describe('Transaction', function() {
872298,
543305
]
.forEach(function(index){
.forEach(function(index) {
var tx = new Transaction();
tx.initialSubmitIndex = index;
queue.push(tx);
@@ -1833,11 +1841,9 @@ describe('Transaction', function() {
return a.initialSubmitIndex - b.initialSubmitIndex;
});
sorted.forEach(function(tx){
sorted.forEach(function(tx) {
assert.strictEqual(queue.getMinLedger(), tx.initialSubmitIndex);
queue.remove(tx);
});
});
});
//vim:sw=2:sts=2:ts=8:et