Compare commits

...

111 Commits

Author SHA1 Message Date
Geert Weening
ed875a35b4 Bump version to 0.12.0-rc2 2015-02-07 11:52:04 -08:00
Geert Weening
e85b0c2122 Update release notes 2015-02-07 11:51:20 -08:00
Geert Weening
f5b192f55f Merge branch 'develop' into release 2015-02-07 11:49:42 -08:00
Geert Weening
ff86d5381d Merge pull request #277 from geertweening/fix/memo_serialization
[FIX] unsymmetric memo serializing
2015-02-07 10:28:08 -08:00
Geert Weening
b63ac4addb [TASK] bump version to 0.12.0-rc1 2015-02-06 15:10:03 -08:00
Geert Weening
3e1a66d617 [DOC] update release notes 2015-02-06 15:09:02 -08:00
Geert Weening
93ed5a8cae Merge pull request #278 from ripple/fix-tests
Fix server-test for updated WS
2015-02-05 14:30:06 -08:00
wltsmrz
2e6e8807be Fix server-test for updated WS 2015-02-05 13:51:25 -08:00
Geert Weening
1ed36fabdb [FIX] unsymmetric memo serializing
treat memos as unknown binary, with optionally parsing unsynthesized hint fields
2015-02-05 11:13:37 -08:00
Geert Weening
8dc40ee379 Merge pull request #276 from ripple/bump-dependencies
Bump dependencies versions
2015-02-05 11:13:16 -08:00
Geert Weening
db4c7c89e3 Merge pull request #275 from ripple/mocha-reporter
Use mocha tap reporter only for Travis CI
2015-02-05 11:12:21 -08:00
wltsmrz
f9bc7cc746 Bump dependencies versions 2015-02-05 00:54:07 -08:00
wltsmrz
8f87ed65f9 Use mocha tap reporter only for Travis CI 2015-02-04 23:32:14 -08:00
Geert Weening
39c37631f3 Merge pull request #269 from clark800/feature/amount_cleanup
Cleanup amount.js
2015-02-04 14:06:17 -08:00
Chris Clark
d0fb291c4e Cleanup amount.js 2015-02-04 13:57:57 -08:00
wltsmrz
793523cbe9 Merge pull request #274 from clark800/fix/parse_human
[FIX] Handle invalid input in parse_human
2015-02-04 12:59:05 -08:00
wltsmrz
6da4dd9ecc Remove Gulp lint task, update eslint.json 2015-02-04 02:50:32 -08:00
Geert Weening
79892af8f8 Merge pull request #272 from ripple/fix-taker-gets-funded
Fix taker_gets_funded exceeding offer.TakerGets
2015-02-03 18:35:25 -08:00
Chris Clark
b86790c854 Check for null in isNumber 2015-02-03 18:23:18 -08:00
Chris Clark
c8f18c8c85 [FIX] Handle invalid input in parse_human 2015-02-03 18:06:15 -08:00
wltsmrz
b19ecb4482 Fix taker_gets_funded exceeding offer.TakerGets 2015-02-03 14:50:33 -08:00
wltsmrz
ba9af55aca Merge pull request #268 from ripple/remote-updates
Remote updates
2015-02-02 16:19:45 -08:00
wltsmrz
35d76b3520 Add deprecation warnings to request constructors
* The first argument to request constructor functions should be an
object containing request properties
* Improve Remote test coverage
2015-01-30 21:08:45 -08:00
Geert Weening
a5a0326092 Merge branch 'release' into develop 2015-01-29 15:39:18 -08:00
Geert Weening
a05833f845 bump version to 0.11.0 2015-01-29 14:58:28 -08:00
Bo Chen
c5deb60510 [FIX] update transaction binary parsing to account for XRP delivered amounts 2015-01-29 14:27:11 -08:00
Geert Weening
ff2ff89e3e Merge pull request #271 from boxbag/parse-binary-transaction
[FIX] update transaction binary parsing to account for XRP delivered amounts
2015-01-29 10:52:12 -08:00
Bo Chen
35a346a674 [FIX] update transaction binary parsing to account for XRP delivered amounts 2015-01-29 10:06:10 -08:00
Geert Weening
1217a95c52 Merge pull request #267 from clark800/feature/bignumberjs
[TASK] Refactor to use bignumber.js
2015-01-27 11:43:09 -08:00
Chris Clark
d025b4a0c3 [TASK] Refactor to use bignumber.js 2015-01-27 10:23:33 -08:00
Geert Weening
2cab50f920 [TASK] bump version to 0.11.0-rc3 2015-01-26 14:42:51 -08:00
Geert Weening
b049278dde [DOC] update release notes 2015-01-26 14:42:34 -08:00
Geert Weening
93335e74cb Merge branch 'release' into develop 2015-01-26 14:34:18 -08:00
wltsmrz
2833a7b66e Cleanup, deprecations
* REMOVED Remote storage interface
* REMOVED Remote "ping" configuration
* REMOVED Old/deprecated Remote server configuration
	  (websocket_ip, websocket_port)
* REMOVED browser "online" reconnect listener
2015-01-26 14:00:54 -08:00
wltsmrz
0d05b960f7 Merge pull request #266 from clark800/feature/perftest
[TASK] Add performance test "npm run perf"
2015-01-26 13:31:31 -08:00
Chris Clark
9fd64a9209 [TASK] Add performance test "npm run perf" 2015-01-23 17:03:57 -08:00
wltsmrz
1637d26de3 Merge pull request #264 from lumberj/bug/ledger_select
[BUG] `Request.prototype.ledgerSelect` can accept ledger_index as String
2015-01-21 10:13:06 -08:00
Alan Cohen
525ff9b75e [BUG] Request.prototype.ledgerSelect can accept ledger_index as String 2015-01-21 08:54:40 -08:00
wltsmrz
42e7932f59 Merge pull request #263 from lumberj/feature/requestOffer
[FEATURE] Remote.prototype.requestOffer
2015-01-20 18:40:42 -08:00
Alan Cohen
98f40abfc3 [FEATURE] Remote.prototype.requestOffer
See: https://ripple.com/build/rippled-apis/#ledger-entry

Rippled request
```js
{
  "id": 1,
  "command": "ledger_entry",
  "type": "offer",
  "offer": {"account": "rEQWVz1qN4DWw5J17s3DgXQzUuVYDSpK6M", "seq":5 },
  "ledger_index": 10850610
}
```

Response
```js
{
  "id": 7,
  "status": "success",
  "type": "response",
  "result": {
    "index": "F9DCCD26C770952D216085BF97D7E225F6A8B578ABD066AA92D7D06144B535EE",
    "ledger_index": 10850610,
    "node": {
      "Account": "rEQWVz1qN4DWw5J17s3DgXQzUuVYDSpK6M",
      "BookDirectory": "CF8D13399C6ED20BA82740CFA78E928DC8D498255249BA6351038D7EA4C68000",
      "BookNode": "0000000000000000",
      "Flags": 131072,
      "LedgerEntryType": "Offer",
      "OwnerNode": "0000000000000000",
      "PreviousTxnID": "64FAB660F9F5D9E7183E977701930AD09F372129C56E1685B68A914D8A43CA41",
      "PreviousTxnLgrSeq": 10850602,
      "Sequence": 5,
      "TakerGets": "1000000",
      "TakerPays": {
        "currency": "USD",
        "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
        "value": "100"
      },
      "index": "F9DCCD26C770952D216085BF97D7E225F6A8B578ABD066AA92D7D06144B535EE"
    },
    "validated": false
  }
}
```
2015-01-20 18:17:33 -08:00
Geert Weening
55cd13ed4e Merge pull request #261 from boxbag/binary-default
[TASK] set binary as default for commands that accept the flag
2015-01-20 15:56:29 -08:00
Bo Chen
7cb113fcbc [TASK] set binary as default for commands that accept the flag 2015-01-20 14:10:22 -08:00
wltsmrz
ddbb999194 Merge pull request #262 from darkdarkdragon/develop-pathfind-fix
[FIX] properly close path find request
2015-01-16 16:17:32 -08:00
Ivan Tivonenko
1db96829ed [FIX] properly close path find request
path create uses broadcast, so close must
use this too
2015-01-17 01:48:18 +02:00
wltsmrz
3498dea18c Merge pull request #260 from ripplerm/develop
double check 'tes' code before emitting 'success'
2015-01-16 09:46:30 -08:00
ripplerm
97a8c87490 double check 'tes' code before emitting 'success'
result of a received transaction could be in 'tes' or 'tec'.
2015-01-17 01:01:27 +08:00
Geert Weening
fa72e09840 [TASK] bump version to 0.11.0-rc2 2015-01-15 16:24:14 -08:00
Geert Weening
d8cad710a5 [TASK] fix superagent dependency 2015-01-15 16:24:14 -08:00
Geert Weening
f91dcc33d3 [TASK] bump version to 0.11.0-rc1 2015-01-15 16:24:14 -08:00
Geert Weening
e5f524ec56 [DOC] update release notes 2015-01-15 16:24:14 -08:00
Geert Weening
f9b13cbc7f [TASK] re-add package.json for crypt 2015-01-15 16:24:14 -08:00
Geert Weening
c7e0ba68f6 [TASK] bump version to 0.11.0-rc2 2015-01-15 16:20:38 -08:00
Geert Weening
220262d192 [TASK] fix superagent dependency 2015-01-15 16:19:36 -08:00
Geert Weening
1f860ecba6 Merge pull request #259 from ripple/use-eslint
Use eslint, remove jshint
2015-01-15 16:11:26 -08:00
wltsmrz
69a13b71ea Use eslint, remove jshint 2015-01-15 16:05:38 -08:00
Geert Weening
0a27afe6ee [TASK] bump version to 0.11.0-rc1 2015-01-15 12:32:05 -08:00
Geert Weening
a3de021cd2 [DOC] update release notes 2015-01-15 12:31:55 -08:00
Geert Weening
4dcbe78e83 [TASK] re-add package.json for crypt 2015-01-15 12:31:46 -08:00
Geert Weening
84a8e8cbf6 Merge pull request #258 from ripple/throw-on-tls-error
Throw on tls error
2015-01-14 17:54:26 -08:00
Geert Weening
e4b2b3d06b Merge pull request #257 from ripple/fix-subscribe-handler
Fix complete ledgers check on subscription that is not initial
2015-01-14 17:53:42 -08:00
wltsmrz
000a2ea00c Abort server connection on unrecoverable TLS error 2015-01-14 17:47:23 -08:00
wltsmrz
89de91301e Fix complete ledgers check on subscription that is not initial 2015-01-14 17:19:21 -08:00
wltsmrz
6be84bfa73 Merge pull request #254 from ripple/use-tap-reporter
Use TAP reporter
2015-01-14 15:30:54 -08:00
wltsmrz
6cd79e7237 Use TAP reporter 2015-01-14 15:21:48 -08:00
wltsmrz
11d73173b8 Merge pull request #252 from ripple/remove-vault-client
Remove blobvault client
2015-01-14 15:07:54 -08:00
wltsmrz
9b3d62b765 Remove blobvault client 2015-01-14 14:54:55 -08:00
wltsmrz
2bdff53e68 Merge pull request #249 from ripple/request-broadcast/filter
Request broadcast/filter
2015-01-14 13:43:58 -08:00
wltsmrz
8af5f9c28e Add request broadcast/filter 2015-01-14 13:40:54 -08:00
Geert Weening
9f71abf978 Merge pull request #248 from clark800/feature/flow
[TASK] Add flow tasks to Gulpfile
2015-01-13 16:48:58 -08:00
Chris Clark
3fc2d3c1d9 [TASK] Add flow tasks to Gulpfile 2015-01-12 17:34:11 -08:00
Geert Weening
c0c8db6dcc [TASK] bump version to 0.10.1-rc2 2015-01-07 11:39:37 -08:00
Geert Weening
27249c0bb4 [DOC] update release notes 2015-01-07 11:39:04 -08:00
Geert Weening
62e9684542 Merge branch 'release' into develop 2015-01-07 11:37:23 -08:00
Geert Weening
74b006cb0b Merge pull request #247 from ripple/binary-account-tx
Decrease redundancy in binary account_tx parsing
2015-01-07 11:00:09 -08:00
Geert Weening
77b33f11ab Merge pull request #246 from lumberj/bug/incorrectly_deleted_function
[BUG] Add function removed in revert 51211bb
2015-01-07 10:59:20 -08:00
wltsmrz
0aba638e6e Decrease redundancy in binary account_tx parsing 2015-01-07 10:53:46 -08:00
Geert Weening
e82522349f Merge pull request #242 from clark800/feature/jshintrc
[TASK] Update jshintrc
2015-01-07 10:37:56 -08:00
Chris Clark
e520700260 [TASK] Update jshintrc 2015-01-07 10:06:54 -08:00
Alan Cohen
3ec335f3a6 [BUG] Add function removed in revert 51211bb
- Also, add tests
2015-01-07 09:55:12 -08:00
Geert Weening
0f212e4dd1 [TASK] bump version to 0.10.1-rc1 2015-01-06 14:59:05 -08:00
Geert Weening
c263654c88 [DOC] update release notes 2015-01-06 14:58:30 -08:00
Geert Weening
874e3f24a6 Merge branch 'release' into develop 2015-01-06 14:52:03 -08:00
Geert Weening
51211bbba0 Revert "[TASK] Add Transaction.getBalanceChanges, computed from metadata"
See the balance change package in [ripple-lib-extensions](https://github.com/ripple/ripple-lib-extensions)

This reverts commit 72387873b4.

Conflicts:
	test/metadata-test.js
2015-01-06 14:42:24 -08:00
Geert Weening
5d1ff1c912 Merge pull request #245 from ripple/binary-account-tx
Wrap account_tx binary parsing in async.setImmediate
2015-01-06 11:08:22 -08:00
wltsmrz
ea1be4fc50 Wrap account_tx binary parsing in async.setImmediate 2015-01-06 06:51:18 -08:00
Geert Weening
7cc05f0d92 Merge pull request #244 from mehulkar/patch-1
Update link to deprecated API docs
2015-01-05 14:48:12 -08:00
Mehul Kar
54606f3c21 Update link to deprecated API docs 2015-01-05 14:43:05 -08:00
wltsmrz
a5d1705930 Merge pull request #236 from ripple/sum-account-offers
Fix cumulative account order tracking for native amounts
2014-12-26 21:06:42 -08:00
wltsmrz
bfc0fb6c88 Merge pull request #240 from lumberj/task/add_limit_to_bookoffers
[TASK] Allow `limit` option in remote.requestBookOffers
2014-12-24 14:44:15 -08:00
Alan Cohen
d1d4452217 [TASK] Allow limit option in remote.requestBookOffers 2014-12-24 14:24:32 -08:00
wltsmrz
2166a434a3 Merge pull request #241 from ripple/add-jshintrc
Add jshintrc
2014-12-23 23:08:52 -08:00
wltsmrz
1053fa18e1 Add jshintrc 2014-12-23 20:20:31 -08:00
wltsmrz
fa147d467e Merge pull request #239 from ripple/metadata-account-fix
Fix metadata account check
2014-12-23 20:19:09 -08:00
wltsmrz
3f61598d6c Fix metadata account check 2014-12-23 03:22:46 -08:00
wltsmrz
9bf3724ce6 Merge pull request #237 from lumberj/task/fix_jsdoc
[TASK] Fixup the annotation for remote.requestBookOffers
2014-12-22 15:37:28 -08:00
Alan Cohen
c2f27a4deb [TASK] Fixup the annotation for remote.requestBookOffers 2014-12-22 15:31:56 -08:00
wltsmrz
b6b99dde02 Fix cumulative account order tracking for native amounts 2014-12-20 20:43:01 -08:00
Geert Weening
1fd0f4a8fe Merge pull request #234 from ripple/sum-account-offers
Track order funded status based on cumulative account offers
2014-12-19 17:27:38 -08:00
wltsmrz
67d39737a4 Track order funded status based on cumulative account orders 2014-12-19 16:43:37 -08:00
Geert Weening
aef4fe29a3 Merge pull request #233 from lumberj/feature_test/request_book_offers
[FEATURE] Add tests and support for ledger for requestBookOffers (RLJS-115)
2014-12-19 16:39:24 -08:00
Alan Cohen
34c0677c45 [FEATURE] Add tests and support for ledger for requestBookOffers 2014-12-19 16:35:29 -08:00
Geert Weening
3cb4a64b47 [TEST] fix Meta require 2014-12-19 16:29:29 -08:00
Geert Weening
0db0375a5e Merge pull request #227 from clark800/feature/balance_changes
[TASK] Add balance_change array to transaction response, computed from metadata
2014-12-19 15:14:22 -08:00
wltsmrz
47e6bdc644 Merge pull request #231 from clark800/feature/test_compiled_js
[TASK] Refactor tests to support testing compiled javascript
2014-12-19 13:28:03 -08:00
Chris Clark
66c2e27711 [TASK] Refactor tests to support testing compiled javascript 2014-12-19 13:17:02 -08:00
Chris Clark
72387873b4 [TASK] Add Transaction.getBalanceChanges, computed from metadata 2014-12-18 16:12:06 -08:00
Geert Weening
59017bc0bd Merge pull request #229 from ripple/account-txn-id
Add AccountTxnID flag and setter
2014-12-17 12:42:40 -08:00
wltsmrz
2dde114d3d Add AccountTxnID flag and setter 2014-12-16 13:25:32 -08:00
wltsmrz
9e89904f03 Fix undefined log function in Transaction.finalize() 2014-12-12 21:26:54 -08:00
Geert Weening
56d0aca254 Merge pull request #224 from kureus/patch-1
Doc example code syntax error
2014-12-12 11:13:54 -08:00
Dan Quirk
239710cebf Doc example code syntax error 2014-12-11 13:16:14 +00:00
79 changed files with 9037 additions and 7265 deletions

7
.flowconfig Normal file
View File

@@ -0,0 +1,7 @@
[ignore]
[include]
[libs]
[options]

7
.gitignore vendored
View File

@@ -25,6 +25,7 @@ Release/*.*
# Ignore locally installed node_modules
node_modules
!test/node_modules
# Ignore tmp directory.
tmp
@@ -52,3 +53,9 @@ npm-debug.log
# Ignore dist folder, build for bower
dist/
# Ignore flow output directory
out/
# Ignore perf test cache
scripts/cache

View File

@@ -1,7 +1,7 @@
language: node_js
node_js:
- "0.10"
script: npm test --coverage
script: MOCHA_REPORTER=tap npm test --coverage
after_success:
- npm run coveralls
notifications:
@@ -10,4 +10,4 @@ notifications:
- https://webhooks.gitter.im/e/d1ec4245f90231619d30
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
on_start: false # default: false

View File

@@ -1,11 +1,17 @@
var gulp = require('gulp');
var gutil = require('gulp-util');
var watch = require('gulp-watch');
var plumber = require('gulp-plumber');
var filelog = require('gulp-filelog');
var cleanDest = require('gulp-clean-dest');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var webpack = require('webpack');
var jshint = require('gulp-jshint');
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');
@@ -50,6 +56,10 @@ var sjclSrc = [
'src/js/sjcl-custom/sjcl-jacobi.js'
];
function logPluginError(error) {
gutil.log(error.toString());
}
gulp.task('concat-sjcl', function() {
return gulp.src(sjclSrc)
.pipe(concat('sjcl.js'))
@@ -149,34 +159,27 @@ gulp.task('bower-version', function() {
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.task('lint', function() {
gulp.src('src/js/ripple/*.js')
.pipe(jshint())
.pipe(map(function(file, callback) {
if (!file.jshint.success) {
console.log('\nIn', file.path);
gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
});
file.jshint.results.forEach(function(err) {
if (err && err.error) {
var col1 = err.error.line + ':' + err.error.character;
var col2 = '[' + err.error.reason + ']';
var col3 = '(' + err.error.code + ')';
while (col1.length < 8) {
col1 += ' ';
}
console.log(' ' + [ col1, col2, col3 ].join(' '));
}
});
}
callback(null, file);
// To use this, each javascript file must have /* @flow */ on the first line
gulp.task('typecheck', function() {
return gulp.src('src/js/ripple/*.js')
.pipe(flow({ // note: do not set the 'all' option, it is broken
weak: true, // remove this after all errors are addressed
killFlow: true
}));
});
gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
gulp.task('strip', function() {
return gulp.src('src/js/ripple/*.js')
.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(filelog())
.pipe(gulp.dest('out'));
});
gulp.task('version-bump', function() {

View File

@@ -1,3 +1,60 @@
##0.12.0
**Breaking Changes**
+ REMOVED Remote storage interface
+ REMOVED Remote `ping` configuration
+ REMOVED Old/deprecated Remote server configuration (websocket_ip, websocket_port)
+ REMOVED browser `online` reconnect listener
- [2833a7b6](https://github.com/ripple/ripple-lib/commit/2833a7b66e696dab427464625077f9b93092d0d5)
+ Remove `jsbn` and use `bignumber.js` instead for big number math
+ The `allow_nan` flag has been removed. Results for invalid amounts will always be `NaN`
- [d025b4a0](https://github.com/ripple/ripple-lib/commit/d025b4a0c3a98a6de27a1bee9573c85347bcd66b)
- [c8f18c8c](https://github.com/ripple/ripple-lib/commit/c8f18c8c8590b7b48e370e0325b6677b7720294f)
- [b86790c8](https://github.com/ripple/ripple-lib/commit/b86790c8543c239a532fd7697d4652829019d385)
- [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)
+ [Fix taker_gets_funded exceeding offer.TakerGets](https://github.com/ripple/ripple-lib/commit/b19ecb4482b589d575382b7a5d0480b963383bb1)
+ [Fix unsymmetric memo serializing](https://github.com/ripple/ripple-lib/commit/1ed36fabdbd54f4d31078c2b0eaa3becc0fe2821)
+ [Update transaction binary parsing to account for XRP delivered amounts](https://github.com/ripple/ripple-lib/commit/35a346a674e6ee1e1e495db93700d55984efc7dd)
+ [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)
+ [Add support for `ledger` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/34c0677c453c409ef0a5b351959abdc176d3bacb)
+ [Add support for `limit` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/d1d4452217c878d0b377d24830b4cd8b3162f6e0)
+ [Add `ledgerSelect` request constructor in `Remote`](https://github.com/ripple/ripple-lib/commit/98f40abfc3aa74dec5067a2d90002756cc8acd01)
+ [Default to binary data for commands that accept the binary flag](https://github.com/ripple/ripple-lib/commit/7cb113fcbcfc1e3e9830a999148b3e78df3387cc)
+ [Fix metadata account check](https://github.com/ripple/ripple-lib/commit/3f61598d6c87e3cc877af60e2d515f9eff73dfe1)
+ [Double check `tes` code before emitting `success`](https://github.com/ripple/ripple-lib/commit/97a8c874903eb7309d8f755955ac80872f670582)
+ [Decrease redundancy in binary account_tx parsing](https://github.com/ripple/ripple-lib/commit/0aba638e6e7f4f6e22cb6424eed3897ebad90a5a)
+ [Abort server connection on unrecoverable TLS error](https://github.com/ripple/ripple-lib/commit/000a2ea00c57157044aeca0fb3f24b37669b163c)
+ [Fix complete ledgers check on subscription that is not initial](https://github.com/ripple/ripple-lib/commit/89de91301e682a46dc60aaacc7ae152e8fe1b7c7)
##0.10.0
+ [Transaction changes](https://github.com/ripple/ripple-lib/pull/221)

View File

@@ -9,7 +9,7 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
###Features
+ Connect to a rippled server in JavaScript (Node.js or browser)
+ Issue [rippled API](https://ripple.com/wiki/JSON_Messages) requests
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
+ Listen to events on the Ripple network (transaction, ledger, etc.)
+ Sign and submit transactions to the Ripple network

20
docs/BUILD.md Normal file
View File

@@ -0,0 +1,20 @@
Using Flow typechecking
=======================
Stage 1
-------
1. Add /* @flow */ to the top of a file you want to typecheck
2. Run `gulp typecheck` to generate a list of warnings
Stage 2
-------
When all source files have the /* @flow */ header and all warnings have been
addressed, remove the `weak: true` option from Gulpfile.js, run
`gulp typecheck` and remove all the additional warnings.
Stage 3
-------
Add type annotations to the source code and run `gulp strip` to strip
the type annotations and write the output to the `out` directory. After
type annotations are added, the program must be run from the `out` directory
because Node does not understand the annotations

View File

@@ -39,7 +39,7 @@ This file provides step-by-step walkthroughs for some of the most common usages
]
}
var remote = new Remote({options});
var remote = new Remote(options);
remote.connect(function(err, res) {
/* remote connected, use some remote functions here */

72
eslint.json Normal file
View File

@@ -0,0 +1,72 @@
{
"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" ]
}
}

82
npm-shrinkwrap.json generated
View File

@@ -1,17 +1,27 @@
{
"name": "ripple-lib",
"version": "0.10.0",
"version": "0.12.0-rc2",
"dependencies": {
"async": {
"version": "0.8.0",
"from": "async@>=0.8.0 <0.9.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.8.0.tgz"
"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"
},
"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"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0",
@@ -90,11 +100,6 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
},
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
}
}
},
@@ -128,26 +133,51 @@
}
},
"ws": {
"version": "0.4.32",
"from": "ws@>=0.4.31 <0.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz",
"version": "0.7.1",
"from": "ws@>=0.7.1 <0.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.1.tgz",
"dependencies": {
"commander": {
"version": "2.1.0",
"from": "commander@>=2.1.0 <2.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
},
"nan": {
"version": "1.0.0",
"from": "nan@>=1.0.0 <1.1.0"
},
"tinycolor": {
"version": "0.0.1",
"from": "tinycolor@>=0.0.0 <1.0.0"
},
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5"
"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"
},
"nan": {
"version": "1.6.1",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.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"
},
"nan": {
"version": "1.6.1",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.1.tgz"
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.10.0",
"version": "0.12.0-rc2",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
@@ -15,33 +15,45 @@
"test": "test"
},
"dependencies": {
"async": "~0.8.0",
"ws": "~0.4.31",
"async": "~0.9.0",
"bignumber.js": "^2.0.0",
"extend": "~1.2.1",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"superagent": "^0.18.0",
"ripple-wallet-generator": "1.0.1"
"ripple-wallet-generator": "1.0.1",
"ws": "~0.7.1",
"superagent": "^0.18.0"
},
"devDependencies": {
"mocha": "~1.14.0",
"gulp": "~3.6.2",
"gulp-concat": "~2.2.0",
"gulp-jshint": "~1.5.5",
"gulp-uglify": "~0.3.0",
"gulp-rename": "~1.2.0",
"gulp-bump": "~0.1.10",
"webpack": "~1.1.11",
"map-stream": "~0.1.0",
"istanbul": "~0.2.10",
"assert-diff": "0.0.4",
"coveralls": "~2.10.0",
"eslint": "^0.13.0",
"gulp": "~3.8.10",
"gulp-bump": "~0.1.13",
"gulp-clean-dest": "^0.1.0",
"gulp-concat": "~2.4.3",
"gulp-filelog": "^0.4.1",
"gulp-flowtype": "^0.4.1",
"gulp-plumber": "^0.6.6",
"gulp-react": "^2.0.0",
"gulp-rename": "~1.2.0",
"gulp-uglify": "~1.1.0",
"gulp-util": "^3.0.3",
"gulp-watch": "^4.1.0",
"istanbul": "~0.3.5",
"map-stream": "~0.1.0",
"mocha": "~2.1.0",
"nock": "^0.34.1",
"webpack": "~1.5.3",
"yargs": "~1.3.1"
},
"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 spec test/*-test.js",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
"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",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
"lint": "eslint --reset -c eslint.json src/js/ripple/*.js",
"perf": "./scripts/perf_test.sh"
},
"repository": {
"type": "git",

11
scripts/perf_test.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
URL="https://www.dropbox.com/s/a0gy7vbb86eeqlq/ledger-full-1000000.json?dl=1"
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
DEST="$DIR/cache/ledger-full-1000000.json"
if [ ! -e "$DEST" ]
then
echo "Downloading test data..."
mkdir -p "$DIR/cache"
curl -L "$URL" > "$DEST"
fi
time node "$DIR/verify_ledger_json.js" "$DEST"

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,7 @@ function Account(remote, account) {
if (!self._subs && self._remote._connected) {
self._remote.request_subscribe()
.add_account(self._account_id)
.broadcast();
.broadcast().request();
}
self._subs += 1;
}
@@ -59,7 +59,7 @@ function Account(remote, account) {
if (!self._subs && self._remote._connected) {
self._remote.request_unsubscribe()
.add_account(self._account_id)
.broadcast();
.broadcast().request();
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,59 +0,0 @@
var async = require('async');
var superagent = require('superagent');
var RippleTxt = require('./rippletxt').RippleTxt;
var AuthInfo = { };
AuthInfo._getRippleTxt = function(domain, callback) {
RippleTxt.get(domain, callback);
};
AuthInfo._getUser = function(url, callback) {
superagent.get(url, callback);
};
/**
* Get auth info for a given username
*
* @param {string} domain - Domain which hosts the user's info
* @param {string} username - Username who's info we are retreiving
* @param {function} fn - Callback function
*/
AuthInfo.get = function(domain, username, callback) {
var self = this;
username = username.toLowerCase();
function getRippleTxt(callback) {
self._getRippleTxt(domain, function(err, txt) {
if (err) {
return callback(err);
}
if (!txt.authinfo_url) {
return callback(new Error('Authentication is not supported on ' + domain));
}
var url = Array.isArray(txt.authinfo_url) ? txt.authinfo_url[0] : txt.authinfo_url;
url += '?domain=' + domain + '&username=' + username;
callback(null, url);
});
};
function getUser(url, callback) {
self._getUser(url, function(err, res) {
if (err || res.error) {
callback(new Error('Authentication info server unreachable'));
} else {
callback(null, res.body);
}
});
};
async.waterfall([ getRippleTxt, getUser ], callback);
};
exports.AuthInfo = AuthInfo;

View File

@@ -2,8 +2,6 @@ var sjcl = require('./utils').sjcl;
var utils = require('./utils');
var extend = require('extend');
var BigInteger = utils.jsbn.BigInteger;
var Base = {};
var alphabets = Base.alphabets = {
@@ -25,33 +23,142 @@ extend(Base, {
function sha256(bytes) {
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
};
}
function sha256hash(bytes) {
return sha256(sha256(bytes));
};
}
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;
}
return remainder;
}
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) {
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;
}
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;
}
function Base58(alphabet) {
var indexes = utils.arraySet(128, -1);
for (var i = 0; i < alphabet.length; i++) {
indexes[alphabet.charCodeAt(i)] = i;
}
return {
decode: decodeString.bind(null, indexes),
encode: encodeString.bind(null, alphabet)
};
}
Base.encoders = {};
Object.keys(alphabets).forEach(function(alphabet){
Base.encoders[alphabet] = Base58(alphabets[alphabet]);
});
// --> input: big-endian array of bytes.
// <-- string at least as long as input.
Base.encode = function(input, alpha) {
var alphabet = alphabets[alpha || 'ripple'];
var bi_base = new BigInteger(String(alphabet.length));
var bi_q = new BigInteger();
var bi_r = new BigInteger();
var bi_value = new BigInteger(input);
var buffer = [];
while (bi_value.compareTo(BigInteger.ZERO) > 0) {
bi_value.divRemTo(bi_base, bi_q, bi_r);
bi_q.copyTo(bi_value);
buffer.push(alphabet[bi_r.intValue()]);
}
for (var i=0; i !== input.length && !input[i]; i += 1) {
buffer.push(alphabet[0]);
}
return buffer.reverse().join('');
return this.encoders[alpha || 'ripple'].encode(input);
};
// --> input: String
@@ -60,48 +167,12 @@ Base.decode = function(input, alpha) {
if (typeof input !== 'string') {
return void(0);
}
var alphabet = alphabets[alpha || 'ripple'];
var bi_base = new BigInteger(String(alphabet.length));
var bi_value = new BigInteger();
var i;
for (i = 0; i !== input.length && input[i] === alphabet[0]; i += 1) {
try {
return this.encoders[alpha || 'ripple'].decode(input);
}
for (; i !== input.length; i += 1) {
var v = alphabet.indexOf(input[i]);
if (v < 0) {
return void(0);
}
var r = new BigInteger();
r.fromInt(v);
bi_value = bi_value.multiply(bi_base).add(r);
catch(e) {
return (void 0);
}
// toByteArray:
// - Returns leading zeros!
// - Returns signed bytes!
var bytes = bi_value.toByteArray().map(function(b) { return b ? b < 0 ? 256+b : b : 0; });
var extra = 0;
while (extra !== bytes.length && !bytes[extra]) {
extra += 1;
}
if (extra) {
bytes = bytes.slice(extra);
}
var zeros = 0;
while (zeros !== input.length && input[zeros] === alphabet[0]) {
zeros += 1;
}
return [].concat(utils.arraySet(zeros, 0), bytes);
};
Base.verify_checksum = function(bytes) {
@@ -129,7 +200,7 @@ Base.encode_check = function(version, input, alphabet) {
};
// --> input : String
// <-- NaN || BigInteger
// <-- NaN || sjcl.bn
Base.decode_check = function(version, input, alphabet) {
var buffer = Base.decode(input, alphabet);
@@ -163,7 +234,8 @@ Base.decode_check = function(version, input, alphabet) {
// intrepret the value as a negative number
buffer[0] = 0;
return new BigInteger(buffer.slice(0, -4), 256);
return sjcl.bn.fromBits (
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
};
exports.Base = Base;

View File

@@ -0,0 +1,33 @@
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;

File diff suppressed because it is too large Load Diff

View File

@@ -308,7 +308,7 @@ Currency.prototype.get_interest_percentage_at = function(referenceDate, decimals
// currency data, since there are some values that are invalid.
//
//Currency.prototype.is_valid = function() {
// return this._value instanceof BigInteger && ...;
// return UInt.prototype.is_valid() && ...;
//};
Currency.prototype.to_json = function(opts) {

View File

@@ -5,6 +5,7 @@ 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;
@@ -12,13 +13,13 @@ exports.Meta = require('./meta').Meta;
exports.SerializedObject = require('./serializedobject').SerializedObject;
exports.RippleError = require('./rippleerror').RippleError;
exports.Message = require('./message').Message;
exports.VaultClient = require('./vaultclient').VaultClient;
exports.AuthInfo = require('./authinfo').AuthInfo;
exports.RippleTxt = require('./rippletxt').RippleTxt;
exports.binformat = require('./binformat');
exports.utils = require('./utils');
exports.Server = require('./server').Server;
exports.Wallet = require('./wallet');
exports.Ledger = require('./ledger').Ledger;
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
exports.RangeSet = require('./rangeset').RangeSet;
// 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,6 +29,7 @@ exports.Wallet = require('./wallet');
// 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');

View File

@@ -1,5 +1,5 @@
var extend = require('extend');
var utils = require('./utils');
var utils = require('./utils');
var UInt160 = require('./uint160').UInt160;
var Amount = require('./amount').Amount;
@@ -26,28 +26,37 @@ function Meta(data) {
data.AffectedNodes.forEach(this.addNode, this);
};
Meta.nodeTypes = [
Meta.NODE_TYPES = [
'CreatedNode',
'ModifiedNode',
'DeletedNode'
];
Meta.amountFieldsAffectingIssuer = [
Meta.AMOUNT_FIELDS_AFFECTING_ISSUER = [
'LowLimit',
'HighLimit',
'TakerPays',
'TakerGets'
];
Meta.ACCOUNT_FIELDS = [
'Account',
'Owner',
'Destination',
'Issuer',
'Target'
];
/**
* @param {Object} node
* @api private
*/
Meta.prototype.getNodeType = function(node) {
var result = null;
for (var i=0; i<Meta.nodeTypes.length; i++) {
var type = Meta.nodeTypes[i];
for (var i=0; i<Meta.NODE_TYPES.length; i++) {
var type = Meta.NODE_TYPES[i];
if (node.hasOwnProperty(type)) {
result = type;
break;
@@ -57,6 +66,15 @@ Meta.prototype.getNodeType = function(node) {
return result;
};
/**
* @param {String} field
* @api private
*/
Meta.prototype.isAccountField = function(field) {
return Meta.ACCOUNT_FIELDS.indexOf(field) !== -1;
};
/**
* Add node to metadata
*
@@ -72,7 +90,6 @@ Meta.prototype.addNode = function(node) {
if ((result.nodeType = this.getNodeType(node))) {
node = node[result.nodeType];
result.diffType = result.nodeType;
result.entryType = node.LedgerEntryType;
result.ledgerIndex = node.LedgerIndex;
@@ -114,56 +131,6 @@ Meta.prototype.getNodes = function(options) {
}
};
/**
* Execute a function on each affected node.
*
* The callback is passed two parameters. The first is a node object which looks
* like this:
*
* {
* // Type of diff, e.g. CreatedNode, ModifiedNode
* nodeType: 'CreatedNode'
*
* // Type of node affected, e.g. RippleState, AccountRoot
* entryType: 'RippleState',
*
* // Index of the ledger this change occurred in
* ledgerIndex: '01AB01AB...',
*
* // Contains all fields with later versions taking precedence
* //
* // This is a shorthand for doing things like checking which account
* // this affected without having to check the nodeType.
* fields: {...},
*
* // Old fields (before the change)
* fieldsPrev: {...},
*
* // New fields (that have been added)
* fieldsNew: {...},
*
* // Changed fields
* fieldsFinal: {...}
* }
*
* The second parameter to the callback is the index of the node in the metadata
* (first entry is index 0).
*/
[
'forEach',
'map',
'filter',
'every',
'some',
'reduce'
].forEach(function(fn) {
Meta.prototype[fn] = function() {
return Array.prototype[fn].apply(this.nodes, arguments);
};
});
Meta.prototype.each = Meta.prototype.forEach;
Meta.prototype.getAffectedAccounts = function(from) {
if (this._affectedAccounts) {
@@ -176,12 +143,16 @@ Meta.prototype.getAffectedAccounts = function(from) {
// TransactionMetaSet::getAffectedAccounts
for (var i=0; i<this.nodes.length; i++) {
var node = this.nodes[i];
var fields = (node.nodeType === 'CreatedNode') ? node.fieldsNew : node.fieldsFinal;
var fields = (node.nodeType === 'CreatedNode')
? node.fieldsNew
: node.fieldsFinal;
for (var fieldName in fields) {
var field = fields[fieldName];
if (typeof field === 'string' && UInt160.is_valid(field)) {
if (this.isAccountField(fieldName) && UInt160.is_valid(field)) {
accounts.push(field);
} else if (~Meta.amountFieldsAffectingIssuer.indexOf(fieldName)) {
} else if (~Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName)) {
var amount = Amount.from_json(field);
var issuer = amount.issuer();
if (issuer.is_valid() && !issuer.is_zero()) {
@@ -238,4 +209,53 @@ Meta.prototype.getAffectedBooks = function() {
return this._affectedBooks;
};
/**
* Execute a function on each affected node.
*
* The callback is passed two parameters. The first is a node object which looks
* like this:
*
* {
* // Type of diff, e.g. CreatedNode, ModifiedNode
* nodeType: 'CreatedNode'
*
* // Type of node affected, e.g. RippleState, AccountRoot
* entryType: 'RippleState',
*
* // Index of the ledger this change occurred in
* ledgerIndex: '01AB01AB...',
*
* // Contains all fields with later versions taking precedence
* //
* // This is a shorthand for doing things like checking which account
* // this affected without having to check the nodeType.
* fields: {...},
*
* // Old fields (before the change)
* fieldsPrev: {...},
*
* // New fields (that have been added)
* fieldsNew: {...},
*
* // Changed fields
* fieldsFinal: {...}
* }
*/
[
'forEach',
'map',
'filter',
'every',
'some',
'reduce'
].forEach(function(fn) {
Meta.prototype[fn] = function() {
return Array.prototype[fn].apply(this.nodes, arguments);
};
});
Meta.prototype.each = Meta.prototype.forEach;
exports.Meta = Meta;

View File

@@ -43,8 +43,9 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
this._shouldSubscribe = true;
this._listeners = 0;
this._offers = [ ];
this._ownerFunds = { };
this._offerCounts = { };
this._ownerFunds = { };
this._ownerOffers = { };
// We consider ourselves synchronized if we have a current
// copy of the offers, we are online and subscribed to updates.
@@ -180,6 +181,7 @@ OrderBook.prototype.unsubscribe = function() {
OrderBook.prototype.resetCache = function() {
this._ownerFunds = { };
this._ownerOffers = { };
this._offerCounts = { };
this._synchronized = false;
};
@@ -232,6 +234,8 @@ OrderBook.prototype.removeCachedFunds = function(account) {
/**
* Get offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.getOfferCount = function(account) {
@@ -241,6 +245,8 @@ OrderBook.prototype.getOfferCount = function(account) {
/**
* Increment offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.incrementOfferCount = function(account) {
@@ -252,6 +258,8 @@ OrderBook.prototype.incrementOfferCount = function(account) {
/**
* Decrement offer count for offer owner
*
* @param {String} account address
*/
OrderBook.prototype.decrementOfferCount = function(account) {
@@ -261,6 +269,58 @@ OrderBook.prototype.decrementOfferCount = function(account) {
return result;
};
/**
* Add offer amount to sum amount being offered by an account
*
* @param {String} account address
* @param {Object|String} offer amount
* @return sum
*/
OrderBook.prototype.addOwnerOffer = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
var previousAmount = this.getOwnerOfferSum(account);
var newAmount = previousAmount.add(Amount.from_json(amount));
this._ownerOffers[account] = newAmount;
return newAmount;
};
/**
* Subtract offer amount from sum amount being offered by an account
*
* @param {String} account address
* @param {Object|String} offer amount
* @return sum
*/
OrderBook.prototype.subtractOwnerOffer = function(account, amount) {
assert(UInt160.is_valid(account), 'Account is invalid');
var previousAmount = this.getOwnerOfferSum(account);
var newAmount = previousAmount.subtract(Amount.from_json(amount));
this._ownerOffers[account] = newAmount;
return newAmount;
};
/**
* Get sum amount for offers by an account
*
* @param {String} account address
* @return sum
*/
OrderBook.prototype.getOwnerOfferSum = function(account) {
assert(UInt160.is_valid(account), 'Account is invalid');
var amount = this._ownerOffers[account];
if (!amount) {
if (this._currencyGets.is_native()) {
amount = Amount.from_json('0');
} else {
amount = Amount.from_json('0' + OrderBook.IOU_SUFFIX);
}
}
return amount;
};
/**
* Compute funded amount for a balance/transferRate
*
@@ -358,11 +418,20 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
return offer;
}
// Sum of offer amounts by an account
var offerSum = this.getOwnerOfferSum(offer.Account);
if (offerSum.is_zero()) {
// If there are no cached offer amounts for the account, default to
// TakerGets of this offer
offerSum = Amount.from_json(offer.TakerGets);
}
offer.is_fully_funded = Amount.from_json(
this._currencyGets.is_native()
? fundedAmount
: fundedAmount + OrderBook.IOU_SUFFIX
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
).compareTo(offerSum) >= 0;
if (offer.is_fully_funded) {
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
@@ -370,18 +439,21 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
return offer;
}
offer.taker_gets_funded = fundedAmount;
var isOfferGetsExceeded = Amount.from_json(fundedAmount)
.compareTo(offer.TakerGets) > 0;
if (isOfferGetsExceeded) {
offer.taker_gets_funded = offer.TakerGets;
} else {
offer.taker_gets_funded = fundedAmount;
}
var takerPaysValue = (typeof offer.TakerPays === 'object')
? offer.TakerPays.value
: offer.TakerPays;
var takerGetsValue = (typeof offer.TakerGets === 'object')
? offer.TakerGets.value
: offer.TakerGets;
var takerPays = Amount.from_json(takerPaysValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(takerGetsValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(offerSum);
var fundedPays = Amount.from_json(fundedAmount + OrderBook.IOU_SUFFIX);
var rate = takerPays.divide(takerGets);
@@ -662,9 +734,16 @@ OrderBook.offerRewrite = function(offer) {
OrderBook.prototype.setOffers = function(offers) {
assert(Array.isArray(offers));
var l = offers.length;
var newOffers = [ ];
for (var i=0, l=offers.length; i<l; i++) {
for (var i=0; i<l; i++) {
var offer = offers[i];
// Add offer amount to sum for account
this.addOwnerOffer(offer.Account, offer.TakerGets);
}
for (var i=0; i<l; i++) {
var offer = OrderBook.offerRewrite(offers[i]);
var fundedAmount;
@@ -675,8 +754,8 @@ OrderBook.prototype.setOffers = function(offers) {
this.addCachedFunds(offer.Account, fundedAmount);
}
this.setFundedAmount(offer, fundedAmount);
this.incrementOfferCount(offer.Account);
this.setFundedAmount(offer, fundedAmount);
newOffers.push(offer);
}
@@ -888,6 +967,9 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
log.info('inserting offer', this._key, node.fields);
}
// Add offer amount to sum for account
this.addOwnerOffer(node.fields.Account, node.fields.TakerGets);
var nodeFields = OrderBook.offerRewrite(node.fields);
nodeFields.LedgerEntryType = node.entryType;
nodeFields.index = node.ledgerIndex;
@@ -904,7 +986,7 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
// XXX Should use Amount#from_quality
var price = Amount.from_json(
nodeFields.TakerPays
).ratio_human(node.fields.TakerGets, DATE_REF);
).ratio_human(nodeFields.TakerGets, DATE_REF);
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
@@ -944,14 +1026,21 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
var offer = this._offers[i];
if (offer.index === node.ledgerIndex) {
if (isDeletedNode) {
// Multiple offers same account?
// Remove offer amount from sum for account
this.subtractOwnerOffer(offer.Account, offer.TakerGets);
this._offers.splice(i, 1);
this.emit('offer_removed', offer);
} else {
// TODO: This assumes no fields are deleted, which is
// probably a safe assumption, but should be checked.
var previousOffer = extend({}, offer);
var previousOffer = extend({ }, offer);
extend(offer, node.fieldsFinal);
// Remove offer amount from sum for account
this.subtractOwnerOffer(offer.Account, previousOffer.TakerGets);
// Add offer amount from sum for account
this.addOwnerOffer(offer.Account, offer.TakerGets);
this.emit('offer_changed', previousOffer, offer);
}
break;

View File

@@ -35,28 +35,27 @@ util.inherits(PathFind, EventEmitter);
PathFind.prototype.create = function () {
var self = this;
var req = this.remote.request_path_find_create(this.src_account,
this.dst_account,
this.dst_amount,
this.src_currencies,
handleInitialPath);
var req = this.remote.request_path_find_create(
this.src_account,
this.dst_account,
this.dst_amount,
this.src_currencies);
function handleInitialPath(err, msg) {
if (err) {
self.emit('error', err);
} else {
self.notify_update(msg);
}
}
req.once('error', function(err) {
self.emit('error', err);
});
req.once('success', function(msg) {
self.notify_update(msg);
});
// XXX We should add ourselves to prepare_subscribe or a similar mechanism so
// that we can resubscribe after a reconnection.
req.request();
req.broadcast().request();
};
PathFind.prototype.close = function () {
this.remote.request_path_find_close().request();
this.remote.request_path_find_close().broadcast().request();
this.emit('end');
this.emit('close');
};

67
src/js/ripple/rangeset.js Normal file
View File

@@ -0,0 +1,67 @@
var assert = require('assert');
var lodash = require('lodash');
function RangeSet() {
this._ranges = [ ];
};
/**
* Add a ledger range
*
* @param {Number|String} range string (n-n2,n3-n4)
*/
RangeSet.prototype.add = function(range) {
assert(typeof range !== 'number' || !isNaN(range), 'Ledger range malformed');
range = String(range).split(',');
if (range.length > 1) {
return range.forEach(this.add, this);
}
range = range[0].split('-').map(Number);
var lRange = {
start: range[0],
end: range[range.length === 1 ? 0 : 1]
};
// Comparisons on NaN should be falsy
assert(lRange.start <= lRange.end, 'Ledger range malformed');
var insertionPoint = lodash.sortedIndex(this._ranges, lRange, function(r) {
return r.start;
});
this._ranges.splice(insertionPoint, 0, lRange);
};
/*
* Check presence of ledger in range
*
* @param {Number|String} ledger
* @return Boolean
*/
RangeSet.prototype.has =
RangeSet.prototype.contains = function(ledger) {
assert(ledger != null && !isNaN(ledger), 'Ledger must be a number');
ledger = Number(ledger);
return this._ranges.some(function(r) {
return ledger >= r.start && ledger <= r.end;
});
};
/**
* Reset ledger ranges
*/
RangeSet.prototype.reset = function() {
this._ranges = [ ];
};
exports.RangeSet = RangeSet;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var async = require('async');
var UInt160 = require('./uint160').UInt160;
var Currency = require('./currency').Currency;
var RippleError = require('./rippleerror').RippleError;
@@ -24,6 +25,9 @@ function Request(remote, command) {
this.remote = remote;
this.requested = false;
this.reconnectTimeout = 1000 * 3;
this.successEvent = 'success';
this.errorEvent = 'error';
this.message = {
command: command,
id: void(0)
@@ -32,22 +36,20 @@ function Request(remote, command) {
util.inherits(Request, EventEmitter);
Request.prototype.broadcast = function() {
var connectedServers = this.remote.getConnectedServers();
this.request(connectedServers);
return connectedServers.length;
};
// Send the request to a remote.
Request.prototype.request = function(servers, callback) {
if (this.requested) {
return this;
}
this.emit('before');
if (typeof servers === 'function') {
callback = servers;
}
this.callback(callback);
if (this.requested) {
return this;
}
this.requested = true;
this.on('error', function(){});
this.emit('request', this.remote);
@@ -61,7 +63,129 @@ Request.prototype.request = function(servers, callback) {
this.remote.request(this);
}
this.callback(callback);
return this;
};
/**
* Broadcast request to all servers, filter responses if a function is
* provided. Return first response that satisfies the filter. Pre-filter
* requests by ledger_index (if a ledger_index is set on the request), and
* automatically retry servers when they reconnect--if they are expected to
*
* Whew
*
* @param [Function] fn
*/
Request.prototype.filter =
Request.prototype.addFilter =
Request.prototype.broadcast = function(filterFn) {
var self = this;
if (!this.requested) {
// Defer until requested, and prevent the normal request() from executing
this.once('before', function() {
self.requested = true;
self.broadcast(filterFn);
});
return this;
}
var filterFn = typeof filterFn === 'function' ? filterFn : Boolean;
var lastResponse = new Error('No servers available');
var connectTimeouts = { };
var emit = this.emit;
this.emit = function(event, a, b) {
// Proxy success/error events
switch (event) {
case 'success':
case 'error':
emit.call(self, 'proposed', a, b);
break;
default:
emit.apply(self, arguments);
}
};
function iterator(server, callback) {
// Iterator is called in parallel
if (server.isConnected()) {
// Listen for proxied success/error event and apply filter
self.once('proposed', function(res) {
lastResponse = res;
callback(filterFn(res));
});
return server._request(self);
}
// Server is disconnected but should reconnect. Wait for it to reconnect,
// and abort after a timeout
var serverID = server.getServerID();
function serverReconnected() {
clearTimeout(connectTimeouts[serverID]);
connectTimeouts[serverID] = null;
iterator(server, callback);
};
connectTimeouts[serverID] = setTimeout(function() {
server.removeListener('connect', serverReconnected);
callback(false);
}, self.reconnectTimeout);
server.once('connect', serverReconnected);
};
function complete(success) {
// Emit success if the filter is satisfied by any server
// Emit error if the filter is not satisfied by any server
// Include the last response
emit.call(self, success ? 'success' : 'error', lastResponse);
};
var servers = this.remote._servers.filter(function(server) {
// Pre-filter servers that are disconnected and should not reconnect
return (server.isConnected() || server._shouldConnect)
// Pre-filter servers that do not contain the ledger in request
&& (!self.message.hasOwnProperty('ledger_index')
|| server.hasLedger(self.message.ledger_index))
&& (!self.message.hasOwnProperty('ledger_index_min')
|| self.message.ledger_index_min === -1
|| server.hasLedger(self.message.ledger_index_min))
&& (!self.message.hasOwnProperty('ledger_index_max')
|| self.message.ledger_index_max === -1
|| server.hasLedger(self.message.ledger_index_max))
});
// Apply iterator in parallel to connected servers, complete when the
// supplied filter function is satisfied once by a server's response
async.some(servers, iterator, complete);
return this;
};
Request.prototype.cancel = function() {
this.removeAllListeners();
this.on('error', function(){});
return this;
};
Request.prototype.setCallback = function(fn) {
if (typeof fn === 'function') {
this.callback(fn);
}
return this;
};
Request.prototype.setReconnectTimeout = function(timeout) {
if (typeof timeout === 'number' && !isNaN(timeout)) {
this.reconnectTimeout = timeout;
}
return this;
};
@@ -73,6 +197,13 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
return this;
}
if (typeof successEvent === 'string') {
this.successEvent = successEvent;
}
if (typeof errorEvent === 'string') {
this.errorEvent = errorEvent;
}
var called = false;
function requestSuccess(message) {
@@ -94,8 +225,8 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
}
};
this.once(successEvent || 'success', requestSuccess);
this.once(errorEvent || 'error' , requestError);
this.once(this.successEvent, requestSuccess);
this.once(this.errorEvent, requestError);
this.request();
return this;
@@ -124,6 +255,7 @@ Request.prototype.timeout = function(duration, callback) {
}
emit.call(self, 'timeout');
self.cancel();
}, duration);
this.emit = function() {
@@ -157,9 +289,7 @@ Request.prototype.setServer = function(server) {
break;
};
if (selected instanceof Server) {
this.server = selected;
}
this.server = selected;
return this;
};
@@ -222,11 +352,10 @@ Request.prototype.ledgerSelect = function(ledger) {
case 'validated':
this.message.ledger_index = ledger;
break;
default:
if (Number(ledger)) {
if (Number(ledger) && isFinite(Number(ledger))) {
this.message.ledger_index = Number(ledger);
} else if (/^[A-F0-9]+$/.test(ledger)) {
} else if (/^[A-F0-9]{64}$/.test(ledger)) {
this.message.ledger_hash = ledger;
}
break;
@@ -236,12 +365,12 @@ Request.prototype.ledgerSelect = function(ledger) {
};
Request.prototype.accountRoot = function(account) {
this.message.account_root = UInt160.json_rewrite(account);
this.message.account_root = UInt160.json_rewrite(account);
return this;
};
Request.prototype.index = function(index) {
this.message.index = index;
this.message.index = index;
return this;
};
@@ -250,8 +379,8 @@ Request.prototype.index = function(index) {
// --> seq : sequence number of transaction creating offer (integer)
Request.prototype.offerId = function(account, sequence) {
this.message.offer = {
account: UInt160.json_rewrite(account),
seq: sequence
account: UInt160.json_rewrite(account),
seq: sequence
};
return this;
};
@@ -286,8 +415,8 @@ Request.prototype.txBlob = function(json) {
Request.prototype.rippleState = function(account, issuer, currency) {
this.message.ripple_state = {
currency : currency,
accounts : [
currency: currency,
accounts: [
UInt160.json_rewrite(account),
UInt160.json_rewrite(issuer)
]
@@ -322,12 +451,8 @@ Request.prototype.addAccount = function(account, proposed) {
}
var processedAccount = UInt160.json_rewrite(account);
if (proposed === true) {
this.message.accounts_proposed = (this.message.accounts_proposed || []).concat(processedAccount);
} else {
this.message.accounts = (this.message.accounts || []).concat(processedAccount);
}
var prop = proposed === true ? 'accounts_proposed' : 'accounts';
this.message[prop] = (this.message[prop] || []).concat(processedAccount);
return this;
};

View File

@@ -1,133 +0,0 @@
var request = require('superagent');
var Currency = require('./currency').Currency;
var RippleTxt = {
txts : { }
};
RippleTxt.urlTemplates = [
'https://{{domain}}/ripple.txt',
'https://www.{{domain}}/ripple.txt',
'https://ripple.{{domain}}/ripple.txt',
'http://{{domain}}/ripple.txt',
'http://www.{{domain}}/ripple.txt',
'http://ripple.{{domain}}/ripple.txt'
];
/**
* Gets the ripple.txt file for the given domain
* @param {string} domain - Domain to retrieve file from
* @param {function} fn - Callback function
*/
RippleTxt.get = function(domain, fn) {
var self = this;
if (self.txts[domain]) {
return fn(null, self.txts[domain]);
}
;(function nextUrl(i) {
var url = RippleTxt.urlTemplates[i];
if (!url) {
return fn(new Error('No ripple.txt found'));
}
url = url.replace('{{domain}}', domain);
request.get(url, function(err, resp) {
if (err || !resp.text) {
return nextUrl(++i);
}
var sections = self.parse(resp.text);
self.txts[domain] = sections;
fn(null, sections);
});
})(0);
};
/**
* Parse a ripple.txt file
* @param {string} txt - Unparsed ripple.txt data
*/
RippleTxt.parse = function(txt) {
var currentSection = '';
var sections = { };
txt = txt.replace(/\r?\n/g, '\n').split('\n');
for (var i = 0, l = txt.length; i < l; i++) {
var line = txt[i];
if (!line.length || line[0] === '#') {
continue;
}
if (line[0] === '[' && line[line.length - 1] === ']') {
currentSection = line.slice(1, line.length - 1);
sections[currentSection] = [];
} else {
line = line.replace(/^\s+|\s+$/g, '');
if (sections[currentSection]) {
sections[currentSection].push(line);
}
}
}
return sections;
};
/**
* extractDomain
* attempt to extract the domain from a given url
* returns the url if unsuccessful
* @param {Object} url
*/
RippleTxt.extractDomain = function (url) {
match = /[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?([^.\?][^\?.]+?)?$/.exec(url);
return match && match[0] ? match[0] : url;
};
/**
* getCurrencies
* returns domain, issuer account and currency object
* for each currency found in the domain's ripple.txt file
* @param {Object} domain
* @param {Object} fn
*/
RippleTxt.getCurrencies = function(domain, fn) {
domain = RippleTxt.extractDomain(domain);
this.get(domain, function(err, txt) {
if (err) {
return fn(err);
}
if (err || !txt.currencies || !txt.accounts) {
return fn(null, []);
}
//NOTE: this won't be accurate if there are
//multiple issuer accounts with different
//currencies associated with each.
var issuer = txt.accounts[0];
var currencies = [];
txt.currencies.forEach(function(currency) {
currencies.push({
issuer : issuer,
currency : Currency.from_json(currency),
domain : domain
});
});
fn(null, currencies);
});
};
exports.RippleTxt = RippleTxt;

View File

@@ -6,8 +6,6 @@ var extend = require('extend');
var utils = require('./utils');
var sjcl = utils.sjcl;
var BigInteger = utils.jsbn.BigInteger;
var Base = require('./base').Base;
var UInt = require('./uint').UInt;
var UInt256 = require('./uint256').UInt256;
@@ -15,7 +13,6 @@ var UInt160 = require('./uint160').UInt160;
var KeyPair = require('./keypair').KeyPair;
var Seed = extend(function () {
// Internal form: NaN or BigInteger
this._curve = sjcl.ecc.curves.k256;
this._value = NaN;
}, UInt);
@@ -60,7 +57,7 @@ Seed.prototype.parse_passphrase = function (j) {
};
Seed.prototype.to_json = function () {
if (!(this._value instanceof BigInteger)) {
if (!(this.is_valid())) {
return NaN;
}

View File

@@ -1,13 +1,12 @@
var _ = require('lodash');
var assert = require('assert');
var extend = require('extend');
var binformat = require('./binformat');
var stypes = require('./serializedtypes');
var UInt256 = require('./uint256').UInt256;
var Crypt = require('./crypt').Crypt;
var utils = require('./utils');
var sjcl = utils.sjcl;
var BigInteger = utils.jsbn.BigInteger;
var TRANSACTION_TYPES = { };
@@ -27,6 +26,13 @@ Object.keys(binformat.ter).forEach(function(key) {
TRANSACTION_RESULTS[binformat.ter[key]] = 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'; });
return i >= 0 ? hex.slice(i) : '0';
}
function SerializedObject(buf) {
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf)) ) {
this.buffer = buf;
@@ -38,11 +44,11 @@ function SerializedObject(buf) {
throw new Error('Invalid buffer passed.');
}
this.pointer = 0;
};
}
SerializedObject.from_json = function(obj) {
// Create a copy of the object so we don't modify it
var obj = extend(true, {}, obj);
obj = extend(true, {}, obj);
var so = new SerializedObject();
var typedef;
@@ -103,8 +109,8 @@ SerializedObject.check_no_missing_fields = function(typedef, obj) {
if (binformat.REQUIRED === requirement && obj[field] === void(0)) {
missing_fields.push(field);
};
};
}
}
if (missing_fields.length > 0) {
var object_name;
@@ -114,12 +120,12 @@ SerializedObject.check_no_missing_fields = function(typedef, obj) {
} else if (obj.LedgerEntryType != null){
object_name = SerializedObject.lookup_type_le(obj.LedgerEntryType);
} else {
object_name = "TransactionMetaData";
object_name = 'TransactionMetaData';
}
throw new Error(object_name + " is missing fields: " +
throw new Error(object_name + ' is missing fields: ' +
JSON.stringify(missing_fields));
};
}
};
SerializedObject.prototype.append = function(bytes) {
@@ -152,7 +158,7 @@ function readOrPeek(advance) {
return result;
};
};
}
SerializedObject.prototype.read = readOrPeek(true);
@@ -209,8 +215,8 @@ SerializedObject.jsonify_structure = function(structure, field_name) {
if (typeof structure.to_json === 'function') {
output = structure.to_json();
} else if (structure instanceof BigInteger) {
output = structure.toString(16).toUpperCase();
} else if (structure instanceof sjcl.bn) {
output = ('0000000000000000' + normalize_sjcl_bn_hex(structure.toString()).toUpperCase()).slice(-16);
} else {
//new Array or Object
output = new structure.constructor();
@@ -248,15 +254,15 @@ SerializedObject.prototype.serialize = function(typedef, obj) {
SerializedObject.prototype.hash = function(prefix) {
var sign_buffer = new SerializedObject();
// Add hashing prefix
if ("undefined" !== typeof prefix) {
if ('undefined' !== typeof prefix) {
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);
@@ -312,7 +318,7 @@ SerializedObject.sort_typedef = function(typedef) {
function sort_field_compare(a, b) {
// Sort by type id first, then by field id
return a[3] !== b[3] ? stypes[a[3]].id - stypes[b[3]].id : a[2] - b[2];
};
}
return typedef.sort(sort_field_compare);
};

View File

@@ -11,6 +11,7 @@ var extend = require('extend');
var binformat = require('./binformat');
var utils = require('./utils');
var sjcl = utils.sjcl;
var BigNumber = require('./bignumber');
var UInt128 = require('./uint128').UInt128;
var UInt160 = require('./uint160').UInt160;
@@ -21,59 +22,44 @@ var amount = require('./amount');
var Amount = amount.Amount;
var Currency = amount.Currency;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
var utf8 = sjcl.codec.utf8String;
var BigInteger = utils.jsbn.BigInteger;
var SerializedType = function (methods) {
extend(this, methods);
};
function isNumber(val) {
return typeof val === 'number' && isFinite(val);
};
}
function isString(val) {
return typeof val === 'string';
};
}
function isHexInt64String(val) {
return isString(val) && /^[0-9A-F]{0,16}$/i.test(val);
};
}
function isCurrencyString(val) {
return isString(val) && /^[A-Z0-9]{3}$/.test(val);
};
function isBigInteger(val) {
return val instanceof BigInteger;
};
function serializeHex(so, hexData, noLength) {
var byteData = bytes.fromBits(hex.toBits(hexData));
function serializeBits(so, bits, noLength) {
var byteData = sjcl.codec.bytes.fromBits(bits);
if (!noLength) {
SerializedType.serialize_varint(so, byteData.length);
}
so.append(byteData);
};
}
function serializeHex(so, hexData, noLength) {
serializeBits(so, sjcl.codec.hex.toBits(hexData), noLength);
}
/**
* parses bytes as hex
*/
function convertByteArrayToHex (byte_array) {
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array)).toUpperCase();
};
function convertStringToHex(string) {
return hex.fromBits(utf8.toBits(string)).toUpperCase();
}
function convertHexToString(hexString) {
return utf8.fromBits(hex.toBits(hexString));
var bits = sjcl.codec.hex.toBits(hexString);
return sjcl.codec.utf8String.fromBits(bits);
}
SerializedType.serialize_varint = function (so, val) {
@@ -130,7 +116,7 @@ function convertIntegerToByteArray(val, bytes) {
}
if (val < 0 || val >= Math.pow(256, bytes)) {
throw new Error('Value out of bounds');
throw new Error('Value out of bounds ');
}
var newBytes = [ ];
@@ -140,7 +126,7 @@ function convertIntegerToByteArray(val, bytes) {
}
return newBytes;
};
}
// Convert a certain number of bytes from the serialized object ('so') into an integer.
function readAndSum(so, bytes) {
@@ -157,7 +143,7 @@ function readAndSum(so, bytes) {
// Convert to unsigned integer
return sum >>> 0;
};
}
var STInt8 = exports.Int8 = new SerializedType({
serialize: function (so, val) {
@@ -201,40 +187,25 @@ var STInt64 = exports.Int64 = new SerializedType({
if (val < 0) {
throw new Error('Negative value for unsigned Int64 is invalid.');
}
bigNumObject = new BigInteger(String(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 BigInteger(val, 16);
} else if (isBigInteger(val)) {
if (val.compareTo(BigInteger.ZERO) < 0) {
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.');
}
bigNumObject = val;
} else {
throw new Error('Invalid type for Int64');
}
var hex = bigNumObject.toString(16);
if (hex.length > 16) {
throw new Error('Int64 is too large');
}
while (hex.length < 16) {
hex = '0' + hex;
}
serializeHex(so, hex, true); //noLength = true
serializeBits(so, bigNumObject.toBits(64), true); //noLength = true
},
parse: function (so) {
var bytes = so.read(8);
// We need to add a 0, so if the high bit is set it won't think it's a
// pessimistic numeric fraek. What doth lief?
var result = new BigInteger([0].concat(bytes), 256);
assert(result instanceof BigInteger);
return result;
return sjcl.bn.fromBits(sjcl.codec.bytes.toBits(bytes));
}
});
@@ -246,7 +217,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash128');
}
serializeHex(so, hash.to_hex(), true); //noLength = true
serializeBits(so, hash.to_bits(), true); //noLength = true
},
parse: function (so) {
return UInt128.from_bytes(so.read(16));
@@ -261,7 +232,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash256');
}
serializeHex(so, hash.to_hex(), true); //noLength = true
serializeBits(so, hash.to_bits(), true); //noLength = true
},
parse: function (so) {
return UInt256.from_bytes(so.read(32));
@@ -276,7 +247,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash160');
}
serializeHex(so, hash.to_hex(), true); //noLength = true
serializeBits(so, hash.to_bits(), true); //noLength = true
},
parse: function (so) {
return UInt160.from_bytes(so.read(20));
@@ -287,7 +258,7 @@ STHash160.id = 17;
// Internal
var STCurrency = new SerializedType({
serialize: function (so, val, xrp_as_ascii) {
serialize: function (so, val) {
var currencyData = val.to_bytes();
if (!currencyData) {
@@ -316,11 +287,18 @@ var STAmount = exports.Amount = new SerializedType({
throw new Error('Not a valid Amount object.');
}
var value = new BigNumber(amount.to_text());
var offset = value.e - 15;
// Amount (64-bit integer)
var valueBytes = utils.arraySet(8, 0);
if (amount.is_native()) {
var valueHex = amount._value.toString(16);
var valueHex = value.abs().toString(16);
if (value.abs().greaterThan(Amount.bi_xns_max)) {
throw new Error('Value out of bounds');
}
// Enforce correct length (64 bits)
if (valueHex.length > 16) {
@@ -331,7 +309,7 @@ var STAmount = exports.Amount = new SerializedType({
valueHex = '0' + valueHex;
}
valueBytes = bytes.fromBits(hex.toBits(valueHex));
valueBytes = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(valueHex));
// Clear most significant two bits - these bits should already be 0 if
// Amount enforces the range correctly, but we'll clear them anyway just
// so this code can make certain guarantees about the encoded value.
@@ -353,10 +331,16 @@ var STAmount = exports.Amount = new SerializedType({
}
// Next eight bits: offset/exponent
hi |= ((97 + amount._offset) & 0xff) << 22;
hi |= ((97 + offset) & 0xff) << 22;
// Remaining 54 bits: mantissa
hi |= amount._value.shiftRight(32).intValue() & 0x3fffff;
lo = amount._value.intValue() & 0xffffffff;
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) & 0x3fffff;
lo = parseInt(mantissaHex.slice(-8), 16);
}
valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
@@ -374,7 +358,6 @@ var STAmount = exports.Amount = new SerializedType({
}
},
parse: function (so) {
var amount = new Amount();
var value_bytes = so.read(8);
var is_zero = !(value_bytes[0] & 0x7f);
@@ -382,6 +365,8 @@ var STAmount = exports.Amount = new SerializedType({
is_zero = is_zero && !value_bytes[i];
}
var is_negative = !is_zero && !(value_bytes[0] & 0x40);
if (value_bytes[0] & 0x80) {
//non-native
var currency = STCurrency.parse(so);
@@ -391,26 +376,23 @@ var STAmount = exports.Amount = new SerializedType({
var offset = ((value_bytes[0] & 0x3f) << 2) + (value_bytes[1] >>> 6) - 97;
var mantissa_bytes = value_bytes.slice(1);
mantissa_bytes[0] &= 0x3f;
var value = new BigInteger(mantissa_bytes, 256);
var mantissa = new BigNumber(utils.arrayToHex(mantissa_bytes), 16);
var sign = is_negative ? '-' : '';
var valueString = sign + mantissa.toString() + 'e' + offset.toString();
if (value.equals(BigInteger.ZERO) && !is_zero ) {
throw new Error('Invalid zero representation');
}
amount._value = value;
amount._offset = offset;
amount._currency = currency;
amount._issuer = issuer;
amount._is_native = false;
return Amount.from_json({
currency: currency,
issuer: issuer.to_json(),
value: valueString
});
} else {
//native
var integer_bytes = value_bytes.slice();
integer_bytes[0] &= 0x3f;
amount._value = new BigInteger(integer_bytes, 256);
amount._is_native = true;
var integer_hex = utils.arrayToHex(integer_bytes);
var value = new BigNumber(integer_hex, 16);
return Amount.from_json((is_negative ? '-' : '') + value.toString());
}
amount._is_negative = !is_zero && !(value_bytes[0] & 0x40);
return amount;
}
});
@@ -439,7 +421,7 @@ var STAccount = exports.Account = new SerializedType({
if (!account.is_valid()) {
throw new Error('Invalid account!');
}
serializeHex(so, account.to_hex());
serializeBits(so, account.to_bits());
},
parse: function (so) {
var len = this.parse_varint(so);
@@ -492,7 +474,7 @@ var STPathSet = exports.PathSet = new SerializedType({
STInt8.serialize(so, type);
if (entry.account) {
so.append(UInt160.from_json(entry.account).to_bytes());
STHash160.serialize(so, entry.account);
}
if (entry.currency) {
@@ -501,7 +483,7 @@ var STPathSet = exports.PathSet = new SerializedType({
}
if (entry.issuer) {
so.append(UInt160.from_json(entry.issuer).to_bytes());
STHash160.serialize(so, entry.issuer);
}
}
}
@@ -541,6 +523,7 @@ var STPathSet = exports.PathSet = new SerializedType({
//It's an entry-begin tag.
//console.log('It's an entry-begin tag.');
var entry = {};
var type = 0;
if (tag_byte & this.typeAccount) {
//console.log('entry.account');
@@ -548,6 +531,7 @@ var STPathSet = exports.PathSet = new SerializedType({
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');
@@ -555,6 +539,7 @@ var STPathSet = exports.PathSet = new SerializedType({
if (entry.currency.to_json() === 'XRP' && !entry.currency.is_native()) {
entry.non_native = true;
}
type = type | this.typeCurrency;
}
if (tag_byte & this.typeIssuer) {
//console.log('entry.issuer');
@@ -562,9 +547,14 @@ var STPathSet = exports.PathSet = new SerializedType({
// 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.
@@ -584,7 +574,7 @@ STPathSet.id = 18;
var STVector256 = exports.Vector256 = new SerializedType({
serialize: function (so, val) { //Assume val is an array of STHash256 objects.
var length_as_varint = SerializedType.serialize_varint(so, val.length * 32);
SerializedType.serialize_varint(so, val.length * 32);
for (var i=0, l=val.length; i<l; i++) {
STHash256.serialize(so, val[i]);
}
@@ -603,7 +593,7 @@ var STVector256 = exports.Vector256 = new SerializedType({
STVector256.id = 19;
// Internal
var STMemo = exports.STMemo = new SerializedType({
exports.STMemo = new SerializedType({
serialize: function(so, val, no_marker) {
var keys = [];
@@ -625,38 +615,9 @@ var STMemo = exports.STMemo = new SerializedType({
// Sort fields
keys = sort_fields(keys);
// store that we're dealing with json
var isJson = val.MemoFormat === 'json';
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = val[key];
switch (key) {
// MemoType and MemoFormat are always ASCII strings
case 'MemoType':
case 'MemoFormat':
value = convertStringToHex(value);
break;
// MemoData can be a JSON object, otherwise it's a string
case 'MemoData':
if (typeof value !== 'string') {
if (isJson) {
try {
value = convertStringToHex(JSON.stringify(value));
} catch (e) {
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');
}
} else if (isString(value)) {
value = convertStringToHex(value);
}
break;
}
serialize(so, key, value);
}
@@ -673,25 +634,42 @@ var STMemo = exports.STMemo = new SerializedType({
output[keyval[0]] = keyval[1];
}
if (output['MemoType'] !== void(0)) {
output['parsed_memo_type'] = convertHexToString(output['MemoType']);
}
if (output.MemoType !== void(0)) {
try {
var parsedType = convertHexToString(output.MemoType);
if (output['MemoFormat'] !== void(0)) {
output['parsed_memo_format'] = convertHexToString(output['MemoFormat']);
}
if (output['MemoData'] !== void(0)) {
// see if we can parse JSON
if (output['parsed_memo_format'] === 'json') {
try {
output['parsed_memo_data'] = JSON.parse(convertHexToString(output['MemoData']));
} catch(e) {
// fail, which is fine, we just won't add the memo_data field
if (parsedType !== 'unformatted_memo') {
output.parsed_memo_type = parsedType;
}
} else if(output['parsed_memo_format'] === 'text') {
output['parsed_memo_data'] = convertHexToString(output['MemoData']);
} catch (e) {
// 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)) {
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
// this is fine, we won't add the parsed_memo_format field
}
}
if (output.MemoData !== void(0)) {
try {
if (output.parsed_memo_format === 'json') {
// see if we can parse JSON
output.parsed_memo_data = JSON.parse(convertHexToString(output.MemoData));
} 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
}
}
@@ -775,7 +753,7 @@ function parse(so) {
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) {
@@ -788,7 +766,7 @@ function sort_fields(keys) {
// 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);
}

View File

@@ -1,7 +1,9 @@
var util = require('util');
var url = require('url');
var LRU = require('lru-cache');
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount').Amount;
var RangeSet = require('./rangeset').RangeSet;
var log = require('./log').internal.sub('server');
/**
@@ -32,7 +34,7 @@ function Server(remote, opts) {
throw new TypeError('Server configuration is not an Object');
}
if (!Server.domainRE.test(opts.host)) {
if (!Server.DOMAIN_RE.test(opts.host)) {
throw new Error('Server host is malformed, use "host" and "port" server configuration');
}
@@ -56,6 +58,8 @@ function Server(remote, opts) {
this._connected = false;
this._shouldConnect = false;
this._state = 'offline';
this._ledgerRanges = new RangeSet();
this._ledgerMap = LRU({ max: 200 });
this._id = 0; // request ID
this._retry = 0;
@@ -117,31 +121,29 @@ function Server(remote, opts) {
self._updateScore('loadchange', load);
});
// If server is not up-to-date, request server_info
// for getting pubkey_node & hostid information.
// Otherwise this information is available on the
// initial server subscribe response
this.on('connect', function requestServerID() {
if (self._pubkey_node) {
return;
}
self.on('response_server_info', function setServerID(message) {
try {
self._pubkey_node = message.info.pubkey_node;
} catch (e) {
}
});
var serverInfoRequest = self._remote.requestServerInfo();
serverInfoRequest.on('error', function() { });
self._request(serverInfoRequest);
this.on('connect', function() {
self.requestServerID();
});
};
util.inherits(Server, EventEmitter);
Server.domainRE = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|[-_]){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|[-_]){0,61}[0-9A-Za-z])?)*\.?$/;
Server.DOMAIN_RE = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|[-_]){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|[-_]){0,61}[0-9A-Za-z])?)*\.?$/;
Server.TLS_ERRORS = [
'UNABLE_TO_GET_ISSUER_CERT', 'UNABLE_TO_GET_CRL',
'UNABLE_TO_DECRYPT_CERT_SIGNATURE', 'UNABLE_TO_DECRYPT_CRL_SIGNATURE',
'UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY', 'CERT_SIGNATURE_FAILURE',
'CRL_SIGNATURE_FAILURE', 'CERT_NOT_YET_VALID', 'CERT_HAS_EXPIRED',
'CRL_NOT_YET_VALID', 'CRL_HAS_EXPIRED', 'ERROR_IN_CERT_NOT_BEFORE_FIELD',
'ERROR_IN_CERT_NOT_AFTER_FIELD', 'ERROR_IN_CRL_LAST_UPDATE_FIELD',
'ERROR_IN_CRL_NEXT_UPDATE_FIELD', 'OUT_OF_MEM',
'DEPTH_ZERO_SELF_SIGNED_CERT', 'SELF_SIGNED_CERT_IN_CHAIN',
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY', 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
'CERT_CHAIN_TOO_LONG', 'CERT_REVOKED', 'INVALID_CA',
'PATH_LENGTH_EXCEEDED', 'INVALID_PURPOSE', 'CERT_UNTRUSTED',
'CERT_REJECTED'
];
/**
* Server states that we will treat as the server being online.
@@ -235,6 +237,31 @@ Server.prototype._checkActivity = function() {
}
};
/**
* If server is not up-to-date, request server_info for getting pubkey_node
* & hostid information. Otherwise this information is available on the
* initial server subscribe response
*/
Server.prototype.requestServerID = function() {
var self = this;
if (this._pubkey_node) {
return;
}
this.on('response_server_info', function setServerID(message) {
try {
self._pubkey_node = message.info.pubkey_node;
} catch (e) {
}
});
var serverInfoRequest = this._remote.requestServerInfo();
serverInfoRequest.on('error', function() { });
this._request(serverInfoRequest);
};
/**
* Server maintains a score for request prioritization.
*
@@ -328,6 +355,8 @@ Server.prototype.disconnect = function() {
this._lastLedgerClose = NaN;
this._score = 0;
this._shouldConnect = false;
this._ledgerRanges.reset();
this._ledgerMap.reset();
this._setState('offline');
if (this._ws) {
@@ -420,6 +449,11 @@ Server.prototype.connect = function() {
log.info(self.getServerID(), 'onerror:', e.data || e);
}
if (Server.TLS_ERRORS.indexOf(e.message) !== -1) {
// Unrecoverable
throw e;
}
// Most connection errors for WebSockets are conveyed as 'close' events with
// code 1006. This is done for security purposes and therefore unlikely to
// ever change.
@@ -545,6 +579,8 @@ Server.prototype._handleMessage = function(message) {
Server.prototype._handleLedgerClosed = function(message) {
this._lastLedgerIndex = message.ledger_index;
this._lastLedgerClose = Date.now();
this._ledgerRanges.add(message.ledger_index);
this._ledgerMap.set(message.ledger_hash, message.ledger_index);
this.emit('ledger_closed', message);
};
@@ -620,24 +656,33 @@ Server.prototype._handlePathFind = function(message) {
};
/**
* Handle subscription response messages. Subscription response
* messages indicate that a connection to the server is ready
* Handle initial subscription response message. The server is considered
* `connected` after it has received a response to initial subscription to
* ledger and server streams
*
* @param {Object} message
* @api private
*/
Server.prototype._handleResponseSubscribe = function(message) {
if (!this._remote._allow_partial_history
if (this.isConnected()) {
// This function only concerns initializing the server's internal
// state after a connection
return;
}
if (!this._remote.allow_partial_history
&& !Server.hasFullLedgerHistory(message)) {
// Server has partial history and Remote has been configured to disallow
// servers with incomplete history
return this.reconnect();
}
if (message.pubkey_node) {
// pubkey_node is used to identify the server
this._pubkey_node = message.pubkey_node;
}
if (Server.isLoadStatus(message)) {
this._load_base = message.load_base || 256;
this._load_factor = message.load_factor || 256;
@@ -646,6 +691,12 @@ Server.prototype._handleResponseSubscribe = function(message) {
this._reserve_base = message.reserve_base;
this._reserve_inc = message.reserve_inc;
}
if (message.validated_ledgers) {
// Add validated ledgers to ledger range set
this._ledgerRanges.add(message.validated_ledgers);
}
if (~Server.onlineStates.indexOf(message.server_status)) {
this._setState('online');
}
@@ -750,6 +801,12 @@ Server.prototype._request = function(request) {
}
};
/**
* Get server connected status
*
* @return boolean
*/
Server.prototype.isConnected =
Server.prototype._isConnected = function() {
return this._connected;
@@ -824,6 +881,36 @@ Server.prototype._reserve = function(ownerCount) {
return reserve_base.add(reserve_inc.product_human(owner_count));
};
/**
* Check that server has seen closed ledger
*
* @param {string|number} ledger hash or index
* @return boolean
*/
Server.prototype.hasLedger = function(ledger) {
var result = false;
if (typeof ledger === 'string' && /^[A-F0-9]{64}$/.test(ledger)) {
result = this._ledgerMap.has(ledger);
} else if (ledger != null && !isNaN(ledger)) {
result = this._ledgerRanges.has(ledger);
}
return result;
};
/**
* Get ledger index of last seen validated ledger
*
* @return number
*/
Server.prototype.getLastLedger =
Server.prototype.getLastLedgerIndex = function() {
return this._lastLedgerIndex;
};
exports.Server = Server;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,203 +0,0 @@
var Crypt = require('./crypt').Crypt;
var Message = require('./message').Message;
var parser = require("url");
var querystring = require('querystring');
var extend = require("extend");
var SignedRequest = function (config) {
// XXX Constructor should be generalized and constructing from an Angular.js
// $http config should be a SignedRequest.from... utility method.
this.config = extend(true, {}, config);
if (!this.config.data) this.config.data = {};
};
/**
* Create a string from request parameters that
* will be used to sign a request
* @param {Object} parsed - parsed url
* @param {Object} date
* @param {Object} mechanism - type of signing
*/
SignedRequest.prototype.getStringToSign = function (parsed, date, mechanism) {
// XXX This method doesn't handle signing GET requests correctly. The data
// field will be merged into the search string, not the request body.
// Sort the properties of the JSON object into canonical form
var canonicalData = JSON.stringify(copyObjectWithSortedKeys(this.config.data));
// Canonical request using Amazon's v4 signature format
// See: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
var canonicalRequest = [
this.config.method || 'GET',
parsed.pathname || '',
parsed.search || '',
// XXX Headers signing not supported
'',
'',
Crypt.hashSha512(canonicalData).toLowerCase()
].join('\n');
// String to sign inspired by Amazon's v4 signature format
// See: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
//
// We don't have a credential scope, so we skip it.
//
// But that modifies the format, so the format ID is RIPPLE1, instead of AWS4.
return [
mechanism,
date,
Crypt.hashSha512(canonicalRequest).toLowerCase()
].join('\n');
};
//prepare for signing
function copyObjectWithSortedKeys(object) {
if (isPlainObject(object)) {
var newObj = {};
var keysSorted = Object.keys(object).sort();
var key;
for (var i in keysSorted) {
key = keysSorted[i];
if (Object.prototype.hasOwnProperty.call(object, key)) {
newObj[key] = copyObjectWithSortedKeys(object[key]);
}
}
return newObj;
} else if (Array.isArray(object)) {
return object.map(copyObjectWithSortedKeys);
} else {
return object;
}
}
//from npm extend
function isPlainObject(obj) {
var hasOwn = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval)
return false;
var has_own_constructor = hasOwn.call(obj, 'constructor');
var has_is_property_of_method = hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
// Not own constructor property must be Object
if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
return false;
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || hasOwn.call( obj, key );
};
/**
* HMAC signed request
* @param {Object} config
* @param {Object} auth_secret
* @param {Object} blob_id
*/
SignedRequest.prototype.signHmac = function (auth_secret, blob_id) {
var config = extend(true, {}, this.config);
// Parse URL
var parsed = parser.parse(config.url);
var date = dateAsIso8601();
var signatureType = 'RIPPLE1-HMAC-SHA512';
var stringToSign = this.getStringToSign(parsed, date, signatureType);
var signature = Crypt.signString(auth_secret, stringToSign);
var query = querystring.stringify({
signature: Crypt.base64ToBase64Url(signature),
signature_date: date,
signature_blob_id: blob_id,
signature_type: signatureType
});
config.url += (parsed.search ? '&' : '?') + query;
return config;
};
/**
* Asymmetric signed request
* @param {Object} config
* @param {Object} secretKey
* @param {Object} account
* @param {Object} blob_id
*/
SignedRequest.prototype.signAsymmetric = function (secretKey, account, blob_id) {
var config = extend(true, {}, this.config);
// Parse URL
var parsed = parser.parse(config.url);
var date = dateAsIso8601();
var signatureType = 'RIPPLE1-ECDSA-SHA512';
var stringToSign = this.getStringToSign(parsed, date, signatureType);
var signature = Message.signMessage(stringToSign, secretKey);
var query = querystring.stringify({
signature: Crypt.base64ToBase64Url(signature),
signature_date: date,
signature_blob_id: blob_id,
signature_account: account,
signature_type: signatureType
});
config.url += (parsed.search ? '&' : '?') + query;
return config;
};
/**
* Asymmetric signed request for vault recovery
* @param {Object} config
* @param {Object} secretKey
* @param {Object} username
*/
SignedRequest.prototype.signAsymmetricRecovery = function (secretKey, username) {
var config = extend(true, {}, this.config);
// Parse URL
var parsed = parser.parse(config.url);
var date = dateAsIso8601();
var signatureType = 'RIPPLE1-ECDSA-SHA512';
var stringToSign = this.getStringToSign(parsed, date, signatureType);
var signature = Message.signMessage(stringToSign, secretKey);
var query = querystring.stringify({
signature: Crypt.base64ToBase64Url(signature),
signature_date: date,
signature_username: username,
signature_type: signatureType
});
config.url += (parsed.search ? '&' : '?') + query;
return config;
};
var dateAsIso8601 = (function () {
function pad(n) {
return (n < 0 || n > 9 ? "" : "0") + n;
}
return function dateAsIso8601() {
var date = new Date();
return date.getUTCFullYear() + "-" +
pad(date.getUTCMonth() + 1) + "-" +
pad(date.getUTCDate()) + "T" +
pad(date.getUTCHours()) + ":" +
pad(date.getUTCMinutes()) + ":" +
pad(date.getUTCSeconds()) + ".000Z";
};
})();
// XXX Add methods for verifying requests
// SignedRequest.prototype.verifySignatureHmac
// SignedRequest.prototype.verifySignatureAsymetric
exports.SignedRequest = SignedRequest;

View File

@@ -11,6 +11,7 @@ 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');
/**
* @constructor Transaction
@@ -131,6 +132,7 @@ Transaction.set_clear_flags = {
asfRequireAuth: 2,
asfDisallowXRP: 3,
asfDisableMaster: 4,
asfAccountTxnID: 5,
asfNoFreeze: 6,
asfGlobalFreeze: 7
}
@@ -833,24 +835,40 @@ Transaction.prototype.addMemo = function(memoType, memoFormat, memoData) {
throw new Error('MemoFormat must be valid ASCII');
}
function convertStringToHex(string) {
var utf8String = sjcl.codec.utf8String.toBits(string);
return sjcl.codec.hex.fromBits(utf8String).toUpperCase();
}
var memo = {};
if (memoType) {
if (Transaction.MEMO_TYPES[memoType]) {
//XXX Maybe in the future we want a schema validator for
//memo types
memo.MemoType = Transaction.MEMO_TYPES[memoType];
} else {
memo.MemoType = memoType;
memoType = Transaction.MEMO_TYPES[memoType];
}
memo.MemoType = convertStringToHex(memoType);
}
if (memoFormat) {
memo.MemoFormat = memoFormat;
memo.MemoFormat = convertStringToHex(memoFormat);
}
if (memoData) {
memo.MemoData = memoData;
if (typeof memoData !== 'string') {
if (memoFormat === 'json') {
try {
memoData = JSON.stringify(memoData);
} catch (e) {
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');
}
}
memo.MemoData = convertStringToHex(memoData);
}
this.tx_json.Memos = (this.tx_json.Memos || []).concat({ Memo: memo });
@@ -858,6 +876,15 @@ Transaction.prototype.addMemo = function(memoType, memoFormat, memoData) {
return this;
};
Transaction.prototype.setAccountTxnID =
Transaction.prototype.accountTxnID = function(id) {
if (typeof id === 'string') {
this.tx_json.AccountTxnID = id;
}
return this;
};
/**
* Construct an 'AccountSet' transaction
*

View File

@@ -23,8 +23,8 @@ function TransactionManager(account) {
this._nextSequence = void(0);
this._maxFee = this._remote.max_fee;
this._maxAttempts = this._remote.max_attempts;
this._submissionTimeout = this._remote._submission_timeout;
this._lastLedgerOffset = this._remote._last_ledger_offset;
this._submissionTimeout = this._remote.submission_timeout;
this._lastLedgerOffset = this._remote.last_ledger_offset;
this._pending = new PendingQueue();
this._account.on('transaction-outbound', function(res) {
@@ -439,7 +439,13 @@ TransactionManager.prototype._resubmit = function(ledgers, pending) {
var received = transaction.findId(self._pending._idCache);
if (received) {
return transaction.emit('success', received);
switch (received.engine_result) {
case 'tesSUCCESS':
transaction.emit('success', received);
break;
default:
transaction.emit('error', received);
}
}
while (self._pending.hasSequence(transaction.tx_json.Sequence)) {
@@ -650,7 +656,7 @@ TransactionManager.prototype._request = function(tx) {
tx.emit('presubmit');
tx.submissions = submitRequest.broadcast();
submitRequest.broadcast().request();
tx.attempts++;
tx.emit('postsubmit');

View File

@@ -2,8 +2,6 @@ var utils = require('./utils');
var sjcl = utils.sjcl;
var config = require('./config');
var BigInteger = utils.jsbn.BigInteger;
//
// Abstract UInt class
//
@@ -11,9 +9,8 @@ var BigInteger = utils.jsbn.BigInteger;
//
var UInt = function() {
// Internal form: NaN or BigInteger
// Internal form: NaN or sjcl.bn
this._value = NaN;
this._update();
};
@@ -96,6 +93,10 @@ UInt.prototype.clone = function() {
UInt.prototype.copyTo = function(d) {
d._value = this._value;
if (this._version_byte !== void(0)) {
d._version_byte = this._version_byte;
}
if (typeof d._update === 'function') {
d._update();
}
@@ -104,15 +105,15 @@ UInt.prototype.copyTo = function(d) {
};
UInt.prototype.equals = function(d) {
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
return this.is_valid() && d.is_valid() && this._value.equals(d._value);
};
UInt.prototype.is_valid = function() {
return this._value instanceof BigInteger;
return this._value instanceof sjcl.bn;
};
UInt.prototype.is_zero = function() {
return this._value.equals(BigInteger.ZERO);
return this.is_valid() && this._value.equals(new sjcl.bn(0));
};
/**
@@ -142,24 +143,25 @@ UInt.prototype.parse_generic = function(j) {
case this.constructor.STR_ZERO:
case this.constructor.ACCOUNT_ZERO:
case this.constructor.HEX_ZERO:
this._value = BigInteger.valueOf();
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 BigInteger([1]);
this._value = new sjcl.bn(1);
break;
default:
if (typeof j !== 'string') {
this._value = NaN;
} else if (this.constructor.width === j.length) {
this._value = new BigInteger(utils.stringToArray(j), 256);
var hex = utils.arrayToHex(utils.stringToArray(j));
this._value = new sjcl.bn(hex, 16);
} else if ((this.constructor.width * 2) === j.length) {
// XXX Check char set!
this._value = new BigInteger(j, 16);
this._value = new sjcl.bn(j, 16);
} else {
this._value = NaN;
}
@@ -172,7 +174,7 @@ UInt.prototype.parse_generic = function(j) {
UInt.prototype.parse_hex = function(j) {
if (typeof j === 'string' && j.length === (this.constructor.width * 2)) {
this._value = new BigInteger(j, 16);
this._value = new sjcl.bn(j, 16);
} else {
this._value = NaN;
}
@@ -186,8 +188,9 @@ UInt.prototype.parse_bits = function(j) {
if (sjcl.bitArray.bitLength(j) !== this.constructor.width * 8) {
this._value = NaN;
} else {
var bytes = sjcl.codec.bytes.fromBits(j);
this.parse_bytes(bytes);
this._value = sjcl.bn.fromBits(j);
// var bytes = sjcl.codec.bytes.fromBits(j);
// this.parse_bytes(bytes);
}
this._update();
@@ -200,7 +203,8 @@ UInt.prototype.parse_bytes = function(j) {
if (!Array.isArray(j) || j.length !== this.constructor.width) {
this._value = NaN;
} else {
this._value = new BigInteger([0].concat(j), 256);
var bits = sjcl.codec.bytes.toBits(j);
this._value = sjcl.bn.fromBits(bits);
}
this._update();
@@ -213,8 +217,9 @@ 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 BigInteger(bytes, 256);
// var bytes = sjcl.codec.bytes.fromBits(j.toBits());
// this._value = new sjcl.bn(utils.arrayToHex(bytes), 16);
this._value = new sjcl.bn(j);
} else {
this._value = NaN;
}
@@ -228,7 +233,7 @@ UInt.prototype.parse_number = function(j) {
this._value = NaN;
if (typeof j === 'number' && isFinite(j) && j >= 0) {
this._value = new BigInteger(String(j));
this._value = new sjcl.bn(j);
}
this._update();
@@ -238,51 +243,31 @@ UInt.prototype.parse_number = function(j) {
// Convert from internal form.
UInt.prototype.to_bytes = function() {
if (!(this._value instanceof BigInteger)) {
if (!this.is_valid()) {
return null;
}
var bytes = this._value.toByteArray();
bytes = bytes.map(function(b) {
return (b + 256) % 256;
});
var target = this.constructor.width;
// XXX Make sure only trim off leading zeros.
bytes = bytes.slice(-target);
while (bytes.length < target) {
bytes.unshift(0);
}
return bytes;
return sjcl.codec.bytes.fromBits(this.to_bits());
};
UInt.prototype.to_hex = function() {
if (!(this._value instanceof BigInteger)) {
if (!this.is_valid()) {
return null;
}
var bytes = this.to_bytes();
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(bytes)).toUpperCase();
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
};
UInt.prototype.to_json = UInt.prototype.to_hex;
UInt.prototype.to_bits = function() {
if (!(this._value instanceof BigInteger)) {
if (!this.is_valid()) {
return null;
}
var bytes = this.to_bytes();
return sjcl.codec.bytes.toBits(bytes);
return this._value.toBits(this.constructor.width * 8);
};
UInt.prototype.to_bn = function() {
if (!(this._value instanceof BigInteger)) {
if (!this.is_valid()) {
return null;
}

View File

@@ -7,7 +7,6 @@ var UInt = require('./uint').UInt;
//
var UInt128 = extend(function () {
// Internal form: NaN or BigInteger
this._value = NaN;
}, UInt);

View File

@@ -2,8 +2,6 @@ var utils = require('./utils');
var config = require('./config');
var extend = require('extend');
var BigInteger = utils.jsbn.BigInteger;
var UInt = require('./uint').UInt;
var Base = require('./base').Base;
@@ -12,7 +10,6 @@ var Base = require('./base').Base;
//
var UInt160 = extend(function() {
// Internal form: NaN or BigInteger
this._value = NaN;
this._version_byte = void(0);
this._update();
@@ -49,7 +46,7 @@ UInt160.prototype.parse_json = function(j) {
// Allow raw numbers - DEPRECATED
// This is used mostly by the test suite and is supported
// as a legacy feature only. DO NOT RELY ON THIS BEHAVIOR.
this._value = new BigInteger(String(j));
this.parse_number(j);
this._version_byte = Base.VER_ACCOUNT_ID;
} else if (typeof j !== 'string') {
this._value = NaN;
@@ -83,7 +80,7 @@ UInt160.prototype.parse_generic = function(j) {
UInt160.prototype.to_json = function(opts) {
opts = opts || {};
if (this._value instanceof BigInteger) {
if (this.is_valid()) {
// If this value has a type, return a Base58 encoded string.
if (typeof this._version_byte === 'number') {
var output = Base.encode_check(this._version_byte, this.to_bytes());

View File

@@ -7,7 +7,6 @@ var UInt = require('./uint').UInt;
//
var UInt256 = extend(function() {
// Internal form: NaN or BigInteger
this._value = NaN;
}, UInt);

View File

@@ -1,3 +1,15 @@
function getMantissaDecimalString(bignum) {
var mantissa = bignum.toPrecision(16)
.replace(/\./, '') // remove decimal point
.replace(/e.*/, '') // remove scientific notation
.replace(/^0*/, ''); // remove leading zeroes
while (mantissa.length < 16) {
mantissa += '0'; // add trailing zeroes until length is 16
}
return mantissa;
}
function filterErr(code, done) {
return function(e) {
done(e.code !== code ? e : void(0));
@@ -69,6 +81,13 @@ function hexToArray(h) {
return stringToArray(hexToString(h));
};
function arrayToHex(a) {
return a.map(function(byteValue) {
var hex = byteValue.toString(16);
return hex.length > 1 ? hex : '0' + hex;
}).join('');
}
function chunkString(str, n, leftAlign) {
var ret = [];
var i=0, len=str.length;
@@ -144,15 +163,16 @@ 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.fromTimestamp = fromTimestamp;
exports.getMantissaDecimalString = getMantissaDecimalString;
// Going up three levels is needed to escape the src-cov folder used for the
// test coverage stuff.
exports.sjcl = require('../../../build/sjcl');
exports.jsbn = require('../../../src/js/jsbn/jsbn');
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,593 +0,0 @@
var async = require('async');
var blobClient = require('./blob').BlobClient;
var AuthInfo = require('./authinfo').AuthInfo;
var crypt = require('./crypt').Crypt;
var log = require('./log').sub('vault');
function VaultClient(opts) {
var self = this;
if (!opts) {
opts = { };
}
if (typeof opts === 'string') {
opts = { domain: opts };
}
this.domain = opts.domain || 'ripple.com';
this.infos = { };
};
/**
* getAuthInfo
* gets auth info for a username. returns authinfo
* even if user does not exists (with exist set to false)
* @param {string} username
* @param {function} callback
*/
VaultClient.prototype.getAuthInfo = function (username, callback) {
AuthInfo.get(this.domain, username, function(err, authInfo) {
if (err) {
return callback(err);
}
if (authInfo.version !== 3) {
return callback(new Error('This wallet is incompatible with this version of the vault-client.'));
}
if (!authInfo.pakdf) {
return callback(new Error('No settings for PAKDF in auth packet.'));
}
if (typeof authInfo.blobvault !== 'string') {
return callback(new Error('No blobvault specified in the authinfo.'));
}
callback(null, authInfo);
});
};
/**
* _deriveLoginKeys
* method designed for asnyc waterfall
*/
VaultClient.prototype._deriveLoginKeys = function (authInfo, password, callback) {
var normalizedUsername = authInfo.username.toLowerCase().replace(/-/g, '');
//derive login keys
crypt.derive(authInfo.pakdf, 'login', normalizedUsername, password, function(err, keys) {
if (err) {
callback(err);
} else {
callback(null, authInfo, password, keys);
}
});
};
/**
* _deriveUnlockKey
* method designed for asnyc waterfall
*/
VaultClient.prototype._deriveUnlockKey = function (authInfo, password, keys, callback) {
var normalizedUsername = authInfo.username.toLowerCase().replace(/-/g, '');
//derive unlock key
crypt.derive(authInfo.pakdf, 'unlock', normalizedUsername, password, function(err, unlock) {
if (err) {
log.error('derive:', err);
return callback(err);
}
if (!keys) {
keys = { };
}
keys.unlock = unlock.unlock;
callback(null, authInfo, keys);
});
};
/**
* Get a ripple name from a given account address, if it has one
* @param {string} address - Account address to query
* @param {string} url - Url of blob vault
*/
VaultClient.prototype.getRippleName = function(address, url, callback) {
//use the url from previously retrieved authInfo, if necessary
if (!url) {
callback(new Error('Blob vault URL is required'));
} else {
blobClient.getRippleName(url, address, callback);
}
};
/**
* Check blobvault for existance of username
*
* @param {string} username
* @param {function} fn - Callback function
*/
VaultClient.prototype.exists = function(username, callback) {
AuthInfo.get(this.domain, username.toLowerCase(), function(err, authInfo) {
if (err) {
callback(err);
} else {
callback(null, !!authInfo.exists);
}
});
};
/**
* Authenticate and retrieve a decrypted blob using a ripple name and password
*
* @param {string} username
* @param {string} password
* @param {function} fn - Callback function
*/
VaultClient.prototype.login = function(username, password, device_id, callback) {
var self = this;
var steps = [
getAuthInfo,
self._deriveLoginKeys,
getBlob
];
async.waterfall(steps, callback);
function getAuthInfo(callback) {
self.getAuthInfo(username, function(err, authInfo){
if (authInfo && !authInfo.exists) {
return callback(new Error('User does not exist.'));
}
return callback (err, authInfo, password);
});
}
function getBlob(authInfo, password, keys, callback) {
var options = {
url : authInfo.blobvault,
blob_id : keys.id,
key : keys.crypt,
device_id : device_id
};
blobClient.get(options, function(err, blob) {
if (err) {
return callback(err);
}
//save for relogin
self.infos[keys.id] = authInfo;
//migrate missing fields
if (blob.missing_fields) {
if (blob.missing_fields.encrypted_blobdecrypt_key) {
log.info('migration: saving encrypted blob decrypt key');
authInfo.blob = blob;
//get the key to unlock the secret, then update the blob keys
self._deriveUnlockKey(authInfo, password, keys, updateKeys);
}
}
callback(null, {
blob : blob,
username : authInfo.username,
verified : authInfo.emailVerified
});
});
};
function updateKeys (err, params, keys) {
if (err || !keys.unlock) {
return; //unable to unlock
}
var secret;
try {
secret = crypt.decrypt(keys.unlock, params.blob.encrypted_secret);
} catch (error) {
return log.error('decrypt:', error);
}
options = {
username : params.username,
blob : params.blob,
masterkey : secret,
keys : keys
};
blobClient.updateKeys(options, function(err, resp){
if (err) {
log.error('updateKeys:', err);
}
});
}
};
/**
* Retreive and decrypt blob using a blob url, id and crypt derived previously.
*
* @param {string} url - Blob vault url
* @param {string} id - Blob id from previously retreived blob
* @param {string} key - Blob decryption key
* @param {function} fn - Callback function
*/
VaultClient.prototype.relogin = function(url, id, key, device_id, callback) {
//use the url from previously retrieved authInfo, if necessary
if (!url && this.infos[id]) {
url = this.infos[id].blobvault;
}
if (!url) {
return callback(new Error('Blob vault URL is required'));
}
var options = {
url : url,
blob_id : id,
key : key,
device_id : device_id
};
blobClient.get(options, function(err, blob) {
if (err) {
callback(err);
} else {
callback (null, { blob: blob });
}
});
};
/**
* Decrypt the secret key using a username and password
*
* @param {string} username
* @param {string} password
* @param {string} encryptSecret
* @param {function} fn - Callback function
*/
VaultClient.prototype.unlock = function(username, password, encryptSecret, fn) {
var self = this;
var steps = [
getAuthInfo,
self._deriveUnlockKey,
unlockSecret
];
async.waterfall(steps, fn);
function getAuthInfo(callback) {
self.getAuthInfo(username, function(err, authInfo){
if (authInfo && !authInfo.exists) {
return callback(new Error('User does not exist.'));
}
return callback (err, authInfo, password, {});
});
}
function unlockSecret (authinfo, keys, callback) {
var secret;
try {
secret = crypt.decrypt(keys.unlock, encryptSecret);
} catch (error) {
return callback(error);
}
callback(null, {
keys : keys,
secret : secret
});
}
};
/**
* Retrieve the decrypted blob and secret key in one step using
* the username and password
*
* @param {string} username
* @param {string} password
* @param {function} fn - Callback function
*/
VaultClient.prototype.loginAndUnlock = function(username, password, device_id, fn) {
var self = this;
var steps = [
login,
deriveUnlockKey,
unlockSecret
];
async.waterfall(steps, fn);
function login (callback) {
self.login(username, password, device_id, function(err, resp) {
if (err) {
return callback(err);
}
if (!resp.blob || !resp.blob.encrypted_secret) {
return callback(new Error('Unable to retrieve blob and secret.'));
}
if (!resp.blob.id || !resp.blob.key) {
return callback(new Error('Unable to retrieve keys.'));
}
//get authInfo via id - would have been saved from login
var authInfo = self.infos[resp.blob.id];
if (!authInfo) {
return callback(new Error('Unable to find authInfo'));
}
callback(null, authInfo, password, resp.blob);
});
};
function deriveUnlockKey (authInfo, password, blob, callback) {
self._deriveUnlockKey(authInfo, password, null, function(err, authInfo, keys){
callback(err, keys.unlock, authInfo, blob);
});
};
function unlockSecret (unlock, authInfo, blob, callback) {
var secret;
try {
secret = crypt.decrypt(unlock, blob.encrypted_secret);
} catch (error) {
return callback(error);
}
callback(null, {
blob : blob,
unlock : unlock,
secret : secret,
username : authInfo.username,
verified : authInfo.emailVerified
});
};
};
/**
* Verify an email address for an existing user
*
* @param {string} username
* @param {string} token - Verification token
* @param {function} fn - Callback function
*/
VaultClient.prototype.verify = function(username, token, callback) {
var self = this;
self.getAuthInfo(username, function (err, authInfo){
if (err) {
return callback(err);
}
blobClient.verify(authInfo.blobvault, username.toLowerCase(), token, callback);
});
};
/*
* changePassword
* @param {object} options
* @param {string} options.username
* @param {string} options.password
* @param {string} options.masterkey
* @param {object} options.blob
*/
VaultClient.prototype.changePassword = function (options, fn) {
var self = this;
var password = String(options.password).trim();
var steps = [
getAuthInfo,
self._deriveLoginKeys,
self._deriveUnlockKey,
changePassword
];
async.waterfall(steps, fn);
function getAuthInfo(callback) {
self.getAuthInfo(options.username, function(err, authInfo) {
return callback (err, authInfo, password);
});
};
function changePassword (authInfo, keys, callback) {
options.keys = keys;
blobClient.updateKeys(options, callback);
};
};
/**
* rename
* rename a ripple account
* @param {object} options
* @param {string} options.username
* @param {string} options.new_username
* @param {string} options.password
* @param {string} options.masterkey
* @param {object} options.blob
* @param {function} fn
*/
VaultClient.prototype.rename = function (options, fn) {
var self = this;
var new_username = String(options.new_username).trim();
var password = String(options.password).trim();
var steps = [
getAuthInfo,
self._deriveLoginKeys,
self._deriveUnlockKey,
renameBlob
];
async.waterfall(steps, fn);
function getAuthInfo(callback) {
self.getAuthInfo(new_username, function(err, authInfo){
if (authInfo && authInfo.exists) {
return callback(new Error('username already taken.'));
} else {
authInfo.username = new_username;
}
return callback (err, authInfo, password);
});
};
function renameBlob (authInfo, keys, callback) {
options.keys = keys;
blobClient.rename(options, callback);
};
};
/**
* Register a new user and save to the blob vault
*
* @param {object} options
* @param {string} options.username
* @param {string} options.password
* @param {string} options.masterkey //optional, will create if absent
* @param {string} options.email
* @param {string} options.activateLink
* @param {object} options.oldUserBlob //optional
* @param {function} fn
*/
VaultClient.prototype.register = function(options, fn) {
var self = this;
var username = String(options.username).trim();
var password = String(options.password).trim();
var result = self.validateUsername(username);
if (!result.valid) {
return fn(new Error('invalid username.'));
}
var steps = [
getAuthInfo,
self._deriveLoginKeys,
self._deriveUnlockKey,
create
];
async.waterfall(steps, fn);
function getAuthInfo(callback) {
self.getAuthInfo(username, function(err, authInfo){
return callback (err, authInfo, password);
});
};
function create(authInfo, keys, callback) {
var params = {
url : authInfo.blobvault,
id : keys.id,
crypt : keys.crypt,
unlock : keys.unlock,
username : username,
email : options.email,
masterkey : options.masterkey || crypt.createMaster(),
activateLink : options.activateLink,
oldUserBlob : options.oldUserBlob,
domain : options.domain
};
blobClient.create(params, function(err, blob) {
if (err) {
callback(err);
} else {
callback(null, {
blob : blob,
username : username
});
}
});
};
};
/**
* validateUsername
* check username for validity
*/
VaultClient.prototype.validateUsername = function (username) {
username = String(username).trim();
var result = {
valid : false,
reason : ''
};
if (username.length < 2) {
result.reason = 'tooshort';
} else if (username.length > 20) {
result.reason = 'toolong';
} else if (!/^[a-zA-Z0-9\-]+$/.exec(username)) {
result.reason = 'charset';
} else if (/^-/.exec(username)) {
result.reason = 'starthyphen';
} else if (/-$/.exec(username)) {
result.reason = 'endhyphen';
} else if (/--/.exec(username)) {
result.reason = 'multhyphen';
} else {
result.valid = true;
}
return result;
};
/**
* generateDeviceID
* create a new random device ID for 2FA
*/
VaultClient.prototype.generateDeviceID = function () {
return crypt.createSecret(4);
};
/*** pass thru some blob client function ***/
VaultClient.prototype.resendEmail = blobClient.resendEmail;
VaultClient.prototype.recoverBlob = blobClient.recoverBlob;
VaultClient.prototype.deleteBlob = blobClient.deleteBlob;
VaultClient.prototype.requestToken = blobClient.requestToken;
VaultClient.prototype.verifyToken = blobClient.verifyToken;
VaultClient.prototype.getAttestation = blobClient.getAttestation;
VaultClient.prototype.updateAttestation = blobClient.updateAttestation;
VaultClient.prototype.getAttestationSummary = blobClient.getAttestationSummary;
//export by name
exports.VaultClient = VaultClient;

View File

@@ -1,5 +1,5 @@
var assert = require('assert');
var Account = require('../src/js/ripple/account').Account;
var Account = require('ripple-lib').Account;
describe('Account', function(){

View File

@@ -1,9 +1,11 @@
var assert = require('assert');
var utils = require('./testutils');
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
var Amount = utils.load_module('amount').Amount;
var UInt160 = utils.load_module('uint160').UInt160;
var config = utils.get_config();
var assert = require('assert');
var Amount = require('ripple-lib').Amount;
var UInt160 = require('ripple-lib').UInt160;
var load_config = require('ripple-lib').config.load;
var config = require('./config-example');
load_config(config);
describe('Amount', function() {
describe('Negatives', function() {
@@ -118,12 +120,21 @@ describe('Amount', function() {
});
});
describe('from_human', function() {
it('empty string', function() {
assert.strictEqual(Amount.from_human('').to_text_full(), 'NaN');
});
it('missing value', function() {
assert.strictEqual(Amount.from_human('USD').to_text_full(), 'NaN');
});
it('1 XRP', function() {
assert.strictEqual(Amount.from_human("1 XRP").to_text_full(), '1/XRP');
});
it('1 XRP human', function() {
assert.strictEqual(Amount.from_human("1 XRP").to_human_full(), '1/XRP');
});
it('1XRP human', function() {
assert.strictEqual(Amount.from_human('1XRP').to_human_full(), '1/XRP');
});
it('0.1 XRP', function() {
assert.strictEqual(Amount.from_human("0.1 XRP").to_text_full(), '0.1/XRP');
});
@@ -278,14 +289,11 @@ describe('Amount', function() {
});
});
describe('UInt160', function() {
it('Parse 0', function () {
assert.deepEqual(new BigInteger(), UInt160.from_generic('0')._value);
});
it('Parse 0 export', function () {
assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_generic('0').set_version(0).to_json());
});
it('Parse 1', function () {
assert.deepEqual(new BigInteger([1]), UInt160.from_generic('1')._value);
assert.deepEqual(UInt160.ACCOUNT_ONE, UInt160.from_generic('1').set_version(0).to_json());
});
it('Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export', function () {
assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_json('rrrrrrrrrrrrrrrrrrrrrhoLvTp').to_json());
@@ -328,9 +336,9 @@ describe('Amount', function() {
});
describe('Amount parsing', function() {
it('Parse invalid string', function() {
assert.strictEqual(Amount.from_json('x').to_text(), '0');
assert.strictEqual(typeof Amount.from_json('x').to_text(true), 'number');
assert(isNaN(Amount.from_json('x').to_text(true)));
assert.strictEqual(Amount.from_json('x').to_text(), 'NaN');
assert.strictEqual(typeof Amount.from_json('x').to_text(), 'string');
assert(isNaN(Amount.from_json('x').to_text()));
});
it('parse dem', function() {
assert.strictEqual(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
@@ -970,7 +978,7 @@ describe('Amount', function() {
assert.strictEqual(Amount.from_json('10000000').product_human(Amount.from_json('10')).to_text_full(), '0.0001/XRP');
});
it('Multiply USD with XAU (dem)', function () {
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '19900.00316303882/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '19900.00316303883/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply 0 XRP with 0 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('0')).to_human_full());
@@ -1042,7 +1050,7 @@ describe('Amount', function() {
assert.strictEqual(Amount.from_json('10000000').product_human(Amount.from_json('10')).to_human_full(), '0.0001/XRP');
});
it('Multiply USD with XAU (dem) human', function () {
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_human_full(), '19,900.00316303882/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_human_full(), '19,900.00316303883/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
@@ -1077,6 +1085,11 @@ describe('Amount', function() {
});
describe('from_quality', function() {
it('XRP/XRP', function () {
assert.throws(function() {
Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'XRP'}).to_text_full()
});
});
it('BTC/XRP', function () {
assert.strictEqual(Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'BTC'}).to_text_full(), '44,970/XRP');
});
@@ -1180,11 +1193,11 @@ describe('Amount', function() {
describe('amount limits', function() {
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_max.toString(), '9000000000000000000');
assert.strictEqual(Amount.bi_xns_max.toString(), '100000000000000000');
});
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_min.toString(), '-9000000000000000000');
assert.strictEqual(Amount.bi_xns_min.toString(), '-100000000000000000');
});
it('max mantissa value', function() {
@@ -1196,29 +1209,29 @@ describe('Amount', function() {
});
it ('from_json minimum XRP', function() {
console.log('max', Amount.bi_xns_max.toString());
var amt = Amount.from_json('-9000000000000000000');
assert.strictEqual(amt.to_json(), '-9000000000000000000');
var amt = Amount.from_json('-100000000000000000');
assert.strictEqual(amt.to_json(), '-100000000000000000');
});
it ('from_json maximum XRP', function() {
var amt = Amount.from_json('-9000000000000000000');
assert.strictEqual(amt.to_json(), '-9000000000000000000');
var amt = Amount.from_json('100000000000000000');
assert.strictEqual(amt.to_json(), '100000000000000000');
});
it ('from_json less than minimum XRP', function() {
var amt = Amount.from_json('-9000000000000000001');
assert.strictEqual(amt.to_json(), '0');
assert.throws(function() {
Amount.from_json('-100000000000000001');
});
});
it ('from_json more than maximum XRP', function() {
var amt = Amount.from_json('9000000000000000001');
assert.strictEqual(amt.to_json(), '0');
assert.throws(function() {
Amount.from_json('100000000000000001');
});
});
it ('from_json minimum IOU', function() {
var amt = Amount.from_json('-1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_min_value.toString());
assert.strictEqual(amt.to_text(), '-1000000000000000e-96');
assert.strictEqual(amt.to_text(), Amount.min_value);
});
@@ -1231,7 +1244,6 @@ describe('Amount', function() {
it ('from_json maximum IOU', function() {
var amt = Amount.from_json('9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_max_value.toString());
assert.strictEqual(amt.to_text(), '9999999999999999e80');
});
@@ -1243,13 +1255,11 @@ describe('Amount', function() {
it ('from_json normalize mantissa to valid max range, lost significant digits', function() {
var amt = Amount.from_json('99999999999999999999999999999999/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_max_value.toString());
assert.strictEqual(amt.to_text(), '9999999999999999e16');
});
it ('from_json normalize mantissa to min valid range, lost significant digits', function() {
var amt = Amount.from_json('-0.0000000000000000000000001/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt._value.toString(), Amount.bi_man_min_value.toString());
assert.strictEqual(amt.to_text(), '-1000000000000000e-40');
});
});

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('Base58', function() {
describe('Seed', function() {

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var currency = utils.load_module('currency').Currency;
var timeUtil = utils.load_module('utils').time;
var currency = require('ripple-lib').Currency;
var timeUtil = require('ripple-lib').utils.time;
describe('Currency', function() {
describe('json_rewrite', function() {

View File

@@ -0,0 +1,797 @@
{
"OfferCreate": {
"binary": {
"ledger_index": 10983428,
"meta": "201C00000000F8E311006F561C0662854F6571DD28392C1AF031757BDF2BC0E62C190ABE0BC22C46A2E443FDE824000263F550107B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE0064400000032A0D8DB065D4838D7EA4C6800000000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D1811473DFB1F8FDE93B1E301897694F0DDE56516BDC40E1E1E51100645642EE066C2D6E683C6FDC95C3C0EF88B3D7C10E31E9D98060F517F18AD98217DFE722000000005842EE066C2D6E683C6FDC95C3C0EF88B3D7C10E31E9D98060F517F18AD98217DF821473DFB1F8FDE93B1E301897694F0DDE56516BDC40E1E1E4110064567B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F400E72200000000365F04CE166242F400587B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F40001110000000000000000000000000000000000000000021100000000000000000000000000000000000000000311000000000000000000000000425443000000000004110A20B3C85F482532A9578DBB3950B85CA06594D1E1E1E3110064567B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE00E8365F04D40AEE52AE00587B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE000311000000000000000000000000425443000000000004110A20B3C85F482532A9578DBB3950B85CA06594D1E1E1E411006F56CDD61BD2DF2ADF53D0C05C171E2C8D48337BFE63868497BC30C5DCF2D0A03AFFE7220000000024000263E72500A79550330000000000000000340000000000000000550F60460F66E991AE6D77C50435E7FF8915453D411B6E43B38AC6410113B06CDC50107B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F400644000000326266D2065D4838D7EA4C6800000000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D1811473DFB1F8FDE93B1E301897694F0DDE56516BDC40E1E1E51100612500A7974E55329262FE69DD4F191AF0CE075489E7B7BDD273EC5528531D8184E1A73E76B7D356E0A052DA53A0D6F6C16422D206D4E38862ED7A13AE90ED0EF5ED09353C2A7A94E624000263F56240000005F01B0F39E1E7220000000024000263F62D000000096240000005F01AE829811473DFB1F8FDE93B1E301897694F0DDE56516BDC40E1E1F1031000",
"tx_blob": "120007228000000024000263F52019000263E764400000032A0D8DB065D4838D7EA4C6800000000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D1684000000000002710732103CDF7533BF6B6DE8C1AEFC1F2F776F8EDAE08D88C6E1F9B69535D9CDDF3071029744630440220153DDCA438981E498EF3AF383845F74B2CC20602FD1E20546A067C68D026DE6502207E4ECB4A23FFBC274CE0C2D08131F26FDDB6240B2A701C8E49410E0F18595053811473DFB1F8FDE93B1E301897694F0DDE56516BDC40",
"validated": true
},
"parsed": {
"validated": true,
"meta": {
"TransactionIndex": 0,
"AffectedNodes": [
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "1C0662854F6571DD28392C1AF031757BDF2BC0E62C190ABE0BC22C46A2E443FD",
"NewFields": {
"Sequence": 156661,
"BookDirectory": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE00",
"TakerPays": "13590433200",
"TakerGets": {
"value": "1",
"currency": "BTC",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"Account": "rBZgggUbdV7wHF1d7BRu1BLsxQqKHX3SN4"
}
}
},
{
"ModifiedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "42EE066C2D6E683C6FDC95C3C0EF88B3D7C10E31E9D98060F517F18AD98217DF",
"FinalFields": {
"Flags": 0,
"RootIndex": "42EE066C2D6E683C6FDC95C3C0EF88B3D7C10E31E9D98060F517F18AD98217DF",
"Owner": "rBZgggUbdV7wHF1d7BRu1BLsxQqKHX3SN4"
}
}
},
{
"DeletedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F400",
"FinalFields": {
"Flags": 0,
"ExchangeRate": "5F04CE166242F400",
"RootIndex": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F400",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000",
"TakerGetsCurrency": "0000000000000000000000004254430000000000",
"TakerGetsIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1"
}
}
},
{
"CreatedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE00",
"NewFields": {
"ExchangeRate": "5F04D40AEE52AE00",
"RootIndex": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04D40AEE52AE00",
"TakerGetsCurrency": "0000000000000000000000004254430000000000",
"TakerGetsIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1"
}
}
},
{
"DeletedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "CDD61BD2DF2ADF53D0C05C171E2C8D48337BFE63868497BC30C5DCF2D0A03AFF",
"FinalFields": {
"Flags": 0,
"Sequence": 156647,
"PreviousTxnLgrSeq": 10982736,
"BookNode": "0000000000000000",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "0F60460F66E991AE6D77C50435E7FF8915453D411B6E43B38AC6410113B06CDC",
"BookDirectory": "7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F04CE166242F400",
"TakerPays": "13524954400",
"TakerGets": {
"value": "1",
"currency": "BTC",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"Account": "rBZgggUbdV7wHF1d7BRu1BLsxQqKHX3SN4"
}
}
},
{
"ModifiedNode": {
"LedgerEntryType": "AccountRoot",
"PreviousTxnLgrSeq": 10983246,
"PreviousTxnID": "329262FE69DD4F191AF0CE075489E7B7BDD273EC5528531D8184E1A73E76B7D3",
"LedgerIndex": "E0A052DA53A0D6F6C16422D206D4E38862ED7A13AE90ED0EF5ED09353C2A7A94",
"PreviousFields": {
"Sequence": 156661,
"Balance": "25503141689"
},
"FinalFields": {
"Flags": 0,
"Sequence": 156662,
"OwnerCount": 9,
"Balance": "25503131689",
"Account": "rBZgggUbdV7wHF1d7BRu1BLsxQqKHX3SN4"
}
}
}
],
"TransactionResult": "tesSUCCESS"
},
"tx": {
"TransactionType": "OfferCreate",
"Flags": 2147483648,
"Sequence": 156661,
"OfferSequence": 156647,
"TakerPays": "13590433200",
"TakerGets": {
"value": "1",
"currency": "BTC",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"Fee": "10000",
"SigningPubKey": "03CDF7533BF6B6DE8C1AEFC1F2F776F8EDAE08D88C6E1F9B69535D9CDDF3071029",
"TxnSignature": "30440220153DDCA438981E498EF3AF383845F74B2CC20602FD1E20546A067C68D026DE6502207E4ECB4A23FFBC274CE0C2D08131F26FDDB6240B2A701C8E49410E0F18595053",
"Account": "rBZgggUbdV7wHF1d7BRu1BLsxQqKHX3SN4",
"hash": "3CC8ED34260911194E8E30543D70A6DF04D3DABC746A546DAED32D22496B478C",
"inLedger": 10983428,
"ledger_index": 10983428
}
}
},
"PartialPayment": {
"binary": {
"ledger_index": 11234994,
"meta": "201C000000126012D4038D7EA4C680010000000000000000000000005553440000000000625E2F1F09A0D769E05C04FAA64F0D2013306C6AF8E51100612500AB6DED55D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A75653539B9154C83B7D657103C27ABCA0EF1AD3674F6D0B341F20710FC50EC4DC03E6240000016962400000C7C1B0629EE1E72200000000240000016A2D0000001662400000C7C1B033BE8114E81DCB25DAA1DDEFF45145D334C56F12EA63C337E1E1E51100722500AB6DED55D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7565A9CDBCBDB64CD58DCD79A352E749EE48D6ACCE258F580F01FE326B31EB023DEE66294CDB50C7C41DBBA0000000000000000000000004A505900000000000000000000000000000000000000000000000001E1E722000200003700000000000000433800000000000000006294CD472C93E8BFBD0000000000000000000000004A5059000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD46780000000000000000000000000000000000000004A50590000000000E81DCB25DAA1DDEFF45145D334C56F12EA63C337E1E1E511006F2500AB6DED55D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7566CD06C01787F1F75688E5C4CE84E83B73ADC1647EC0A2761D553DA776564D1BBE664D5844871834DEC610000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD465D4E3860923E65C0000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1E1E72200020000240000DFA82A1C51809E33000000000000000034000000000000062D50103B95C29205977C2136BBC70F21895F8C8F471C8522BF446E5704488DA40C79F964D5844855628F5EC90000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD465D4E3851FD80BB80000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1811488F647BBEC01AE19BE070B8B91063D14CE77F523E1E1E51100722500AB6DED55D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A756785D84438CD44D7BD8234721BC77022E2BE590E38F9AB73C6E3FBC190524EF26E662940E35FA931A000000000000000000000000000055534400000000000000000000000000000000000000000000000001E1E7220002000037000000000000027D380000000000000000629411C37937E080010000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D167D503E871B540C0000000000000000000000000005553440000000000625E2F1F09A0D769E05C04FAA64F0D2013306C6AE1E1E51100722500AB6E1455E6190463C940CFE3D75B031D95768F55F8F5E163EEB460AE6ED003784FDBC06B567A12DF691E1E8039D53278D20D7CDC88D2C585DDBC4A769CD377CD8FF5C7E6A0E6629544C2582DF5BB4000000000000000000000000055534400000000000000000000000000000000000000000000000001E1E72200220000370000000000000288380000000000000000629544C255D8B8AA400000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D167D5438D7EA4C68000000000000000000000000000555344000000000088F647BBEC01AE19BE070B8B91063D14CE77F523E1E1E51100722500AB6DED55D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A756ADB3988C0EF801FF788E40AFA5EA28FBF5C6943C65F4651DDA411881E7FBBACFE662D5C3F42A882475860000000000000000000000004A505900000000000000000000000000000000000000000000000001E1E7220011000020123B9ACA0037000000000000000038000000000000004662D5C3F42D583783AE0000000000000000000000004A50590000000000000000000000000000000000000000000000000166D5CAA87BEE5380000000000000000000000000004A5059000000000088F647BBEC01AE19BE070B8B91063D14CE77F5236780000000000000000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD4E1E1F1031000",
"tx_blob": "12000022800200002400000169201B00AB6EB461D4838D7EA4C680000000000000000000000000005553440000000000625E2F1F09A0D769E05C04FAA64F0D2013306C6A684000000000002EE069D4844ABF137B17EA0000000000000000000000004A50590000000000E81DCB25DAA1DDEFF45145D334C56F12EA63C337732102AC2A11C997C04EC6A4139E6189111F90E89D05F9A9DDC3E2CA459CEA89C539D374463044022010E0D6884B36694342958C4872D7BADB825F36E6972757870665DD49580949A30220475F4CEA2904D23148AF51F194973AC73BDB986DA94BD9DEF51A5BBB9D7426108114E81DCB25DAA1DDEFF45145D334C56F12EA63C3378314625E2F1F09A0D769E05C04FAA64F0D2013306C6A011201E5C92828261DBAAC933B6309C6F5C72AF020AFD43000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1010A20B3C85F482532A9578DBB3950B85CA06594D1FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD41000000000000000000000000000000000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1010A20B3C85F482532A9578DBB3950B85CA06594D1FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD4100000000000000000000000000000000000000000300000000000000000000000005553440000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD301DD39C650A96EDA48334E70CC4A85B8B2E8502CD3FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD4300000000000000000000000005553440000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD301DD39C650A96EDA48334E70CC4A85B8B2E8502CD300",
"validated": true
},
"parsed": {
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"Balance": "857948042174",
"Flags": 0,
"OwnerCount": 22,
"Sequence": 362
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "53539B9154C83B7D657103C27ABCA0EF1AD3674F6D0B341F20710FC50EC4DC03",
"PreviousFields": {
"Balance": "857948054174",
"Sequence": 361
},
"PreviousTxnID": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"PreviousTxnLgrSeq": 11234797
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-37.37431482875837"
},
"Flags": 131072,
"HighLimit": {
"currency": "JPY",
"issuer": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"LowNode": "0000000000000043"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "5A9CDBCBDB64CD58DCD79A352E749EE48D6ACCE258F580F01FE326B31EB023DE",
"PreviousFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-38.5823992616441"
}
},
"PreviousTxnID": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"PreviousTxnLgrSeq": 11234797
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"BookDirectory": "3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E5704488DA40C79F9",
"BookNode": "0000000000000000",
"Expiration": 475103390,
"Flags": 131072,
"OwnerNode": "000000000000062D",
"Sequence": 57256,
"TakerGets": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "99.97996"
},
"TakerPays": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "12054.31469825737"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "6CD06C01787F1F75688E5C4CE84E83B73ADC1647EC0A2761D553DA776564D1BB",
"PreviousFields": {
"TakerGets": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "99.98998"
},
"TakerPays": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "12055.52278269025"
}
},
"PreviousTxnID": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"PreviousTxnLgrSeq": 11234797
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.05000000000000001"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "110"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "000000000000027D"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "785D84438CD44D7BD8234721BC77022E2BE590E38F9AB73C6E3FBC190524EF26",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.04"
}
},
"PreviousTxnID": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"PreviousTxnLgrSeq": 11234797
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1339.573870832192"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"value": "1000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "0000000000000288"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "7A12DF691E1E8039D53278D20D7CDC88D2C585DDBC4A769CD377CD8FF5C7E6A0",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1339.583890832192"
}
},
"PreviousTxnID": "E6190463C940CFE3D75B031D95768F55F8F5E163EEB460AE6ED003784FDBC06B",
"PreviousTxnLgrSeq": 11234836
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "111290.052087083"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000046",
"LowLimit": {
"currency": "JPY",
"issuer": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"value": "300000"
},
"LowNode": "0000000000000000",
"LowQualityIn": 1000000000
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "ADB3988C0EF801FF788E40AFA5EA28FBF5C6943C65F4651DDA411881E7FBBACF",
"PreviousFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "111288.8440026502"
}
},
"PreviousTxnID": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"PreviousTxnLgrSeq": 11234797
}
}
],
"DeliveredAmount": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "0.01000000000000001"
},
"TransactionIndex": 18,
"TransactionResult": "tesSUCCESS",
"delivered_amount": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "0.01000000000000001"
}
},
"tx": {
"Account": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"Amount": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "1"
},
"Destination": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"Fee": "12000",
"Flags": 2147614720,
"LastLedgerSequence": 11234996,
"Paths": [
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"SendMax": {
"currency": "JPY",
"issuer": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"value": "1.208084432885738"
},
"Sequence": 361,
"SigningPubKey": "02AC2A11C997C04EC6A4139E6189111F90E89D05F9A9DDC3E2CA459CEA89C539D3",
"TransactionType": "Payment",
"TxnSignature": "3044022010E0D6884B36694342958C4872D7BADB825F36E6972757870665DD49580949A30220475F4CEA2904D23148AF51F194973AC73BDB986DA94BD9DEF51A5BBB9D742610",
"hash": "F1E35F73FC81BE1599FBEE184F39AC4168E4C181C7ED000137E9E3C62E18D8C6",
"inLedger": 11234994,
"ledger_index": 11234994
},
"validated": true
}
},
"Payment": {
"binary": {
"ledger_index": 11234797,
"meta": "201C00000003F8E51100612500AA666C55BDB03864DA53C51FE3981DAE091A72289F732FC8A5F3D16F74E7D2036246FA8D5653539B9154C83B7D657103C27ABCA0EF1AD3674F6D0B341F20710FC50EC4DC03E6240000016862400000C7C1B0917EE1E7220000000024000001692D0000001662400000C7C1B0629E8114E81DCB25DAA1DDEFF45145D334C56F12EA63C337E1E1E51100722500A0DB72559926ED5C3974070FCA9B3566B7FBF3BCB7C29FCD8A4435781A4AAAE5778576E5565A9CDBCBDB64CD58DCD79A352E749EE48D6ACCE258F580F01FE326B31EB023DEE66294CE22EC649AF7B70000000000000000000000004A505900000000000000000000000000000000000000000000000001E1E722000200003700000000000000433800000000000000006294CDB50C7C41DBBA0000000000000000000000004A5059000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD46780000000000000000000000000000000000000004A50590000000000E81DCB25DAA1DDEFF45145D334C56F12EA63C337E1E1E511006F2500AB6CF9556E81745BB5DA1EB60C3FDB988EC5CDFE55AD493482F39A54A6AB66E149DB364B566CD06C01787F1F75688E5C4CE84E83B73ADC1647EC0A2761D553DA776564D1BBE664D584488DA40C79F90000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD465D5038D7EA4C6800000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1E1E72200020000240000DFA82A1C51809E33000000000000000034000000000000062D50103B95C29205977C2136BBC70F21895F8C8F471C8522BF446E5704488DA40C79F964D5844871834DEC610000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD465D4E3860923E65C0000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1811488F647BBEC01AE19BE070B8B91063D14CE77F523E1E1E51100722500A9FF9E5567550A7B0E398944B5528F678BCCA8A53C1356AF9C4FC8EE863B1CC83965C61A56785D84438CD44D7BD8234721BC77022E2BE590E38F9AB73C6E3FBC190524EF26E662940AA87BEE53800000000000000000000000000055534400000000000000000000000000000000000000000000000001E1E7220002000037000000000000027D38000000000000000062940E35FA931A00000000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D167D503E871B540C0000000000000000000000000005553440000000000625E2F1F09A0D769E05C04FAA64F0D2013306C6AE1E1E51100722500AB689E55ADE928FA078F53F269317B4FD8BF46C8953D381573BB80231BAC2EB3196DD74A567A12DF691E1E8039D53278D20D7CDC88D2C585DDBC4A769CD377CD8FF5C7E6A0E66295451D7C249ADC4000000000000000000000000055534400000000000000000000000000000000000000000000000001E1E722002200003700000000000002883800000000000000006295451D79CF5DCB400000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D167D5438D7EA4C68000000000000000000000000000555344000000000088F647BBEC01AE19BE070B8B91063D14CE77F523E1E1E51100722500AB4C9D55ECABF9CF633CF8FA6578A2C6430B3B9BA9611B8A31BC1730D222A6DD413B0CF156ADB3988C0EF801FF788E40AFA5EA28FBF5C6943C65F4651DDA411881E7FBBACFE662D5C3F427B811675E0000000000000000000000004A505900000000000000000000000000000000000000000000000001E1E7220011000020123B9ACA0037000000000000000038000000000000004662D5C3F42A882475860000000000000000000000004A50590000000000000000000000000000000000000000000000000166D5CAA87BEE5380000000000000000000000000004A5059000000000088F647BBEC01AE19BE070B8B91063D14CE77F5236780000000000000000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD4E1E1F1031000",
"tx_blob": "12000022800200002400000168201B00AB6DEF61D4038D7EA4C680000000000000000000000000005553440000000000625E2F1F09A0D769E05C04FAA64F0D2013306C6A684000000000002EE069D491C37937E080000000000000000000000000004A50590000000000E81DCB25DAA1DDEFF45145D334C56F12EA63C337732102AC2A11C997C04EC6A4139E6189111F90E89D05F9A9DDC3E2CA459CEA89C539D37446304402204529AC13FDE2AF411F83DFCCCA1A41534C36A73EC56C00B822EF36B037F8D146022013A1EBC759497D9BB352263C50B49A3E8BD83FA174F6F66B1F095E820026E3588114E81DCB25DAA1DDEFF45145D334C56F12EA63C3378314625E2F1F09A0D769E05C04FAA64F0D2013306C6A011201E5C92828261DBAAC933B6309C6F5C72AF020AFD43000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1010A20B3C85F482532A9578DBB3950B85CA06594D1FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD41000000000000000000000000000000000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1010A20B3C85F482532A9578DBB3950B85CA06594D1FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD4100000000000000000000000000000000000000000300000000000000000000000005553440000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD301DD39C650A96EDA48334E70CC4A85B8B2E8502CD3FF01E5C92828261DBAAC933B6309C6F5C72AF020AFD4300000000000000000000000005553440000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD301DD39C650A96EDA48334E70CC4A85B8B2E8502CD300",
"validated": true
},
"parsed": {
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"Balance": "857948054174",
"Flags": 0,
"OwnerCount": 22,
"Sequence": 361
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "53539B9154C83B7D657103C27ABCA0EF1AD3674F6D0B341F20710FC50EC4DC03",
"PreviousFields": {
"Balance": "857948066174",
"Sequence": 360
},
"PreviousTxnID": "BDB03864DA53C51FE3981DAE091A72289F732FC8A5F3D16F74E7D2036246FA8D",
"PreviousTxnLgrSeq": 11167340
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-38.5823992616441"
},
"Flags": 131072,
"HighLimit": {
"currency": "JPY",
"issuer": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"LowNode": "0000000000000043"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "5A9CDBCBDB64CD58DCD79A352E749EE48D6ACCE258F580F01FE326B31EB023DE",
"PreviousFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-39.79048369452983"
}
},
"PreviousTxnID": "9926ED5C3974070FCA9B3566B7FBF3BCB7C29FCD8A4435781A4AAAE5778576E5",
"PreviousTxnLgrSeq": 10541938
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"BookDirectory": "3B95C29205977C2136BBC70F21895F8C8F471C8522BF446E5704488DA40C79F9",
"BookNode": "0000000000000000",
"Expiration": 475103390,
"Flags": 131072,
"OwnerNode": "000000000000062D",
"Sequence": 57256,
"TakerGets": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "99.98998"
},
"TakerPays": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "12055.52278269025"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "6CD06C01787F1F75688E5C4CE84E83B73ADC1647EC0A2761D553DA776564D1BB",
"PreviousFields": {
"TakerGets": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "100"
},
"TakerPays": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "12056.73086712313"
}
},
"PreviousTxnID": "6E81745BB5DA1EB60C3FDB988EC5CDFE55AD493482F39A54A6AB66E149DB364B",
"PreviousTxnLgrSeq": 11234553
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.04"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "110"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "000000000000027D"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "785D84438CD44D7BD8234721BC77022E2BE590E38F9AB73C6E3FBC190524EF26",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.03"
}
},
"PreviousTxnID": "67550A7B0E398944B5528F678BCCA8A53C1356AF9C4FC8EE863B1CC83965C61A",
"PreviousTxnLgrSeq": 11141022
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1439.783890832192"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"value": "1000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "0000000000000288"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "7A12DF691E1E8039D53278D20D7CDC88D2C585DDBC4A769CD377CD8FF5C7E6A0",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1439.793910832192"
}
},
"PreviousTxnID": "ADE928FA078F53F269317B4FD8BF46C8953D381573BB80231BAC2EB3196DD74A",
"PreviousTxnLgrSeq": 11233438
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "111288.8440026502"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000046",
"LowLimit": {
"currency": "JPY",
"issuer": "rDVBvAQScXrGRGnzrxRrcJPeNLeLeUTAqE",
"value": "300000"
},
"LowNode": "0000000000000000",
"LowQualityIn": 1000000000
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "ADB3988C0EF801FF788E40AFA5EA28FBF5C6943C65F4651DDA411881E7FBBACF",
"PreviousFields": {
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "111287.6359182174"
}
},
"PreviousTxnID": "ECABF9CF633CF8FA6578A2C6430B3B9BA9611B8A31BC1730D222A6DD413B0CF1",
"PreviousTxnLgrSeq": 11226269
}
}
],
"TransactionIndex": 3,
"TransactionResult": "tesSUCCESS",
"delivered_amount": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "0.01"
}
},
"tx": {
"Account": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"Amount": {
"currency": "USD",
"issuer": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"value": "0.01"
},
"Destination": "r9y3sFjvdnTMJff1j8k2dodJkwgtghpf1o",
"Fee": "12000",
"Flags": 2147614720,
"LastLedgerSequence": 11234799,
"Paths": [
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"SendMax": {
"currency": "JPY",
"issuer": "r4wKTbb8AX5kEhXDvHDvhunDqsLZnXGfL9",
"value": "5"
},
"Sequence": 360,
"SigningPubKey": "02AC2A11C997C04EC6A4139E6189111F90E89D05F9A9DDC3E2CA459CEA89C539D3",
"TransactionType": "Payment",
"TxnSignature": "304402204529AC13FDE2AF411F83DFCCCA1A41534C36A73EC56C00B822EF36B037F8D146022013A1EBC759497D9BB352263C50B49A3E8BD83FA174F6F66B1F095E820026E358",
"hash": "D6405F3E92213763D9AA7270C0CC940864EFEB7CC6A95723E9BC4F33486994A7",
"inLedger": 11234797,
"ledger_index": 11234797
},
"validated": true
}
}
}

95
test/fixtures/binary-ledger-data.json vendored Normal file
View File

@@ -0,0 +1,95 @@
{
"RippleState": {
"binary": {
"data": "110072220002000025000B657837000000000000003B3800000000000000005587591A63051645F37B85D1FBA55EE69B1C96BFF16904F5C99F03FB93D42D03756280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D167D4C38D7EA4C680000000000000000000000000004254430000000000C795FDF8A637BCAAEDAD1C434033506236C82A2D",
"index": "000103996A3BAD918657F86E12A67D693E8FC8A814DA4B958A244B5F14D93E58"
},
"parsed": {
"Balance": {
"currency": "BTC",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
},
"Flags": 131072,
"HighLimit": {
"currency": "BTC",
"issuer": "rKUK9omZqVEnraCipKNFb5q4tuNTeqEDZS",
"value": "10"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "BTC",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "000000000000003B",
"PreviousTxnID": "87591A63051645F37B85D1FBA55EE69B1C96BFF16904F5C99F03FB93D42D0375",
"PreviousTxnLgrSeq": 746872,
"index": "000103996A3BAD918657F86E12A67D693E8FC8A814DA4B958A244B5F14D93E58"
}
},
"AccountRoot": {
"binary": {
"data": "1100612200000000240000000125000DA4192D0000000055CA2EADCF53FEEFE2FF6B47D683A1E33413BC876C7C87669618365C91BD12A42262400000003B9ACA008114D018AF490EB4E476D735159CDEA798B6AF670FF7",
"index": "0002D81201E576CF3E0120E2CC35C03E08E22452F498B8C874CD1DF9D3DC305B"
},
"parsed": {
"Account": "rKyK3pQtCTMz6nt6Yxtx8vgju6hLyLWpwh",
"Balance": "1000000000",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "CA2EADCF53FEEFE2FF6B47D683A1E33413BC876C7C87669618365C91BD12A422",
"PreviousTxnLgrSeq": 893977,
"Sequence": 1,
"index": "0002D81201E576CF3E0120E2CC35C03E08E22452F498B8C874CD1DF9D3DC305B"
}
},
"Offer": {
"binary": {
"data": "11006F2200000000240002A44625000F26213300000000000000003400000000000000C0555CCD6C8CD4E7BB1E508F05B831A9B4B634E02862435D5E3E3EEF3AB1690331395010FE4D53B02BC5D46DE095166E0667A0F3797F8A782F8A203B570EBC4086D53E0064D5459C5A55C77D00000000000000000000000000494C53000000000092D705968936C419CE614BF264B5EEB1CEA47FF465D48D871095D18000000000000000000000000000425443000000000092D705968936C419CE614BF264B5EEB1CEA47FF481141C40DA3AAC605E30B6CC5891EA6CDDFA0C996CE8",
"index": "002B4106648895A68068DF34FC55AAD9BC1DC135FD737A318C582D706EA505A1"
},
"parsed": {
"Account": "rs2PcQ9HX8afGGPsvUxEmTsrKs5D34kwwK",
"BookDirectory": "FE4D53B02BC5D46DE095166E0667A0F3797F8A782F8A203B570EBC4086D53E00",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "00000000000000C0",
"PreviousTxnID": "5CCD6C8CD4E7BB1E508F05B831A9B4B634E02862435D5E3E3EEF3AB169033139",
"PreviousTxnLgrSeq": 992801,
"Sequence": 173126,
"TakerGets": {
"currency": "BTC",
"issuer": "rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9",
"value": "3.80768"
},
"TakerPays": {
"currency": "ILS",
"issuer": "rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9",
"value": "1579.28668368"
},
"index": "002B4106648895A68068DF34FC55AAD9BC1DC135FD737A318C582D706EA505A1"
}
},
"DirectoryNode": {
"binary": {
"data": "110064220000000058001E3B28ABD08E399095EABC6C493E84BFA47B1A6474C04D5289E767A404AEF18214D5A7C190E367A150A2E4A04DE62E08766B8B48C2011360FE8118802AF3344FDC1DEB95D12F8E7C548928C19FEFE8CF7BD14D28E8F0E9EA3B70BB091D385EB6AFF31C52F05498BB70268FC4BEEDDC6B86770A85D74CEA71E676F574B44A1030EBA09A9A29C939550FAF2849171B3422EB521365F9052D00",
"index": "001E3B28ABD08E399095EABC6C493E84BFA47B1A6474C04D5289E767A404AEF1"
},
"parsed": {
"Flags": 0,
"Indexes": [
"FE8118802AF3344FDC1DEB95D12F8E7C548928C19FEFE8CF7BD14D28E8F0E9EA",
"3B70BB091D385EB6AFF31C52F05498BB70268FC4BEEDDC6B86770A85D74CEA71",
"E676F574B44A1030EBA09A9A29C939550FAF2849171B3422EB521365F9052D00"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rL76tmsh1Pgy57B4KMqjqWL7mGhwG24xgv",
"RootIndex": "001E3B28ABD08E399095EABC6C493E84BFA47B1A6474C04D5289E767A404AEF1",
"index": "001E3B28ABD08E399095EABC6C493E84BFA47B1A6474C04D5289E767A404AEF1"
}
}
}

1228
test/fixtures/binary-transaction.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
{
"AffectedNodes": [
{
"DeletedNode": {
"FinalFields": {
"Account": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600",
"BookNode": "0000000000000000",
"Flags": 131072,
"OwnerNode": "0000000000000031",
"PreviousTxnID": "83CA9AB231B0B4DA8ACF6305A6D7B00AB83404A1FDB8F8BCF7108EB87E0A8196",
"PreviousTxnLgrSeq": 10555009,
"Sequence": 12370,
"TakerGets": "44978350398",
"TakerPays": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "1056.990784602728"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "0496450E3F46368FB011B8B524605A906C0854441D30420457A81EA89BE649BE"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "0"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rGa3Tb6vaMVU5RQMjxk4nsMGSArbu8epGG",
"value": "5"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "2DECFAC23B77D5AEA6116C15F5C6D4669EBAEE9E7EE050A40FE2B1E47B6A9419",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "3"
}
},
"PreviousTxnID": "6F35AD78AA196389D15F4BAF054122070506633C1506EF16A48877E2593CCE2D",
"PreviousTxnLgrSeq": 10555014
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1228.52678703286"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "0"
},
"HighNode": "0000000000000076",
"LowLimit": {
"currency": "USD",
"issuer": "rPofp4ruTavGuFnb9AYN2vRPBFxHB7RRsH",
"value": "50000"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "4AB2FFDEE65E025AAB54305A161C80D32082574BB0502311F29227089C696388",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1225.52678703286"
}
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"RootIndex": "65E53030C59CD541BB6A8B631F13FA9BBF6F6E28B63D41AA363F23313B34B094"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "65E53030C59CD541BB6A8B631F13FA9BBF6F6E28B63D41AA363F23313B34B094"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"Balance": "335297994919",
"Flags": 0,
"OwnerCount": 12,
"Sequence": 12379
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "6ACA1AAB7CD8877EA63E0C282A37FC212AD24640743F019AC2DD8674E003B741",
"PreviousFields": {
"OwnerCount": 13
},
"PreviousTxnID": "83CA9AB231B0B4DA8ACF6305A6D7B00AB83404A1FDB8F8BCF7108EB87E0A8196",
"PreviousTxnLgrSeq": 10555009
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-15.68270575272517"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rGa3Tb6vaMVU5RQMjxk4nsMGSArbu8epGG",
"value": "5000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "0"
},
"LowNode": "000000000000004A"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "826CF5BFD28F3934B518D0BDF3231259CBD3FD0946E3C3CA0C97D2C75D2D1A09",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-12.68270575272517"
}
},
"PreviousTxnID": "6F35AD78AA196389D15F4BAF054122070506633C1506EF16A48877E2593CCE2D",
"PreviousTxnLgrSeq": 10555014
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexPrevious": "000000000000002C",
"Owner": "rNGySgyyEdRJ6MKmzsZwyhhVeFKdENRGQ6",
"RootIndex": "A93C6B313E260AC7C7734DF44F4461075E2C937C936C2B81DA2C9F69D4A0B0F2"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "8D8FB3359BBA810ED5C5894088F2415E322811181ADCC5BB087E829207DFBBEB"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"Balance": "25116082528",
"Flags": 0,
"OwnerCount": 5,
"Sequence": 2197
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "8F55B7E947241AD38FD6D47374BF8E7CA7DF177C8B79712B4CAC5E91FD5023FF",
"PreviousFields": {
"OwnerCount": 6
},
"PreviousTxnID": "0327747C391C678CA2AC46F422E1E8D307A41E9C0ED5DB2677B51CEBE41BD243",
"PreviousTxnLgrSeq": 10554892
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rauAjp7gUp8xqHnPFDSo72Nc6aMx2k9yDk",
"Balance": "233929959",
"Flags": 0,
"OwnerCount": 14,
"Sequence": 31927
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "A27BB98F7C9D32F404B364622645F80480F87C8A91BB13CA9F6E569144C2A5A8",
"PreviousFields": {
"Balance": "233941959",
"Sequence": 31926
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "4D08594FC79E1600",
"Flags": 0,
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0520B3C85F482532A9578DBB3950B85CA03594D1"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FC79E1600"
}
},
{
"DeletedNode": {
"FinalFields": {
"ExchangeRate": "4D08594FE7353EDE",
"Flags": 0,
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0520B3C85F482532A9578DBB3950B85CA03594D1"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2818.268620725051"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rauAjp7gUp8xqHnPFDSo72Nc6aMx2k9yDk",
"value": "50000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "0"
},
"LowNode": "00000000000001B0"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "FAD28E839AF29C6CCB8DA6DC71510A5BF8A9C34062C128071C7D22E2469B8288",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2821.274620725051"
}
},
"PreviousTxnID": "65C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4C",
"PreviousTxnLgrSeq": 10554856
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rP5VvumKn5qqn4RYTMGipmPE9965ARVQNT",
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D08594FE7353EDE",
"BookNode": "0000000000000000",
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "0327747C391C678CA2AC46F422E1E8D307A41E9C0ED5DB2677B51CEBE41BD243",
"PreviousTxnLgrSeq": 10554892,
"Sequence": 2196,
"TakerGets": "4255320000",
"TakerPays": {
"currency": "USD",
"issuer": "rQ3qJZwtzi4Zo3nWbmX9gwCke6S8jmHRCn",
"value": "100"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "FB2E442ED1A5BCA1E237BA133807AE17AED1A7E4B9F404906308ADB01A57609D"
}
}
],
"DeliveredAmount": {
"currency": "USD",
"issuer": "rabVnHuo1eq747GbnLDAJfE9GpsGwcL9Hy",
"value": "3"
},
"TransactionIndex": 5,
"TransactionResult": "tesSUCCESS"
}

788
test/fixtures/pathfind.json vendored Normal file
View File

@@ -0,0 +1,788 @@
{
"id": 2,
"type": "path_find",
"alternatives": [
{
"paths_computed": [
[
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rwBWBFZrbLzHoe3PhwWYv89iHJdxAFrxcB",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rULnR9YhAkj9HrcxAcudzBhaXRSqT7zJkq",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": "64235"
},
{
"paths_computed": [
[
{
"account": "rpgKWEmNqSDAGFhy5WDnsyPqfQxbWxKeVd",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "BTC",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.000003810807915357615"
}
},
{
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "CHF",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.004290898000000001"
}
},
{
"paths_computed": [
[
{
"account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "CNY",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.006251153484651535"
}
},
{
"paths_computed": [
[
{
"account": "rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "DYM",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.001503"
}
},
{
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "EUR",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.0008032032"
}
},
{
"paths_computed": [
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "JPY",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.12872694"
}
},
{
"paths_computed": [
[
{
"account": "rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rrpNnNLKrartuEqfJGpqyDwPj1AFPg9vn1",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rpHgehzdpfWRXKvSv6duKvVuo1aZVimdaT",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
],
[
{
"account": "rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4",
"type": 1,
"type_hex": "0000000000000001"
},
{
"currency": "XRP",
"type": 16,
"type_hex": "0000000000000010"
},
{
"currency": "USD",
"issuer": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 48,
"type_hex": "0000000000000030"
},
{
"account": "rHHa9t2kLQyXRbdLkSzEgkzwf9unmFgZs9",
"type": 1,
"type_hex": "0000000000000001"
},
{
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"type": 1,
"type_hex": "0000000000000001"
}
]
],
"source_amount": {
"currency": "MXN",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "0.008172391857506362"
}
}
],
"destination_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"destination_amount": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0.001"
},
"source_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
}

123
test/fixtures/payment-iou.json vendored Normal file
View File

@@ -0,0 +1,123 @@
{
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied.",
"ledger_hash": "F3F1416BF2E813396AB01FAA38E9F1023AC4D2368D94B0D52B2BC603CEEC01C3",
"ledger_index": 10459371,
"status": "closed",
"type": "transaction",
"validated": true,
"metadata": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1.525330905250352"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "0"
},
"HighNode": "00000000000001E8",
"LowLimit": {
"currency": "USD",
"issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc",
"value": "1000000000"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "1.535330905250352"
}
},
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
"PreviousTxnLgrSeq": 10459364
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0.02"
},
"Flags": 1114112,
"HighLimit": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "0"
},
"HighNode": "00000000000001E8",
"LowLimit": {
"currency": "USD",
"issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K",
"value": "1000000000"
},
"LowNode": "0000000000000000"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0.01"
}
},
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
"PreviousTxnLgrSeq": 10459364
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc",
"Balance": "239555992",
"Flags": 0,
"OwnerCount": 1,
"Sequence": 38
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818",
"PreviousFields": {
"Balance": "239567992",
"Sequence": 37
},
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
"PreviousTxnLgrSeq": 10459364
}
}
],
"TransactionIndex": 2,
"TransactionResult": "tesSUCCESS"
},
"tx_json": {
"Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc",
"Amount": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "0.01"
},
"Destination": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K",
"Fee": "12000",
"Flags": 2147483648,
"LastLedgerSequence": 10459379,
"Sequence": 37,
"SigningPubKey": "03F16A52EBDCA6EBF5D99828E1E6918C64D45E6F136476A8F4757512FE553D18F0",
"TransactionType": "Payment",
"TxnSignature": "3044022031D6AB55CDFD17E06DA0BAD6D6B7DC9B5CA8FFF50405F2FCD3ED8D3893B1835E02200524CC1E7D70AE3F00C9F94405C55EE179C363F534905168AE8B5BA01CF568A0",
"date": 471644150,
"hash": "34671C179737CC89E0F8BBAA83C313885ED1733488FC0F3088BAE16A3D9A5B1B"
}
}

View File

@@ -0,0 +1,171 @@
{
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_hash": "60A87F43A4A95AF446AE9391CEFC4FD41E24CA632286EBACA8B1337791009D2A",
"ledger_index": 11409475,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2008.589162146675"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rQBgvk1GMACRzpSBrHJNQ6rNfgdzUv2SaX",
"value": "1000000000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR",
"value": "0"
},
"LowNode": "000000000000028B"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "10E1B93C71D6FC528B36BFDAD005DFDA8BA4C6EC691ADE225ACD91A18BBBC2BA",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-2003.931566739001"
}
},
"PreviousTxnID": "308FD18F3D42F83F5C7043F2DEAA4B8DDCAF20F564D180D606F00F3E575FEEC5",
"PreviousTxnLgrSeq": 11409459
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rHvo925Q1zmDJbzze6a5L3DfXdvhrwh1oJ",
"Balance": "222138630497",
"Flags": 0,
"OwnerCount": 5,
"Sequence": 1040
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "3FB24E2BA13AE12802781D03F263CE28569BF487C766812485E3C286ED990CFC",
"PreviousFields": {
"Balance": "221810037141"
},
"PreviousTxnID": "736CEAD2BE2C49285BBC3ADCD1EA42FBB0D1A292E1A03C344234F664FC6EC523",
"PreviousTxnLgrSeq": 11409463
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rQBgvk1GMACRzpSBrHJNQ6rNfgdzUv2SaX",
"Balance": "473511395546",
"Flags": 0,
"OwnerCount": 5,
"Sequence": 4094
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "4179A2DBAF895E9D0618A8C0FB2FFDBEAE92E74C275C7992D538484AE20B2140",
"PreviousFields": {
"Balance": "473839998902",
"Sequence": 4093
},
"PreviousTxnID": "308FD18F3D42F83F5C7043F2DEAA4B8DDCAF20F564D180D606F00F3E575FEEC5",
"PreviousTxnLgrSeq": 11409459
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rHvo925Q1zmDJbzze6a5L3DfXdvhrwh1oJ",
"BookDirectory": "4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C19107C30E031F0",
"BookNode": "0000000000000000",
"Flags": 131072,
"OwnerNode": "0000000000000000",
"Sequence": 1039,
"TakerGets": {
"currency": "USD",
"issuer": "rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR",
"value": "190.5072902688483"
},
"TakerPays": "13440289328"
},
"LedgerEntryType": "Offer",
"LedgerIndex": "AAD85C591B95855B524675AE73EAEC90D5E4BF32425D36A5B449914ADE33EDD9",
"PreviousFields": {
"TakerGets": {
"currency": "USD",
"issuer": "rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR",
"value": "195.1648856765226"
},
"TakerPays": "13768882684"
},
"PreviousTxnID": "736CEAD2BE2C49285BBC3ADCD1EA42FBB0D1A292E1A03C344234F664FC6EC523",
"PreviousTxnLgrSeq": 11409463
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-459.5348867698966"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rHvo925Q1zmDJbzze6a5L3DfXdvhrwh1oJ",
"value": "1000000000"
},
"HighNode": "0000000000000000",
"LowLimit": {
"currency": "USD",
"issuer": "rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR",
"value": "0"
},
"LowNode": "0000000000000296"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "CDE76B186BBB028CA4C8A0C6D22085AD1DF8F692920150189343831E2C0C647D",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-464.2017973683862"
}
},
"PreviousTxnID": "5AF91C90CE8AE78FB8ACFFE2AB626936AB2CA4A8D66C25D087A41D4B75C16F39",
"PreviousTxnLgrSeq": 11409443
}
}
],
"TransactionIndex": 10,
"TransactionResult": "tesSUCCESS"
},
"status": "closed",
"transaction": {
"Account": "rQBgvk1GMACRzpSBrHJNQ6rNfgdzUv2SaX",
"Fee": "10000",
"Flags": 524288,
"Sequence": 4093,
"SigningPubKey": "020D6BF674E9ABF3BD8C08BA7CCFA878DC283729240CEC096FC1EE23DA823472C0",
"TakerGets": "328593356",
"TakerPays": {
"currency": "USD",
"issuer": "rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR",
"value": "3.943120281680337"
},
"TransactionType": "OfferCreate",
"TxnSignature": "3045022100FC89A7B7573754B33DA7556D861AC180CFEDD1F72DE9436384540745E3B2DBB4022074EDA52B2BE636A002743A49B791FD362B70B0DAFB8CC0AF3C49EDC261D0EA58",
"date": 475884270,
"hash": "FC7A9D19B40B7BCC66E3A38536E7DB888925BF935440307F6A4F55818E141BAC",
"owner_funds": "473466395546"
},
"type": "transaction",
"validated": true
}

19
test/fixtures/transaction-proposed.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_current_index": 114177,
"status": "proposed",
"transaction": {
"Account": "rUPotLj5CNKaP4bQANcecEuT8hai3VpxfB",
"Fee": "10",
"Flags": 2147483648,
"LastLedgerSequence": 114177,
"Sequence": 3862,
"SigningPubKey": "FA16E9F38DF11402953A5B030C1AE8A88C47E348170C3B8EC6C8D775E797168F09",
"TransactionType": "AccountSet",
"hash": "815AF3AC669F513C039C0AB9F31D30703343A4D39EA7AA8D26F515B56B74D728"
},
"type": "transaction",
"validated": false
}

44
test/fixtures/transaction.json vendored Normal file
View File

@@ -0,0 +1,44 @@
{
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_hash": "000515A6137F4D24C111EE1FA48AE555671E7788428085D6A7492CD82A166000",
"ledger_index": 11368743,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "rfFTrpgpm7c3pXsZMD9wnMiUDDF1UGvdcy",
"Balance": "1",
"Flags": 4849664,
"OwnerCount": 3,
"Sequence": 3860
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "000400950EA27EB5710C0D5BE1D2B0009139F168AC5D07C13B8140EC3F82A000",
"PreviousFields": {
"Balance": "382724563",
"Sequence": 3859
}
}
}
],
"TransactionIndex": 1,
"TransactionResult": "tesSUCCESS"
},
"status": "closed",
"transaction": {
"Account": "rfFTrpgpm7c3pXsZMD9wnMiUDDF1UGvdcy",
"Fee": "12000",
"Flags": 2147483648,
"LastLedgerSequence": 11368745,
"Sequence": 3859,
"SigningPubKey": "`123E9F38DF11402953A5B030C1AE8AE348170C3B8EC6C8D775E7971684222",
"TransactionType": "AccountSet",
"date": 475696890,
"hash": "2D019346ED13ED8FB61F60B9F96E69B47A05B0055EB4AD5215E2072250E6CF07"
},
"type": "transaction",
"validated": true
}

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('KeyPair', function() {
it('can generate an address', function () {

View File

@@ -1,9 +1,7 @@
var assert = require('assert');
var fs = require('fs');
var utils = require('./testutils');
var Ledger = utils.load_module('ledger').Ledger;
var config = require('./testutils').get_config();
var Ledger = require('ripple-lib').Ledger;
/**
* @param ledger_index {Number}

View File

@@ -1,8 +1,8 @@
var assert = require('assert');
var sjcl = require('../build/sjcl');
var Message = require('../src/js/ripple/message').Message;
var Seed = require('../src/js/ripple/seed').Seed;
var Remote = require('../src/js/ripple/remote').Remote;
var sjcl = require('ripple-lib').sjcl;
var Message = require('ripple-lib').Message;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
describe('Message', function(){

37
test/metadata-test.js Normal file
View File

@@ -0,0 +1,37 @@
var assert = require('assert');
var Meta = require('ripple-lib').Meta;
describe('Meta', function() {
var meta = new Meta(require('./fixtures/payment-iou.json').metadata);
function callback(el, idx, ary) {
assert.strictEqual(meta.nodes[idx],el);
}
it('forEach', function() {
meta.forEach(callback);
});
it('map', function() {
meta.map(callback);
});
it('filter', function() {
meta.filter(callback);
});
it('every', function() {
meta.every(callback);
});
it('some', function() {
meta.some(callback);
});
it('reduce', function() {
meta.reduce(function(prev,curr,idx,ary) {
assert.strictEqual(meta.nodes[idx], curr);
}, []);
});
});

1
test/node_modules/ripple-lib generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../../src/js/ripple/

File diff suppressed because it is too large Load Diff

78
test/rangeset-test.js Normal file
View File

@@ -0,0 +1,78 @@
var assert = require('assert');
var RangeSet = require('ripple-lib').RangeSet;
describe('RangeSet', function() {
it('add()', function() {
var r = new RangeSet();
r.add('4-5');
r.add('7-10');
r.add('1-2');
r.add('3');
assert.deepEqual(r._ranges, [
{ start: 1, end: 2 },
{ start: 3, end: 3 },
{ start: 4, end: 5 },
{ start: 7, end: 10 }
]);
});
it('add() -- malformed range', function() {
var r = new RangeSet();
assert.throws(function() {
r.add(null);
});
assert.throws(function() {
r.add(void(0));
});
assert.throws(function() {
r.add('a');
});
assert.throws(function() {
r.add('2-1');
});
});
it('contains()', function() {
var r = new RangeSet();
r.add('32570-11005146');
r.add('11005147');
assert.strictEqual(r.contains(1), false);
assert.strictEqual(r.contains(32569), false);
assert.strictEqual(r.contains(32570), true);
assert.strictEqual(r.contains('32570'), true);
assert.strictEqual(r.contains(50000), true);
assert.strictEqual(r.contains(11005146), true);
assert.strictEqual(r.contains(11005147), true);
assert.strictEqual(r.contains(11005148), false);
assert.strictEqual(r.contains(12000000), false);
});
it('contains() -- invalid ledger', function() {
var r = new RangeSet();
assert.throws(function() {
r.contains(null);
});
assert.throws(function() {
r.contains(void(0));
});
assert.throws(function() {
r.contains('a');
});
});
it('reset()', function() {
var r = new RangeSet();
r.add('4-5');
r.add('7-10');
r.reset();
assert.deepEqual(r._ranges, [ ]);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
var assert = require('assert');
var utils = require('./testutils');
var Request = utils.load_module('request').Request;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Currency = utils.load_module('currency').Currency;
var Request = require('ripple-lib').Request;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Currency = require('ripple-lib').Currency;
var RippleError = require('ripple-lib').RippleError;
function makeServer(url) {
var server = new Server(new process.EventEmitter(), url);
@@ -56,7 +56,7 @@ describe('Request', function() {
request.request();
});
it('Broadcast request', function(done) {
it('Send request -- filterRequest', function(done) {
var servers = [
makeServer('wss://localhost:5006'),
makeServer('wss://localhost:5007')
@@ -64,24 +64,401 @@ describe('Request', function() {
var requests = 0;
servers.forEach(function(server, index, arr) {
server._request = function(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'server_info');
if (++requests === arr.length) {
done();
}
};
});
var successResponse = {
account_data: {
Account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
Balance: '13188802787',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 17,
PreviousTxnID: 'C6A2313CD9E34FFA3EB42F82B2B30F7FE12A045F1F4FDDAF006B25D7286536DD',
PreviousTxnLgrSeq: 8828020,
Sequence: 1406,
index: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05'
},
ledger_current_index: 9022821,
validated: false
};
var errorResponse = {
error: 'remoteError',
error_message: 'Remote reported an error.',
remote: {
id: 3,
status: 'error',
type: 'response',
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
error: 'actNotFound',
error_code: 15,
error_message: 'Account not found.',
ledger_current_index: 9022856,
request: {
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
command: 'account_info',
id: 3
},
validated: false
}
};
function checkRequest(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'account_info');
};
servers[0]._request = function(req) {
++requests;
checkRequest(req);
req.emit('error', errorResponse);
};
servers[1]._request = function(req) {
++requests;
checkRequest(req);
setImmediate(function() {
req.emit('success', successResponse);
});
};
var remote = new Remote();
remote._connected = true;
remote._servers = servers;
var request = new Request(remote, 'server_info');
var request = new Request(remote, 'account_info');
request.broadcast();
request.message.account = 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC';
request.filter(function(res) {
return res
&& typeof res === 'object'
&& !res.hasOwnProperty('error');
});
request.callback(function(err, res) {
assert.ifError(err);
assert.strictEqual(requests, 2, 'Failed to broadcast');
assert.deepEqual(res, successResponse);
done();
});
});
it('Send request -- filterRequest -- no success', function(done) {
var servers = [
makeServer('wss://localhost:5006'),
makeServer('wss://localhost:5007')
];
var requests = 0;
var errorResponse = {
error: 'remoteError',
error_message: 'Remote reported an error.',
remote: {
id: 3,
status: 'error',
type: 'response',
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
error: 'actNotFound',
error_code: 15,
error_message: 'Account not found.',
ledger_current_index: 9022856,
request: {
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
command: 'account_info',
id: 3
},
validated: false
}
};
function checkRequest(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'account_info');
};
function sendError(req) {
++requests;
checkRequest(req);
req.emit('error', errorResponse);
};
servers[0]._request = sendError;
servers[1]._request = sendError;
var remote = new Remote();
remote._connected = true;
remote._servers = servers;
var request = new Request(remote, 'account_info');
request.message.account = 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC';
request.filter(function(res) {
return res
&& typeof res === 'object'
&& !res.hasOwnProperty('error');
});
request.callback(function(err, res) {
setImmediate(function() {
assert.strictEqual(requests, 2, 'Failed to broadcast');
assert.deepEqual(err, new RippleError(errorResponse));
done();
});
});
});
it('Send request -- filterRequest -- ledger prefilter', function(done) {
var servers = [
makeServer('wss://localhost:5006'),
makeServer('wss://localhost:5007')
];
var requests = 0;
var successResponse = {
account_data: {
Account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
Balance: '13188802787',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 17,
PreviousTxnID: 'C6A2313CD9E34FFA3EB42F82B2B30F7FE12A045F1F4FDDAF006B25D7286536DD',
PreviousTxnLgrSeq: 8828020,
Sequence: 1406,
index: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05'
},
ledger_current_index: 9022821,
validated: false
};
function checkRequest(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'account_info');
};
servers[0]._request = function(req) {
assert(false, 'Should not request; server does not have ledger');
};
servers[1]._request = function(req) {
++requests;
checkRequest(req);
setImmediate(function() {
req.emit('success', successResponse);
});
};
servers[0]._ledgerRanges.add('5-6');
servers[1]._ledgerRanges.add('1-4');
var remote = new Remote();
remote._connected = true;
remote._servers = servers;
var request = new Request(remote, 'account_info');
request.message.account = 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC';
request.selectLedger(4);
request.filter(function(res) {
return res
&& typeof res === 'object'
&& !res.hasOwnProperty('error');
});
request.callback(function(err, res) {
assert.ifError(err);
assert.deepEqual(res, successResponse);
done();
});
});
it('Send request -- filterRequest -- server reconnects', function(done) {
var servers = [
makeServer('wss://localhost:5006'),
makeServer('wss://localhost:5007')
];
var requests = 0;
var successResponse = {
account_data: {
Account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
Balance: '13188802787',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 17,
PreviousTxnID: 'C6A2313CD9E34FFA3EB42F82B2B30F7FE12A045F1F4FDDAF006B25D7286536DD',
PreviousTxnLgrSeq: 8828020,
Sequence: 1406,
index: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05'
},
ledger_current_index: 9022821,
validated: false
};
var errorResponse = {
error: 'remoteError',
error_message: 'Remote reported an error.',
remote: {
id: 3,
status: 'error',
type: 'response',
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
error: 'actNotFound',
error_code: 15,
error_message: 'Account not found.',
ledger_current_index: 9022856,
request: {
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
command: 'account_info',
id: 3
},
validated: false
}
};
function checkRequest(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'account_info');
};
servers[0]._connected = false;
servers[0]._shouldConnect = true;
servers[0].removeAllListeners('connect');
servers[0]._request = function(req) {
++requests;
checkRequest(req);
req.emit('success', successResponse);
};
servers[1]._request = function(req) {
++requests;
checkRequest(req);
req.emit('error', errorResponse);
servers[0]._connected = true;
servers[0].emit('connect');
};
var remote = new Remote();
remote._connected = true;
remote._servers = servers;
var request = new Request(remote, 'account_info');
request.message.account = 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC';
request.filter(function(res) {
return res
&& typeof res === 'object'
&& !res.hasOwnProperty('error');
});
request.callback(function(err, res) {
assert.ifError(err);
setImmediate(function() {
assert.strictEqual(requests, 2, 'Failed to broadcast');
assert.deepEqual(res, successResponse);
done();
});
});
});
it('Send request -- filterRequest -- server fails to reconnect', function(done) {
var servers = [
makeServer('wss://localhost:5006'),
makeServer('wss://localhost:5007')
];
var requests = 0;
var successResponse = {
account_data: {
Account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
Balance: '13188802787',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 17,
PreviousTxnID: 'C6A2313CD9E34FFA3EB42F82B2B30F7FE12A045F1F4FDDAF006B25D7286536DD',
PreviousTxnLgrSeq: 8828020,
Sequence: 1406,
index: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05'
},
ledger_current_index: 9022821,
validated: false
};
var errorResponse = {
error: 'remoteError',
error_message: 'Remote reported an error.',
remote: {
id: 3,
status: 'error',
type: 'response',
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
error: 'actNotFound',
error_code: 15,
error_message: 'Account not found.',
ledger_current_index: 9022856,
request: {
account: 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC',
command: 'account_info',
id: 3
},
validated: false
}
};
function checkRequest(req) {
assert(req instanceof Request);
assert.strictEqual(typeof req.message, 'object');
assert.strictEqual(req.message.command, 'account_info');
};
servers[0]._connected = false;
servers[0]._shouldConnect = true;
servers[0].removeAllListeners('connect');
setTimeout(function() {
servers[0]._connected = true;
servers[0].emit('connect');
}, 20);
servers[0]._request = function(req) {
++requests;
checkRequest(req);
req.emit('success', successResponse);
};
servers[1]._request = function(req) {
++requests;
checkRequest(req);
req.emit('error', errorResponse);
};
var remote = new Remote();
remote._connected = true;
remote._servers = servers;
var request = new Request(remote, 'account_info');
request.setReconnectTimeout(10);
request.message.account = 'rnoFoLJmqmXe7a7iswk19yfdMHQkbQNrKC';
request.filter(function(res) {
return res
&& typeof res === 'object'
&& !res.hasOwnProperty('error');
});
request.callback(function(err, res) {
setTimeout(function() {
// Wait for the request that would emit 'success' to time out
assert.deepEqual(err, new RippleError(errorResponse));
assert.deepEqual(servers[0].listeners('connect'), [ ]);
done();
}, 20);
});
});
it('Events API', function(done) {
@@ -349,6 +726,16 @@ describe('Request', function() {
assert.strictEqual(request.message.ledger_hash, void(0));
});
it('Select ledger - index (String)', function() {
var remote = new Remote();
remote._connected = true;
var request = new Request(remote, 'server_info');
request.ledgerSelect('7016915');
assert.strictEqual(request.message.ledger_index, 7016915);
assert.strictEqual(request.message.ledger_hash, void(0));
});
it('Select ledger - hash', function() {
var remote = new Remote();
remote._connected = true;

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var config = require('./testutils').get_config();
var Seed = require('ripple-lib').Seed;
describe('Seed', function() {
it('can generate many addresses', function () {

View File

@@ -1,7 +1,6 @@
var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var sjcl = require('./../src/js/ripple/utils').sjcl;
var SerializedObject = require('ripple-lib').SerializedObject;
var sjcl = require('ripple-lib').sjcl;
// Shortcuts
var hex = sjcl.codec.hex;
@@ -30,10 +29,14 @@ describe('Serialized object', function() {
{
account: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV',
currency: 'USD',
issuer: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV'
issuer: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV',
type: 49,
type_hex: "0000000000000031"
},
{
currency: 'XRP'
currency: 'XRP',
type: 16,
type_hex: "0000000000000010"
}
]],
SendMax: {
@@ -127,10 +130,10 @@ describe('Serialized object', function() {
it('should serialize and parse - full memo, all strings text/plain ', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": "some data"
Memo: {
MemoType: '74657374',
MemoFormat: '74657874',
MemoData: '736F6D652064617461'
}
}
];
@@ -149,10 +152,11 @@ describe('Serialized object', function() {
it('should serialize and parse - full memo, all strings, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "application/json",
"MemoData": "some data"
"Memo":
{
MemoType: '74657374',
MemoFormat: '6170706C69636174696F6E2F6A736F6E',
MemoData: '736F6D652064617461'
}
}
];
@@ -168,36 +172,14 @@ describe('Serialized object', function() {
assert.strictEqual(input_json.Memos[0].Memo.parsed_memo_data, void(0));
});
it('should throw an error - full memo, json data, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: MemoData can only be a JSON object with a valid json MemoFormat \(Memo\) \(Memos\)/);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, ignored field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"ignored" : "ignored",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
Memo: {
MemoType: '74657374',
MemoFormat: '6A736F6E',
ignored : 'ignored',
MemoData: '7B22737472696E67223A22736F6D655F737472696E67222C22626F6F6C65616E223A747275657D'
}
}
];
@@ -222,74 +204,20 @@ describe('Serialized object', function() {
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = {
"string" : "some_string",
"boolean" : true
};
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
{
"string" : "some_string",
"boolean" : true
}
));
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, integer', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": 3
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = 3;
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.parse(3));
assert.deepEqual(so, input_json);
});
it('should throw an error - invalid Memo field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoParty": "json",
"MemoData": 3
Memo: {
MemoType: '74657374',
MemoField: '6A736F6E',
MemoData: '7B22737472696E67223A22736F6D655F737472696E67222C22626F6F6C65616E223A747275657D'
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: JSON contains unknown field: "MemoParty" \(Memo\) \(Memos\)/);
}, /^Error: JSON contains unknown field: "MemoField" \(Memo\) \(Memos\)/);
});
@@ -303,7 +231,7 @@ describe('Serialized object', function() {
Memos: [
{
Memo: {
MemoType: 'image'
MemoType: '696D616765'
}
}
],
@@ -325,4 +253,4 @@ describe('Serialized object', function() {
});
// vim:sw=2:sts=2:ts=8:et
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,11 +1,8 @@
var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var types = utils.load_module('serializedtypes');
var amountConstants = require('../src/js/ripple/amount').consts;
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
var config = require('./testutils').get_config();
var SerializedObject = require('ripple-lib').SerializedObject;
var types = require('ripple-lib').types;
var Amount = require('ripple-lib').Amount;
var sjcl = require('ripple-lib').sjcl;
describe('Serialized types', function() {
describe('Int8', function() {
@@ -290,7 +287,7 @@ describe('Serialized types', function() {
var so = new SerializedObject("8B2386F26F8E232B");
var num = types.Int64.parse(so);
// We get a positive number
assert.strictEqual(num.toString(16), '8b2386f26f8e232b');
assert.strictEqual(num.toString(), '0x8b2386f26f8e232b');
});
it('Serialize "0123456789ABCDEF"', function () {
var so = new SerializedObject();
@@ -302,15 +299,15 @@ describe('Serialized types', function() {
types.Int64.serialize(so, 'F0E1D2C3B4A59687');
assert.strictEqual(so.to_hex(), 'F0E1D2C3B4A59687');
});
it('Serialize BigInteger("FFEEDDCCBBAA9988")', function () {
it('Serialize bn("FFEEDDCCBBAA9988")', function () {
var so = new SerializedObject();
types.Int64.serialize(so, new BigInteger('FFEEDDCCBBAA9988', 16));
types.Int64.serialize(so, new sjcl.bn('FFEEDDCCBBAA9988', 16));
assert.strictEqual(so.to_hex(), 'FFEEDDCCBBAA9988');
});
it('Fail to serialize BigInteger("-1")', function () {
it('Fail to serialize BigNumber("-1")', function () {
var so = new SerializedObject();
assert.throws(function () {
types.Int64.serialize(so, new BigInteger('-1', 10));
types.Int64.serialize(so, new BigNumber('-1', 10));
});
});
it('Fail to serialize "10000000000000000"', function () {
@@ -346,7 +343,7 @@ describe('Serialized types', function() {
it('Parse "0123456789ABCDEF"', function () {
var so = new SerializedObject("0123456789ABCDEF");
var num = types.Int64.parse(so);
assert.strictEqual(num.toString(10), '81985529216486895');
assert.strictEqual(num.toString(), '0x123456789abcdef');
});
});
@@ -512,8 +509,10 @@ describe('Serialized types', function() {
});
it('Serialize 1161981756646125568 XRP', function () {
var so = new SerializedObject();
types.Amount.serialize(so, '1161981756646125696');
assert.strictEqual(so.to_hex(), '5020304050607080');
assert.throws(function() {
var amt = Amount.from_json('1161981756646125696');
types.Amount.serialize(so, amt);
});
});
it('Serialize 1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject();
@@ -553,7 +552,7 @@ describe('Serialized types', function() {
});
it('Serialize max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject();
types.Amount.serialize(so, amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
types.Amount.serialize(so, Amount.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(so.to_hex(), 'EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
});
it('Parse 1 XRP', function () {
@@ -573,8 +572,11 @@ describe('Serialized types', function() {
assert.strictEqual(types.Amount.parse(so).to_json(), '270544960');
});
it('Parse 1161981756646125568 XRP', function () {
var so = new SerializedObject('5020304050607080');
assert.strictEqual(types.Amount.parse(so).to_json(), '1161981756646125696');
assert.throws(function() {
// hex(1161981756646125568) = 1020304050607000
var so = new SerializedObject('1020304050607000');
types.Amount.parse(so).to_json();
})
});
it('Parse 1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject('D4838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
@@ -590,7 +592,7 @@ describe('Serialized types', function() {
});
it('Parse max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject('EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
assert.strictEqual(types.Amount.parse(so).to_text_full(), amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(types.Amount.parse(so).to_text_full(), Amount.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
@@ -676,7 +678,7 @@ describe('Serialized types', function() {
it('Serialize path through XRP', function () {
var hex = '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000000000000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100';
var json = [
[ {
[{
account: "rrrrrrrrrrrrrrrrrrrrNxV3Xza",
currency: 'USD',
issuer: "rrrrrrrrrrrrrrrrrrrpYnYCNYf"
@@ -690,18 +692,39 @@ describe('Serialized types', function() {
}]
];
var result_json = [
[{
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
type: 49,
type_hex: '0000000000000031'
}],
[{
currency: 'XRP',
type: 16,
type_hex: '0000000000000010'
}, {
account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
currency: 'EUR',
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2',
type: 49,
type_hex: '0000000000000031'
}]
];
var so = new SerializedObject();
types.PathSet.serialize(so, json);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject(hex);
var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so));
assert.deepEqual(parsed_path, json);
assert.deepEqual(parsed_path, result_json);
});
it('Serialize path through XRP IOUs', function () {
var hex = '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000058525000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100';
var json = [
[ {
[{
account: "rrrrrrrrrrrrrrrrrrrrNxV3Xza",
currency: 'USD',
issuer: "rrrrrrrrrrrrrrrrrrrpYnYCNYf"
@@ -716,13 +739,35 @@ describe('Serialized types', function() {
}]
];
var result_json = [
[{
account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
type: 49,
type_hex: '0000000000000031'
}],
[{
currency: 'XRP',
non_native: true,
type: 16,
type_hex: '0000000000000010'
}, {
account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
currency: 'EUR',
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2',
type: 49,
type_hex: '0000000000000031'
}]
];
var so = new SerializedObject();
types.PathSet.serialize(so, json);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject(hex);
var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so));
assert.deepEqual(parsed_path, json);
assert.deepEqual(parsed_path, result_json);
});
it('Serialize path through XRP IOUs (realistic example)', function () {
// Appears in the history
@@ -781,13 +826,87 @@ describe('Serialized types', function() {
}]
];
var result_json = [
[{
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
type: 49,
type_hex: '0000000000000031'
}, {
account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
currency: 'BTC',
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
type: 49,
type_hex: '0000000000000031'
}, {
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
type: 49,
type_hex: '0000000000000031'
}, {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
type: 48,
type_hex: '0000000000000030'
}],
[{
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
type: 49,
type_hex: '0000000000000031'
}, {
account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
currency: 'BTC',
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
type: 49,
type_hex: '0000000000000031'
}, {
account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
currency: 'BTC',
issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
type: 49,
type_hex: '0000000000000031'
}, {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
type: 48,
type_hex: '0000000000000030'
}],
[{
account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
type: 49,
type_hex: '0000000000000031'
}, {
account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
currency: 'BTC',
issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
type: 49,
type_hex: '0000000000000031'
}, {
currency: 'XRP',
non_native: true,
type: 16,
type_hex: '0000000000000010'
}, {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
type: 48,
type_hex: '0000000000000030'
}]
];
var so = new SerializedObject();
types.PathSet.serialize(so, json);
assert.strictEqual(so.to_hex(), hex);
so = new SerializedObject(hex);
var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so));
assert.deepEqual(parsed_path, json);
assert.deepEqual(parsed_path, result_json);
});
it('Parse single empty path [[]]', function () {
var so = new SerializedObject('00');
@@ -800,13 +919,20 @@ describe('Serialized types', function() {
var parsed_path = types.PathSet.parse(so);
var comp = [ [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' } ],
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
type: 49,
type_hex: '0000000000000031' } ],
[ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' },
issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf',
type: 49,
type_hex: '0000000000000031' },
{ account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3',
currency: 'EUR',
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2' } ] ];
issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2',
type: 49,
type_hex: '0000000000000031' } ] ];
assert.deepEqual(SerializedObject.jsonify_structure(parsed_path, ""), comp);
});
});

View File

@@ -1,10 +1,9 @@
var assert = require('assert');
var ws = require('ws');
var utils = require('./testutils');
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Request = utils.load_module('request').Request;
var Transaction = utils.load_module('transaction').Transaction;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Request = require('ripple-lib').Request;
var Transaction = require('ripple-lib').Transaction;
describe('Server', function() {
it('Server constructor - invalid options', function() {
@@ -379,8 +378,6 @@ describe('Server', function() {
validated_ledgers: '32570-7053695'
}
}));
wss.close();
});
});
@@ -388,6 +385,7 @@ describe('Server', function() {
server.once('connect', function() {
server.once('disconnect', function() {
wss.close();
done();
});
server.disconnect();
@@ -443,8 +441,6 @@ describe('Server', function() {
validated_ledgers: '32570-7053695'
}
}));
wss.close();
});
});
@@ -452,6 +448,7 @@ describe('Server', function() {
server.once('connect', function() {
server.once('disconnect', function() {
wss.close();
done();
});
server.disconnect();
@@ -510,15 +507,16 @@ describe('Server', function() {
validated_ledgers: '3175520-3176615'
}
}));
wss.close();
});
});
var server = new Server(new Remote({ allow_partial_history: false }), 'ws://localhost:5748');
var server = new Server(new Remote({
allow_partial_history: false
}), 'ws://localhost:5748');
server.reconnect = function() {
setImmediate(function() {
wss.close();
done();
});
};
@@ -555,8 +553,6 @@ describe('Server', function() {
server_status: 'syncing'
}
}));
wss.close();
});
});
@@ -568,6 +564,7 @@ describe('Server', function() {
assert.strictEqual(server._load_factor, 256);
assert.strictEqual(server._fee_base, 10);
assert.strictEqual(server._fee_ref, 10);
wss.close();
done();
});
@@ -1103,12 +1100,15 @@ describe('Server', function() {
case 'subscribe':
assert.strictEqual(m.command, 'subscribe');
assert.deepEqual(m.streams, [ 'ledger', 'server' ]);
sendSubscribe(m);
setImmediate(function() {
sendSubscribe(m);
});
break;
case 'server_info':
assert.strictEqual(m.command, 'server_info');
sendServerInfo(m);
wss.close();
setImmediate(function() {
sendServerInfo(m);
});
break;
}
});
@@ -1117,19 +1117,86 @@ describe('Server', function() {
var server = new Server(new Remote(), 'ws://localhost:5748');
server.once('connect', function() {
var receivedSubscribe = false;
server.once('response_server_info', function() {
receivedSubscribe = true;
});
server.once('disconnect', function() {
assert(receivedSubscribe);
assert.strictEqual(server.getServerID(), 'ws://localhost:5748 (n94pSqypSfddzAVj9qoezHyUoetsrMnwgNuBqRJ3WHvM8aMMf7rW)');
done();
server.once('disconnect', function() {
wss.close();
done();
});
server.disconnect();
});
});
server.connect();
});
it('Track ledger ranges', function(done) {
var wss = new ws.Server({ port: 5748 });
wss.once('connection', function(ws) {
function sendSubscribe(message) {
ws.send(JSON.stringify({
id: message.id,
status: 'success',
type: 'response',
result: {
fee_base: 10,
fee_ref: 10,
ledger_hash: '1838539EE12463C36F2C53B079D807C697E3D93A1936B717E565A4A912E11776',
ledger_index: 7053695,
ledger_time: 455414390,
load_base: 256,
load_factor: 256,
random: 'E56C9154D9BE94D49C581179356C2E084E16D18D74E8B09093F2D61207625E6A',
reserve_base: 20000000,
reserve_inc: 5000000,
server_status: 'full',
validated_ledgers: '32570-7053695',
pubkey_node: 'n94pSqypSfddzAVj9qoezHyUoetsrMnwgNuBqRJ3WHvM8aMMf7rW',
}
}));
};
ws.on('message', function(message) {
var m = JSON.parse(message);
switch (m.command) {
case 'subscribe':
assert.strictEqual(m.command, 'subscribe');
assert.deepEqual(m.streams, [ 'ledger', 'server' ]);
sendSubscribe(m);
break;
}
});
});
var server = new Server(new Remote(), 'ws://localhost:5748');
server.once('connect', function() {
assert.strictEqual(server.hasLedger(32569), false);
assert.strictEqual(server.hasLedger(32570), true);
assert.strictEqual(server.hasLedger(7053695), true);
assert.strictEqual(server.hasLedger(7053696), false);
server.emit('message', {
type: 'ledgerClosed',
fee_base: 10,
fee_ref: 10,
ledger_hash: 'F29E1F2A2617A88E9DAA14F468B169E6875092ECA0B3B1FA2BE1BC5524DE7CB2',
ledger_index: 7053696,
ledger_time: 455327690,
reserve_base: 20000000,
reserve_inc: 5000000,
txn_count: 1
});
assert.strictEqual(server.hasLedger(7053696), true);
assert.strictEqual(server.hasLedger('F29E1F2A2617A88E9DAA14F468B169E6875092ECA0B3B1FA2BE1BC5524DE7CB2'), true);
server.once('disconnect', done);
wss.close();
});
server.connect();
});
});

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var Seed = utils.load_module('seed').Seed;
var Seed = require('ripple-lib').Seed;
function _isNaN(n) {
return typeof n === 'number' && isNaN(n);

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var Seed = require('../src/js/ripple/seed').Seed;
var sjcl = require('ripple-lib').sjcl;
var Seed = require('ripple-lib').Seed;
describe('SJCL ECDSA Canonicalization', function() {
describe('canonicalizeSignature', function() {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('ECDSA signing with recoverable public key', function(){

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('SJCL Extramath', function() {
describe('setBitM', function() {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
describe('SJCL Jacobi', function() {
it('(15/13) = -1', function () {

View File

@@ -1,6 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var sjcl = require('../build/sjcl');
var sjcl = require('ripple-lib').sjcl;
function testExp(vec) {
var actual = new sjcl.bn(vec.g).powermodMontgomery(new sjcl.bn(vec.e),

View File

@@ -1,31 +0,0 @@
var ripple = require('../src/js/ripple');
exports.get_config = get_config;
function get_config() {
var config = { };
try {
config = require('./config');
} catch(exception) {
config = require('./config-example');
}
return load_config(config);
};
exports.load_config = load_config;
function load_config(config) {
return load_module('config').load(config);
};
exports.load_module = load_module;
function load_module(name) {
if (process.env.RIPPLE_LIB_COV) {
return require('../src-cov/js/ripple/' + name)
} else if (!ripple.hasOwnProperty(name)) {
return require('../src/js/ripple/' + name);
} else {
return require('../src/js/ripple')[name];
}
};

View File

@@ -1,7 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var Transaction = utils.load_module('transaction').Transaction;
var TransactionQueue = utils.load_module('transactionqueue').TransactionQueue;
var Transaction = require('ripple-lib').Transaction;
var TransactionQueue = require('ripple-lib').TransactionQueue;
describe('Transaction queue', function() {
it('Push transaction', function() {

View File

@@ -1,10 +1,9 @@
var utils = require('./testutils');
var assert = require('assert');
var Amount = utils.load_module('amount').Amount;
var Transaction = utils.load_module('transaction').Transaction;
var TransactionQueue = utils.load_module('transactionqueue').TransactionQueue;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var Amount = require('ripple-lib').Amount;
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 transactionResult = {
engine_result: 'tesSUCCESS',
@@ -645,6 +644,83 @@ describe('Transaction', function() {
assert.deepEqual(transaction.hash(), expected_hash);
});
it('Get hash - complex transaction including Memo', function() {
var input_json = {
Account: 'rfe8yiZUymRPx35BEwGjhfkaLmgNsTytxT',
Amount: '1',
Destination: 'r9kiSEUEw6iSCNksDVKf9k3AyxjW3r1qPf',
Fee: '10',
Flags: 2147483648,
Memos: [
{
Memo: {
MemoData: '61BAF845AF6D4FB1AC08A58D817261457034F1431FB9997B3460EB355DCA98D9382181A0E0125B4B0D6DAAF9A460D09C9EFDEFB2BE49E545036028A04DDFCFE8CBDD03DA844EEF9235B708574319A0F1186ADA054D2A4E970E73C67BE3232662726CD59C53CA2EF1DC0939C4793B1794932832D08B9B830AA917BB7CADA5B76C93DC5E0C928B93D8C336D6722E4757332A61DEB7E2A601E8D3766D5285A26A8DFAFFFDDED8BD49D471B9D885F3DF0CC031D522197BC248A5E2BEFAB12BC4A8D77BAB1555C8B38C26254C7BE8563D97EDD806EB7BE3872C750F28B41F693CB179849E6E2105B627F63D390FDDEFE863D3E7C28D6465AA158E7D96920E0EEF0BCB7993EC652C97F876F1666DBA9DA0A612FF9FE0A00AF03B2D5DADFC71984CBC93CA5EAE4C50BDDC839C1C6C3EEAAB44E8493BB7940C0C9ACA0BFEC2999DF5109A3EC40E62280E252712CC91476FB45E40EE314A26F3259027349E47FDD1C21A8DBDF58635943A13B7E2690B4CD153E2FA147A035E979BBDD814635BAB79683D7F62A7D7FF433F9DD35D0967F591D6D3776FC8ADF53E04EAB1DBC5863CBB85FC6F8E5C8B75037DEA9FEEB6A4D5FDF0AEE3F1BDD42EB1B976E98784A15C851E4F3B6234BAFFD11204CB2B76A3CBAA02E3B21051FAF012504DF33CAF9567A333DCE2BB5F454D4BA4B319DF43ECBC86DE214A712A4E214E874092DC84E05B',
MemoType: 'D723898B3DB828F061BCE8DA8F3068B31E527CBDB4D0D22F3D4F5F2C6A961A84EF1189E9CBE2741FEC5C7A46011316D6F9A7769C8E8157E5209FED2D3F950E2763BEF07254327B0EDE9C3CFEB248997EDF148BA36E9D1167C87D73FE9F047FF167DF37B0EF30ABF8E5FD0DCC7E7B964EA0E60E8B2C27E2C7C214BD8334CE830B66BBD724172F7ADCEF491B9B495A979819944DF8EB3A13E4F03B2A8D6FFF332E5C9980A540BEDF659DFDDA03EC78F4F0279F90C8BFD494C15708197C2BAE5CA661EC75FB6E6097E7C5435F374332B7A066FBBC14C629E8EE6042A64226B075B9309BF5FE227CEEEB9CEC7A6B79E724BFBA2BA706F28EC9F3702FB3AFB4C74F0411C7EFDE7927D1ED0670C4F426B8C40F09EB715713788902A4374D8CC7E5111BCA39A97D1F9BD3BD56E28E6167E4DC97A7DEF5428B809D03AE72CB5BA1D25DE3523BC182E3B8905666A972A949B20C30C4FBD1D0A2D9AD8E46EFDBE4E46F4E340FE39F4AD315F5D9EBF7ED9BDC6D577375B56E0CD9FAF0BFA02453F90290E0962D6362048F737BEE3E0E1C46ECE61CCAF4C317B4135B2C5B5D5C4EE728002B2116BB1AF21903AB3F2E4E1A4FE4C5D76507C71C50670281DAB334C37503FB851FC25EC85C757976450004EC642E217D7F4B2E4B6DD820B5E3968B79CB9D7706F28714003C63F4B89AD1B6208A56DF5AFF02E5327A8EAA532BD3ED1ED0'
}
}
],
Sequence: 74519,
SigningPubKey: '02BA5A9F27C34830542D7AA460B82D68AB34B410470108EE2DFFD9D280B49DB161',
TransactionType: 'Payment',
TxnSignature: '3045022100EA99CD20B47AB1C7AEF348102B515DB2FA26F1C9E7DC8FCAE72A763CEC37F21102206190F16F509A088E6303ACB66E9A6C1B9886C20B23F55C3B0721F2B97DC0926E',
date: 455562850,
hash: '4907745B5254B1093E037BA3250B95CFAF35C11CC2CA5E538A68FCE39D16F402',
inLedger: 7085699,
ledger_index: 7085699,
meta: {
AffectedNodes: [
{
ModifiedNode: {
FinalFields: {
Account: 'r9kiSEUEw6iSCNksDVKf9k3AyxjW3r1qPf',
Balance: '40900380802',
Flags: 0,
OwnerCount: 6,
Sequence: 98
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: '0EB37649FDF753A78DACB689057E827296F47AB7D04A7CB273C971A207E17960',
PreviousFields: {
Balance: '40900380801'
},
PreviousTxnID: '42C003842B5188E860B087DD509880C82263B31DE3DD6B5E4AC89AF541CF486E',
PreviousTxnLgrSeq: 5088491
}
},
{
ModifiedNode: {
FinalFields: {
Account: 'rfe8yiZUymRPx35BEwGjhfkaLmgNsTytxT',
Balance: '35339588',
Domain: '9888A596C489373CF5C40858A66C8922C54A2BE9521E39CFFD0EF7A9906DAF298CDA4ADA16111A020C8B940CBABC4D39406381E0DE791147FAD0A5729F3546BF47D515FCDC80A85A52701CF9120C64DD6D41D0CF3DF2B56AEF6DBB463BB69F153BEEAC31D5300B8A3AE558122E192D7211DC0E4F547AD96B2E2F30F46AF8B5D76A58A75D764BA6FDC8E748EEC7A29C2F2A71784B8141D1A9E66544FF7C07025827C3BBCD66FA121D5E50407A622C803B33FCFBA4B2A7454BF86C32628DE0259EC0014783871BD3ADAF2E9F4E0FA421A68AFE1EF3ADEDD9CB24E783D284666BA8ABC2428F77D5550BE76751AA500A90E648CF7524CFC8E8785CB1ACBFB5F0AA50',
EmailHash: 'A9527827303A62DA5DB83FE47ACC1B62',
Flags: 1048576,
OwnerCount: 0,
RegularKey: 'rDLNuE5hfxbRzuXaNn7iUQBftoKfYQQtFA',
Sequence: 74520,
WalletLocator: 'D091F672A5A6FCE5AD3CD35BDD7E6FA15D4205D22E5EB36CBFB961D6E355EF9A'
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: 'A85F8FD83E579C50AF595482824E2AC8C747E4673E83537E8CACCE595AA0C590',
PreviousFields: {
Balance: '35339599',
Sequence: 74519
},
PreviousTxnID: '3CB97C94E76F3A26C083DCA702E91446EB51D4DE62B25D8B7BA10F24FDA2F4E2',
PreviousTxnLgrSeq: 7085699
}
}
],
TransactionIndex: 13,
TransactionResult: 'tesSUCCESS',
delivered_amount: '1'
},
validated: true
};
assert.deepEqual(Transaction.from_json(input_json).hash(), input_json.hash);
});
it('Serialize transaction', function() {
var input_json = {
Account : 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
@@ -1052,7 +1128,7 @@ describe('Transaction', function() {
transaction.tx_json.TransactionType = 'Payment';
var memoType = 'message';
var memoFormat = 'application/json';
var memoFormat = 'json';
var memoData = {
string: 'value',
bool: true,
@@ -1065,9 +1141,9 @@ describe('Transaction', function() {
{
Memo:
{
MemoType: memoType,
MemoFormat: memoFormat,
MemoData: memoData
MemoType: '6D657373616765',
MemoFormat: '6A736F6E',
MemoData: '7B22737472696E67223A2276616C7565222C22626F6F6C223A747275652C22696E7465676572223A317D'
}
}
];
@@ -1088,9 +1164,10 @@ describe('Transaction', function() {
var expected = [
{
Memo: {
MemoType: memo.memoType,
MemoData: memo.memoData
Memo:
{
MemoType: '74797065',
MemoData: '64617461'
}
}
];
@@ -1109,26 +1186,36 @@ describe('Transaction', function() {
transaction.addMemo('testkey4', 'text/html', '<html>');
var expected = [
{ Memo: {
MemoType: 'testkey',
MemoData: 'testvalue'
}},
{ Memo: {
MemoType: 'testkey2',
MemoData: 'testvalue2'
}},
{ Memo: {
MemoType: 'testkey3',
MemoFormat: 'text/html'
}},
{ Memo: {
MemoData: 'testvalue4'
}},
{ Memo: {
MemoType: 'testkey4',
MemoFormat: 'text/html',
MemoData: '<html>'
}}
{
Memo: {
MemoType: '746573746B6579',
MemoData: '7465737476616C7565'
}
},
{
Memo: {
MemoType: '746573746B657932',
MemoData: '7465737476616C756532'
}
},
{
Memo: {
MemoType: '746573746B657933',
MemoFormat: '746578742F68746D6C'
}
},
{
Memo: {
MemoData: '7465737476616C756534'
}
},
{
Memo: {
MemoType: '746573746B657934',
MemoFormat: '746578742F68746D6C',
MemoData: '3C68746D6C3E'
}
}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
@@ -1179,7 +1266,7 @@ describe('Transaction', function() {
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: 'some_string'
MemoData: '736F6D655F737472696E67'
}
}
]);
@@ -1190,6 +1277,7 @@ describe('Transaction', function() {
transaction.tx_json.TransactionType = 'Payment';
var memo = {
memoFormat: 'json',
memoData: {
string: 'string',
int: 1,
@@ -1209,20 +1297,22 @@ describe('Transaction', function() {
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: memo.memoData
MemoFormat: '6A736F6E',
MemoData: '7B22737472696E67223A22737472696E67222C22696E74223A312C226172726179223A5B7B22737472696E67223A22737472696E67227D5D2C226F626A656374223A7B22737472696E67223A22737472696E67227D7D'
}
}
]);
});
it('Construct AccountSet transaction', function() {
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
it('Set AccountTxnID', function() {
var transaction = new Transaction();
assert(transaction instanceof Transaction);
transaction.accountTxnID('75C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4F');
assert.deepEqual(transaction.tx_json, {
Flags: 0,
TransactionType: 'AccountSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm'
AccountTxnID: '75C5A92212AA82A89C3824F6F071FE49C95C45DE9113EB51763A217DBACB5B4F'
});
});
@@ -1239,6 +1329,18 @@ describe('Transaction', function() {
});
});
it('Construct AccountSet transaction - with AccountTxnID SetFlag', function() {
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'asfAccountTxnID');
assert(transaction instanceof Transaction);
assert.deepEqual(transaction.tx_json, {
Flags: 0,
SetFlag: 5,
TransactionType: 'AccountSet',
Account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm'
});
});
it('Construct AccountSet transaction - params object', function() {
var transaction = new Transaction().accountSet({
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var utils = require('./testutils');
var UInt128 = utils.load_module('uint128').UInt128;
var config = require('./testutils').get_config();
var UInt128 = require('ripple-lib').UInt128;
describe('UInt', function() {
describe('128', function() {
@@ -11,8 +9,8 @@ describe('UInt', function() {
assert.strictEqual(val.to_hex(), '00000000000000000000000000000000');
});
it('should create 00000000000000000000000000000001 when called with 1', function () {
var val = UInt128.from_number(0);
assert.strictEqual(val.to_hex(), '00000000000000000000000000000000');
var val = UInt128.from_number(1);
assert.strictEqual(val.to_hex(), '00000000000000000000000000000001');
});
it('should create 000000000000000000000000FFFFFFFF when called with 0xFFFFFFFF', function () {
var val = UInt128.from_number(0xFFFFFFFF);

View File

@@ -1,6 +1,5 @@
var fs = require('fs');
var assert = require('assert');
var utils = require('./testutils').load_module('utils');
var utils = require('ripple-lib').utils;
describe('Utils', function() {
describe('hexToString and stringToHex', function() {

View File

@@ -1,851 +0,0 @@
var assert = require('assert');
var RippleTxt = require('../src/js/ripple/rippletxt').RippleTxt;
var AuthInfo = require('../src/js/ripple/authinfo').AuthInfo;
var VaultClient = require('../src/js/ripple/vaultclient').VaultClient;
var Blob = require('../src/js/ripple/blob').Blob;
var UInt256 = require('../src/js/ripple/uint256').UInt256;
var sjcl = require('../build/sjcl');
var nock = require('nock');
var online = process.argv.indexOf('--online-blobvault') !== -1 ? true : false;
var exampleData = {
id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
crypt: 'f0cc91a7c1091682c245cd8e13c246cc150b2cf98b17dd6ef092019c99dc9d82',
unlock: '3e15fe3218a9c664835a6f585582e14480112110ddbe50e5028d05fc5bd9b5f4',
username: 'exampleUser',
new_username : 'exampleUser-rename',
password: 'pass word',
domain: 'staging.ripple.com',
masterkey : 'ssize4HrSYZShMWBtK6BhALGEk8VH',
email_token : '77825040-9096-4695-9cbc-76720f6a8649',
activateLink : 'https://staging.ripple.com/client/#/register/activate/',
device_id : "ac1b6f6dbca98190eb9687ba06f0e066",
identity_id : "17fddb71-a5c2-44ce-8b50-4b381339d4f2",
blob: {
url: 'https://id.staging.ripple.com',
id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
key: 'f0cc91a7c1091682c245cd8e13c246cc150b2cf98b17dd6ef092019c99dc9d82',
data: {
auth_secret: 'd0aa918e693080a6a8d0ddc7f4dcf4bc0eecc3c3e3235f16a98661ee9c2e7a58',
account_id: 'raVUps4RghLYkVBcpMaRbVKRTTzhesPXd',
email: 'example@example.com',
contacts: [ ],
created: '2014-05-20T23:39:52.538Z',
apps: [ ],
lastSeenTxDate: 1401925490000,
identityVault: { },
revision: 2199,
encrypted_secret: 'APYqtqvjJk/J324rx2BGGzUiQ3mtmMMhMsbrUmgxb00W2aFVQzCC2mqd58Z17gzeUUcjtjAm'
}
}
};
var rippleTxtRes = "[authinfo_url]\r\nhttps://id.staging.ripple.com/v1/authinfo";
var authInfoRes = {
body : {
version: 3,
blobvault: 'https://id.staging.ripple.com',
pakdf: {
modulus: 'c7f1bc1dfb1be82d244aef01228c1409c1988943ca9e21431f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd',
alpha: '7283d19e784f48a96062271a5fa6e2c3addf14e6ezf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa',
url: 'https://auth1.ripple.com/api/sign',
exponent: '010001',
host: 'auth1.ripple.com'
},
exists: true,
username: 'exampleUser',
address: 'raVUps4RghLYkVBcpMaRbVKRTTzhesPXd',
emailVerified: true,
reserved: false
}
};
var authInfoNewUsernameRes = {
body : {
version: 3,
blobvault: 'https://id.staging.ripple.com',
pakdf: {
modulus: 'c7f1bc1dfb1be82d244aef01228c1409c1988943ca9e21431f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd',
alpha: '7283d19e784f48a96062271a5fa6e2c3addf14e6ezf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa',
url: 'https://auth1.ripple.com/api/sign',
exponent: '010001',
host: 'auth1.ripple.com'
},
exists: false,
username: exampleData.new_username,
emailVerified: false,
reserved: false
}
};
var signRes = '{"result":"success","signres":"64e9e46618fff0b720b8162e6caa209e046af128b929b766d3be421d3f048ba523453dad42597dcec01f23a5080c16695f6209d39a03668d46b782409e4a53821f70b5e6f7c8fd28eb641c504f9f9b2f378bf2ea7f19950790ac6a8832e2659800f5bb06b735bd450fa47b499fbcebeb3b0fc327619dd2171fa40fb0a41d9bcd69dd29567fa94e9466d4674b908f1cfc43822b38b94534cb37eead183b11b33761a73d78be6ba6f3a53291d4154ca0891fa59da58380e05a1e85b15a24d12406795385bcc5a6360a24ecbf068ff6f02097cd917281972d4895769f3a8668b852ea5d4232050200bcd03934f49ea0693d832980614dff1ead67ca2e0ce9073c25","modulus":"c7f1bc1dfb1be82d244aef01228c1409c198894eca9e21430f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd","alpha":"7283d19e784f48a96062271a4fa6e2c3addf14e6edf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa","exponent":"010001"}';
var blobRes = {
body : {
result: 'success',
encrypted_secret: 'APYqtqvjJk/J324rx2BGGzUiQ3mtmMMhMsbrUmgxb00W2aFVQzCC2mqd58Z17gzeUUcjtjAm',
blob: 'ALXga/k8mgvPpZCY0zJZdaqlptHUBL0E4V/90p4edvb7eCucU2M7aFsHIl3Z3UDu9MdlDnDU42/C+YKL1spkSTPb3rGWr0kXIFmRu8xDAd+OA3Ot7u3OBq0sN2BUHbEc47WiCue84XQHTgBh9tdeiRTqm90LJ7hZ1pD0oqr823YpFguwcC1inxFbSTNxIdWSoC3XCqZtRFM2Y5ALleWhaWKc3OwaFU6yPRcW05IBvTY/7a2SfZyklvXnJh7Bg+vfvz7ms8UCybmBgHlBPY/UqGOdZI6iFGrEQrDMFHgbxwf7bTTiaOM7Su3OsqhM1k90LvQgk3b1olb1VIMZ5J1UuTtOVTpLSsIlzgMvxxdUUyN2zMkeDE3t8kHOThhwWbLG6O+s9F9fktIv4NtoAm0dG9LtkSE1YXajk0qIYr/zrblJy7pEvNv+EzdSr+dpvssmPshgwxoHwvCwae0vL7UTmrCxIWLlHbsbU2uAzgvudJL0WOpX4W+R43U3sgMD2XysKgX783Sa7DLUWCk3Rk9eGp7c3k/XpI0IWvuKzMxID8VzdyMmXP0RE77uUufisBDtwr8gPGzS5kzU3Z/FG/dHJkfBLZdbHOffQTPKO9DKUjztWpx7CTAkN9O21XrdLK1FRAtWFTuvlA66sDYtHRaqglzFjt9DsJk7PAKi1odHeLBmYob/Bs5eK9yNnlLwu3JpHLH/jKxkuxcZ3NEdTm1WPjTdNlvT7kdGAIG9c0vIywEABkQh1kPDOe39h3GRGcUqVWJcMjJmdVDrQH7BBVV+VptCVtMOo1LviaD0MIWMiYdZyGeH5x+FkpAMjKDB3cCUkmxmis8lrDiMlnTZ5Czj+bDPp62Looc7cr2pTR2niFZRosYNgUPx6cAh7tn64RDaa/spAyv0mWyD1qRA8H0sEPmC7m7EPaBIQpODh1NFg/Bxunh+QGSmy9deINB78b9A9zLS6qWljrzg5fMDUN66xRUUKJMSD9+QJePsM4pb60vbnBBtbe04JzY7iOc/CxiT0Px6/1jlSmnY6SCtaFqtDgmQ5MLGTm1tA+aj6caT6FWsXrBboXt3eXRDPHTN+ciKELx7M3dpd4mKVWhBu7nnnVMEu1rSUrmtUStXQHod/C7vVRF2EU1hhTW7ou0hvLn+7xs9B76QeVG7iYFLiZH1qgs+upqnLCnmY3ug9yd9GQ6YwbVL1hbXJLadaOg7qhKst0KXjjjcE4G9AIEyI+UCxGdc/0PNPOCCeYEPshvonCgElGo/fAbaIuSSOFfusiovYffJ0rCkq1RagH0R/llWtUEFEDR5YFVlD3DqK6B22fQK',
revision: 2191,
email: 'example@example.com',
quota: -2975,
patches: []
}
};
var recoverRes = {
body: {
encrypted_secret: 'AAd69B9En2OF4O4LsjD+pFNeJHEGIuLh2hbla58zGvN7qU/16bDfy0QlFj8/Gu++AdFwH5U6',
revision: 2403,
blob_id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
blob: 'AFfW9vuHJ2J5UMnEl4WrVIT9z2d+PPVNNHkqzN64b3pKDQcRPFp8vVEqL9B+YVs/KHhFVFNxxCNyVXwO/yGg4BAslYl8Ioo11IODmOltJmb94oKR/JVyfaY4bDWaOzAoa5N/c9LHpmd0L+9igK1o260MK5OZW4BQ6EG7I+8cYi5uM2CLguiddySu2yTEnyHW47zspWP33y2deh6p5mHtLdii/tmlm7b2rKpzrRVuLN/J09jqilhMxlCEr4X065YZLlQapJ45UWvpifejEw/6Qgl1WngZxwifHa504aR/QYhb1XCNeYbkjQ1MmkTmTef47Al4r/Irzoe//pDbAFA70XXkBUVUMAXWiOxU5V6gHO4yhXbTFEn7922JZlY7PIjo2Q+BxLkozMzuh8MZdoeadqffZX1fOuyTRWfPlqi7vIYgnUyTmThKe2EZv1LsB5ZUaX3KSArKDv1xPTKS0nexGNZoFckwEfVr6B2PGbMx8LPLYEEEmd95kh8NAKN1wkOPuBehLAtbMtcnLpTsotY6diqWdW4V9BSst0KDMTxZVfeesWD7/7ga9hzNvAWO1MN3aAvDCiQVufb44i4Qfu6fLS7+nxtcDCN2PqPHcANcW0cUhUNB50ajzNwRXN8B92CiY0zkS61CzWeooHOslGp0Acau1CJy8iHGyjzbPS4ui8F2h2TbDUuInOoMqiRjXFvRTxA=',
encrypted_blobdecrypt_key: 'AA9vUokfQ1WXEOArl2DUwY3cxgXGKj9uNEqrJQzUu0hqXIWRu1V+6l1qqxXKPnm9BNscMpm0BMSbxUz++lfV50c1B4akvrzIBH+MUUgNyyPcHR7JBgjEYt0=',
patches: [],
result: 'success'
}
}
var getProfileRes = {
"result":"success",
"addresses":[],
"attributes":[{
"attribute_id":"4034e477-ffc9-48c4-bcbc-058293f081d8",
"identity_id":"17fddb71-a5c2-44ce-8b50-4b381339d4f2",
"name":"email",
"type":"default",
"domain":null,
"value":"example@example.com",
"visibility":"public",
"updated":null
}
]
};
var blob = new Blob();
blob.url = exampleData.blob.url;
blob.id = exampleData.blob.id;
blob.device_id = exampleData.device_id;
blob.key = exampleData.blob.key;
blob.identity_id = exampleData.blob.identity_id;
blob.data = exampleData.blob.data;
blob.revision = exampleData.blob.data.revision;
//must be set for self signed certs
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
while(!sjcl.random.isReady()) {
sjcl.random.addEntropy(require('crypto').randomBytes(128).toString('base64')); //add entropy to seed the generator
}
var mockRippleTxt;
var mockRippleTxt2;
var mockAuthSign;
var mockRegister;
var mockBlob;
var mockRename;
var mockUpdate;
var mockRecover;
var mockVerify;
var mockEmail;
var mockProfile;
var mockDelete;
if (!online) {
mockRippleTxt = nock('https://ripple.com')
.get('/ripple.txt')
.reply(200, rippleTxtRes, {
'Content-Type': 'text/plain'
});
mockRippleTxt2 = nock('https://' + exampleData.domain)
.get('/ripple.txt')
.reply(200, rippleTxtRes, {
'Content-Type': 'text/plain'
});
mockAuthSign = nock('https://auth1.ripple.com')
.persist()
.post('/api/sign')
.reply(200, signRes, {
'Content-Type': 'text/plain'
});
mockRegister = nock('https://id.staging.ripple.com');
mockRegister.filteringPath(/(v1\/user\?signature(.+))/g, 'register/')
.post('/register/')
.reply(200, { result: 'error', message: 'User already exists' }, {
'Content-Type': 'application/json'
});
mockDelete = nock('https://id.staging.ripple.com');
mockDelete.filteringPath(/(v1\/user\/(.+))/g, 'delete/')
.delete('/delete/')
.reply(200, { result: 'success' }, {
'Content-Type': 'application/json'
});
mockBlob = nock('https://id.staging.ripple.com');
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.username.toLowerCase())
.reply(200, JSON.stringify(authInfoRes.body), {
'Content-Type': 'application/json'
});
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.new_username.toLowerCase())
.reply(200, JSON.stringify(authInfoNewUsernameRes.body), {
'Content-Type': 'application/json'
});
mockBlob.filteringPath(/(blob\/.+)/g, 'blob/')
.persist()
.get('/v1/blob/')
.reply(200, JSON.stringify(blobRes.body), {
'Content-Type': 'application/json'
});
mockRename = nock('https://id.staging.ripple.com/v1/user/');
mockRename.filteringPath(/((.+)\/rename(.+))/g, 'rename/')
.post('rename/')
.reply(200, {result:'success',message:'rename'}, {
'Content-Type': 'application/json'
});
mockUpdate = nock('https://id.staging.ripple.com/v1/user/');
mockUpdate.filteringPath(/((.+)\/updatekeys(.+))/g, 'update/')
.post('update/')
.reply(200, {result:'success',message:'updateKeys'}, {
'Content-Type': 'application/json'
});
mockRecover = nock('https://id.staging.ripple.com/')
mockRecover.filteringPath(/((.+)user\/recov\/(.+))/g, 'recov/')
.get('recov/')
.reply(200, recoverRes.body, {
'Content-Type': 'application/json'
});
mockVerify = nock('https://id.staging.ripple.com/v1/user/');
mockVerify.filteringPath(/((.+)\/verify(.+))/g, 'verify/')
.get('verify/')
.reply(200, {result:'error', message:'invalid token'}, {
'Content-Type': 'application/json'
});
mockEmail = nock('https://id.staging.ripple.com/v1/user');
mockEmail.filteringPath(/((.+)\/email(.+))/g, 'email/')
.post('email/')
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
}
describe('Ripple Txt', function () {
it('should get the content of a ripple.txt file from a given domain', function(done) {
RippleTxt.get(exampleData.domain, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
done();
});
});
it('should get currencies from a ripple.txt file for a given domain', function(done) {
RippleTxt.getCurrencies(exampleData.domain, function(err, currencies) {
assert.ifError(err);
assert(Array.isArray(currencies));
done();
});
});
it('should get the domain from a given url', function() {
var domain = RippleTxt.extractDomain("http://www.example.com");
assert.strictEqual(typeof domain, 'string');
});
});
describe('AuthInfo', function() {
it('should get auth info', function(done) {
AuthInfo.get(exampleData.domain, exampleData.username, function(err, resp) {
assert.ifError(err);
Object.keys(authInfoRes.body).forEach(function(prop) {
assert(resp.hasOwnProperty(prop));
});
done();
});
});
});
describe('VaultClient', function () {
var client = new VaultClient(exampleData.domain);
describe('#initialization', function() {
it('should be initialized with a domain', function() {
var client = new VaultClient({ domain: exampleData.domain });
assert.strictEqual(client.domain, exampleData.domain);
});
it('should default to ripple.com without a domain', function () {
var client = new VaultClient();
assert.strictEqual(client.domain, 'ripple.com');
});
});
describe('#exists', function() {
it('should determine if a username exists on the domain', function(done) {
this.timeout(10000);
client.exists(exampleData.username, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'boolean');
done();
});
});
});
describe('#login', function() {
it('with username and password should retrive the blob, crypt key, and id', function(done) {
this.timeout(10000);
client.login(exampleData.username, exampleData.password, exampleData.device_id, function(err, resp) {
if (online) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert(resp.blob instanceof Blob);
assert.strictEqual(typeof resp.blob.id, 'string');
assert(UInt256.from_json(resp.blob.id).is_valid());
assert.strictEqual(typeof resp.blob.key, 'string');
assert(UInt256.from_json(resp.blob.key).is_valid());
assert.strictEqual(typeof resp.username, 'string');
assert.strictEqual(typeof resp.verified, 'boolean');
} else {
assert(err instanceof Error);
assert.strictEqual(resp, void(0));
}
done();
});
});
});
describe('#relogin', function() {
it('should retrieve the decrypted blob with blob vault url, id, and crypt key', function(done) {
this.timeout(10000);
client.relogin(exampleData.blob.url, exampleData.id, exampleData.crypt, exampleData.device_id, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert(resp.blob instanceof Blob);
done();
});
});
});
describe('#unlock', function() {
it('should access the wallet secret using encryption secret, username and password', function (done) {
this.timeout(10000);
client.unlock(exampleData.username, exampleData.password, exampleData.blob.data.encrypted_secret, function(err, resp) {
if (online) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.keys, 'object');
assert.strictEqual(typeof resp.keys.unlock, 'string');
assert(UInt256.from_json(resp.keys.unlock).is_valid());
} else {
assert.strictEqual(err.toString(), 'CORRUPT: ccm: tag doesn\'t match');
assert.strictEqual(resp, void(0));
}
done();
});
});
});
describe('#loginAndUnlock', function () {
it('should get the decrypted blob and decrypted secret given name and password', function (done) {
this.timeout(10000);
client.loginAndUnlock(exampleData.username, exampleData.password, exampleData.device_id, function(err, resp) {
if (online) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert(resp.blob instanceof Blob);
assert.strictEqual(typeof resp.blob.id, 'string');
assert(UInt256.from_json(resp.blob.id).is_valid());
assert.strictEqual(typeof resp.blob.key, 'string');
assert(UInt256.from_json(resp.blob.key).is_valid());
assert.strictEqual(typeof resp.unlock, 'string');
assert(UInt256.from_json(resp.unlock).is_valid());
assert.strictEqual(typeof resp.secret, 'string');
assert.strictEqual(typeof resp.username, 'string');
assert.strictEqual(typeof resp.verified, 'boolean');
} else {
assert(err instanceof Error);
assert.strictEqual(resp, void(0));
}
done();
});
});
});
describe('#register', function () {
it('should create a new blob', function (done) {
this.timeout(10000);
var options = {
username : exampleData.username,
password : exampleData.password,
email : exampleData.blob.data.email,
activateLink : exampleData.activateLink
}
client.register(options, function(err, resp) {
//fails, user already exists
assert(err instanceof Error);
assert.strictEqual(resp, void(0));
done();
});
});
});
describe('#deleteBlob', function () {
it('should remove an existing blob', function (done) {
this.timeout(10000);
var options = {
url : exampleData.blob.url,
blob_id : exampleData.blob.id,
username : online ? "" : exampleData.username,
account_id : exampleData.blob.data.account_id,
masterkey : exampleData.masterkey
}
client.deleteBlob(options, function(err, resp) {
if (online) {
//removing the username will result in an error from the server
assert(err instanceof Error);
assert.strictEqual(resp, void(0));
} else {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
}
done();
});
});
});
/*
describe('#updateProfile', function () {
it('should update profile parameters associated with a blob', function (done) {
this.timeout(10000);
var options = {
url : exampleData.blob.url,
blob_id : exampleData.blob.id,
username : exampleData.username,
auth_secret : exampleData.blob.data.auth_secret,
profile : {
city : "San Francisco",
phone : "555-555-5555"
}
}
client.updateProfile(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
});
*/
});
describe('Blob', function () {
var client;
var resp;
client = new VaultClient({ domain: exampleData.domain });
before(function(done) {
if (online) {
this.timeout(10000);
client.login(exampleData.username, exampleData.password, exampleData.device_id, function(err, res) {
resp = res;
blob = res.blob;
done();
});
} else {
mockBlob.filteringPath(/(blob\/.+)/g, 'blob/')
.persist()
.post('/v1/blob/')
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
done();
}
});
describe('#rename', function () {
it('should change the username of a blob', function (done) {
this.timeout(20000);
var options = {
username : exampleData.username,
new_username : exampleData.new_username,
password : exampleData.password,
masterkey : exampleData.masterkey,
blob : blob
}
client.rename(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
assert.strictEqual(typeof resp.message, 'string');
if (online) {
options.username = exampleData.new_username;
options.new_username = exampleData.username;
//change it back
client.rename(options, function(err,resp){
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
assert.strictEqual(typeof resp.message, 'string');
done();
});
} else {
done();
}
});
});
});
describe('#changePassword', function () {
it('should change the password and keys of a blob', function (done) {
this.timeout(10000);
var options = {
username : exampleData.username,
password : exampleData.password,
masterkey : exampleData.masterkey,
blob : blob
}
client.changePassword(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
assert.strictEqual(typeof resp.message, 'string');
done();
});
});
});
describe('#recoverBlob', function () {
it('should recover the blob given a username and secret', function (done) {
this.timeout(10000);
var options = {
url : exampleData.blob.url,
username : exampleData.username,
masterkey : exampleData.masterkey,
}
client.recoverBlob(options, function(err, blob) {
assert.ifError(err);
assert(blob instanceof Blob);
done();
});
});
});
describe('#verifyEmail', function () {
it('should verify an email given a username and token', function (done) {
this.timeout(10000);
client.verify(exampleData.username, exampleData.email_token, function(err, resp) {
//result will be error, because of invalid token
assert(err instanceof Error);
assert.strictEqual(resp, void(0));
done();
});
});
});
describe('#resendVerifcationEmail', function () {
it('should resend a verification given options', function (done) {
this.timeout(10000);
var options = {
url : exampleData.blob.url,
id : exampleData.blob.id,
username : exampleData.username,
account_id : exampleData.blob.data.account_id,
email : exampleData.blob.data.email,
activateLink : exampleData.activateLink,
masterkey : exampleData.masterkey
}
client.resendEmail(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
});
it('#set', function(done) {
this.timeout(10000)
blob.extend('/testObject', {
foo: [],
}, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#extend', function(done) {
this.timeout(10000)
blob.extend('/testObject', {
foobar: 'baz',
}, function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#unset', function(done) {
this.timeout(10000)
blob.unset('/testObject', function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#unshift', function(done) {
this.timeout(10000)
blob.unshift('/testArray', {
name: 'bob',
address: '1234'
}, function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#filter', function(done) {
this.timeout(10000)
blob.filter('/testArray', 'name', 'bob', 'extend', '', {description:'Alice'}, function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#consolidate', function(done) {
this.timeout(10000)
blob.unset('/testArray', function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
blob.consolidate(function(err, resp){
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
});
describe('identity', function() {
it('#identity_set', function (done) {
this.timeout(10000);
blob.identity.set('address', exampleData.unlock, {city:"San Francisco", region:"CA"}, function (err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
it('#identity_get', function () {
var property = blob.identity.get('address', exampleData.unlock);
assert.ifError(property.error);
assert.strictEqual(typeof property.encrypted, 'boolean');
assert.notEqual(typeof property.value, 'undefined');
});
it('#identity_getAll', function () {
var obj = blob.identity.getAll(exampleData.unlock);
assert.strictEqual(typeof obj, 'object');
});
it('#identity_getFullAddress', function () {
var address = blob.identity.getFullAddress(exampleData.unlock);
assert.strictEqual(typeof address, 'string');
});
it('#identity_unset', function (done) {
this.timeout(10000);
blob.identity.unset('name', exampleData.unlock, function (err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
done();
});
});
});
describe('identityVault', function() {
it('#identity - Get Attestation', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
options.type = 'identity';
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/identity(.+))/g, '')
.post('/')
.reply(200, {
result: 'success',
status: 'verified',
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
}, {'Content-Type': 'application/json'});
client.getAttestation(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.blinded, 'string');
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
done();
});
});
it('#identity - Update Attestation', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
options.type = 'identity';
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/identity\/update(.+))/g, '')
.post('/')
.reply(200, {
result: 'success',
status: 'verified',
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
}, {'Content-Type': 'application/json'});
client.updateAttestation(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.blinded, 'string');
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
done();
});
});
it('#identity - Get Attestation Summary', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/summary(.+))/g, '')
.get('/')
.reply(200, {
result: 'success',
attestation: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2ZGI3MzgxIn0%3D.eyJwcm9maWxlX3ZlcmlmaWVkIjpmYWxzZSwiaWRlbnRpdHlfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2lkLnJpcHBsZS5jb20iLCJzdWIiOiIwNDMzNTA0ZS0yYTRmLTQ1NjktODQwMi1lYWI2YTU0YTgzYjUiLCJleHAiOjE0MTI4MTc2NjksImlhdCI6MTQxMjgxNTgwOX0%3D.Jt14Y2TsM7fKqGWn0j16cPldlYqRr7%2F2dptBsdZuZhRGRTREO4TSpZZhBaU95WL3M9eXIfaoSs8f2pTOa%2BBGAYHZSZK4%2FLqeWdDH8zz8Bx9YFqGije1KmHQR%2FeoWSp1GTEfcq5Oho4nSHozHhGNN8IrDkl8woMvWb%2FE1938Y5Zl2vyv7wjlNUF4ND33XWzJkvQjzIK15uYfaB%2FUIsNW32udfHAdkigesdMDNm%2BRGBqHMDZeAMdVxzrDzE3m8oWKDMJXbcaLmk75COfJrLWYiZCHd7VcReyPEZegwEucetZJ9uDnoBcvw0%2B6hIRmjTN6Gy1eeBoJaiDYsWuOwInbIlw%3D%3D',
}, {'Content-Type': 'application/json'});
client.getAttestationSummary(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.decoded.header, 'object');
assert.strictEqual(typeof resp.decoded.payload, 'object');
assert.strictEqual(typeof resp.decoded.signature, 'string');
done();
});
});
});
//only do these offline
if (!online) {
describe('2FA', function() {
it('#2FA_set2FA', function (done) {
blob.set2FA({masterkey:exampleData.masterkey}, function(err, resp){
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
it('#2FA_get2FA', function (done) {
blob.get2FA(function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
it('#2FA_requestToken', function (done) {
client.requestToken(exampleData.blob.url, exampleData.blob.id, function(err, resp){
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
it('#2FA_verifyToken', function (done) {
var options = {
url : exampleData.blob.url,
id : exampleData.blob.id,
device_id : client.generateDeviceID(),
token : "5555",
remember_me : true
}
client.verifyToken(options, function(err, resp){
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');
done();
});
});
});
}
if (!online) {
after(function () {
nock.restore();
});
}
});