Compare commits

...

275 Commits

Author SHA1 Message Date
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
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
b14343f3cf [TASK] shrinkwrap dependencies 2015-01-06 14:48:13 -08:00
Geert Weening
732b50dea7 [TASK] bump version to 0.10.0 2015-01-06 14:48:05 -08:00
Geert Weening
be3bbe9b61 [DOC] update release notes 2015-01-06 14:46:44 -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
Geert Weening
1eaad617cb [TASK] bump version to 0.10.0-rc2 2014-12-11 00:20:50 -08:00
wltsmrz
3c21994adc [FIX] Fix empty queue check on reconnect 2014-12-10 23:58:00 -08:00
wltsmrz
d15d14e197 [FIX] Fix undefined Transaction.prototype.summary() 2014-12-10 14:17:28 -08:00
Geert Weening
e32694dc79 [TASK] bump version to 0.10.0-rc1 2014-12-10 14:02:48 -08:00
wltsmrz
6ec8124287 [DOC] Update release notes 2014-12-10 14:00:10 -08:00
Geert Weening
2222adfc10 [DOC] update release notes 2014-12-10 13:29:47 -08:00
Geert Weening
fcc2377657 Merge pull request #223 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-10 13:08:04 -08:00
Geert Weening
1704ac4ae1 [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat

[FIX] double serialization overriding Memo contents

The copy made in from_json wasn't a deep copy
2014-12-10 13:07:44 -08:00
Geert Weening
666e4348e0 Merge pull request #222 from boxbag/transaction-set-fee
[FEATURE] allow per transaction fees to be set
2014-12-10 13:07:20 -08:00
Bo Chen
9b22f279bc [FEATURE] allow per transaction fees to be set 2014-12-10 11:52:16 -08:00
Geert Weening
0835de983b Merge pull request #221 from ripple/transaction-updates
Transaction updates
2014-12-10 11:37:36 -08:00
wltsmrz
1a892d58fc Rework tef and tel-class transaction error handling
Do not finalize tef or tel-class errors until LastLedgerSequence is
exceeded. Transactions that fail in this way will now be aborted
with a tej-class error. Errors like tefALREADY and tefMAX_LEDGER
should now be opaque to the user and have no consequence in
determining a final state.
2014-12-10 11:30:45 -08:00
wltsmrz
73a3cce4a4 Do not bump LastLedgerSequence on resubmit 2014-12-10 11:30:45 -08:00
wltsmrz
d5ef4774fa Make LastLedgerSequence offset configurable, default to 3 2014-12-10 11:30:45 -08:00
wltsmrz
c5bd4239a4 Cleanup
- Deprecate 'save' event
- Add TransactionQueue.getMinLedger(), use this as ledger_index_min
  in account_tx request on reconnect
- tx.sign() no longer accepts a callback
- Add various setters and jsdoc to transaction.js
- Normalize setters, e.g. sourceTag() and destinationTag()
- Minor optimization in call to tx.hash() in TransactionManager prior
  to submit; allow `serialized` argument to tx.hash() such that the
  transaction is not serialized twice
2014-12-10 11:30:45 -08:00
Geert Weening
634e811888 Merge pull request #220 from geertweening/update_sjcl
[TASK] update sjcl
2014-12-08 15:21:13 -08:00
Geert Weening
3204998fcb [TASK] update sjcl
- native support for k256
- improved entropy by taking advantage of platform crypto

- remove unnecessary sjcl overrides from sjcl-secp256k1.js
- updated ripple-lib curve instantiations to use k256
- add curve override so c256 points to k256

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

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

change `requestAccountLines` and `requestAccountOffers` to use the `pagingAccountRequest`
2014-11-06 18:40:45 -08:00
Geert Weening
1ad6e5a15f [FIX] from_human 'XRP' with full name
from_human 'XRP - Ripples' should result in native XRP
2014-11-05 16:54:45 -08:00
Geert Weening
3554572db7 [TASK] bump version to 0.9.2-rc2 2014-11-05 16:32:37 -08:00
Geert Weening
f1abff962f Merge pull request #200 from boxbag/fix-max-fee
[FIX] fix test and do not set tx_json.Fee in maxFee method
2014-11-05 16:07:01 -08:00
Geert Weening
f05941fbc4 [FIX] cap IOU Amounts to their max and min value
respect rippled's limits
2014-11-05 16:04:02 -08:00
Bo Chen
237c46d5a0 [FIX] fix test and do not set tx_json.Fee in maxFee method 2014-11-05 15:59:57 -08:00
Geert Weening
76cfb69d9f [TASK] bump version to 0.9.2-rc1 2014-11-05 14:38:47 -08:00
Geert Weening
7610df0fbb [DOC] update release notes 2014-11-05 14:38:35 -08:00
Geert Weening
8bc935aa62 Merge pull request #198 from boxbag/transaction-max-fee
[FEATURE] set max fee the submitter of a transaction is willing to pay
2014-11-05 09:27:26 -08:00
Bo Chen
24587fab9c [FEATURE] set max fee the submitter of a transaction is willing to pay 2014-11-04 14:13:42 -08:00
Geert Weening
0248475473 [DOC] update description in package.json 2014-10-31 18:52:09 -07:00
Geert Weening
d2fa5c4b12 [TASK] bump version to 0.9.1 2014-10-30 10:01:16 -07:00
Geert Weening
c60c0cb6e0 [TASK] bump version to 0.9.1-rc3 2014-10-28 17:35:29 -07:00
Geert Weening
cdf1112666 [FIX] ledgerSelect setting both ledger_index and ledger_hash 2014-10-28 17:33:55 -07:00
Geert Weening
d861bb2e34 [TASK] bump version to 0.9.1-rc2 2014-10-27 17:32:37 -07:00
Geert Weening
006849a3d5 [DOC] update release notes 2014-10-27 17:32:18 -07:00
wltsmrz
a3c1d06eba Change initial account transaction sequence to 1 2014-10-27 17:29:51 -07:00
Geert Weening
4bd1e7a2bc [TASK] bump version to 0.9.1-rc1 2014-10-27 16:40:46 -07:00
Geert Weening
68643f3118 [DOC] update release notes 2014-10-27 16:37:14 -07:00
Geert Weening
560dfc8ae6 Merge branch 'release' into develop 2014-10-27 16:27:52 -07:00
Geert Weening
b0459e096b [TASK] bump version to 0.9.0 2014-10-27 16:25:34 -07:00
Geert Weening
2a0dfc4587 [DOC] update release notes 2014-10-27 16:25:09 -07:00
Geert Weening
2dcd5f94fb [TASK] shrinkwrap packages for dependency locking 2014-10-27 16:24:18 -07:00
Geert Weening
13685d03e1 Merge pull request #195 from ripple/use-ledgerselect
Switch account requests to use ledgerSelect rather than ledgerChoose
2014-10-27 16:07:40 -07:00
wltsmrz
278df9025a Switch account requests to use ledgerSelect rather than ledgerChoose 2014-10-27 15:35:07 -07:00
Geert Weening
cb608406f8 Merge pull request #191 from geertweening/fix/undefined_remote
[FIX] transaction without explicit remote
2014-10-27 12:09:59 -07:00
Geert Weening
f4a55d03d3 Merge pull request #192 from geertweening/fix/amount_precision
[FIX] confusion between precision and min_precision
2014-10-26 19:21:09 -04:00
Geert Weening
d3b6b8127c [FIX] transaction without explicit remote
remote was instantiated as an object and checks through the class for `this.remote` would pass and cause 
unintended behavior

e.g. `.complete()` would view an undefined remote as untrusted and not allow local signing
e.g. calling `_computeFee()` with an undefined remote would crash ripple-lib
2014-10-26 19:09:04 -04:00
wltsmrz
bc1f9f8a28 Fix account root request ledger argument #121 2014-10-26 15:39:17 -07:00
wltsmrz
9a5c9aea75 Merge pull request #193 from professorhantzen/patch-1
[FIX] correct usage example with surrounding apostrophes
2014-10-24 18:12:32 -07:00
professorhantzen
f1004c6db2 Correct usage example with surrounding apostrophes 2014-10-25 13:14:07 +13:00
Geert Weening
7708c64576 [FIX] confusion between precision and min_precision
`precision` expresses precision within the provided value and won't expand it if the max expression of the value is smaller
`min_precision` will expand the value if the max expression of the value is smaller
2014-10-23 17:59:53 -07:00
Geert Weening
0527b8c981 [TASK] move gulp-bump to devDependencies 2014-10-22 12:07:36 -07:00
Geert Weening
13f89e2fcc [TASK] bump version to 0.9.0-rc5 2014-10-21 15:05:02 -07:00
Geert Weening
69a0a473a6 [DOC] update release notes 2014-10-21 15:04:52 -07:00
Geert Weening
4ab82d7e01 Merge pull request #188 from geertweening/fix/amount_precision
[FIX] amount precision bugs
2014-10-21 15:01:50 -07:00
Geert Weening
4be209e286 [FIX] amount precision bugs
- leading 0's for fraction part disappearing
- first decimal lower than 5 wouldn't result in dropping the decimal
2014-10-21 13:22:42 -07:00
Geert Weening
8b10325895 [TASK] bump version to 0.9.0-rc4 2014-10-16 14:11:36 -07:00
Geert Weening
70bf600247 [DOC] update release notes 2014-10-16 14:11:14 -07:00
Geert Weening
d42e06d48b [FIX] fraction_part is a string 2014-10-16 14:05:26 -07:00
wltsmrz
9c080b6790 Merge pull request #187 from ripple/amount_precision
[FIX] Amount.to_human() precision slicing instead of rounding
2014-10-16 14:00:29 -07:00
Geert Weening
033257b03b [FIX] Amount.to_human() precision slicing instead of rounding 2014-10-16 13:54:42 -07:00
Geert Weening
39d8bcdfc2 [TASK] bump version to 0.9.0-rc3 2014-10-15 11:32:16 -07:00
Geert Weening
2ddcb4e2b7 [DOC] update release notes 2014-10-15 11:31:52 -07:00
Geert Weening
d972718a53 Merge pull request #185 from shekenahglory/fix/serialize
[FIX] binformat: added missing TransactionResult options
2014-10-14 11:37:35 -07:00
Matthew Fettig
6abed8dd53 [FIX] binformat: added missing TransactionResult options 2014-10-14 11:32:33 -07:00
Geert Weening
e74e697b45 [TASK] bump version to 0.9.0-rc2 2014-10-09 14:46:12 -07:00
Geert Weening
26c59e8565 [DOC] update release notes 2014-10-09 14:45:44 -07:00
Geert Weening
a5e83c4f23 [FIX] run test by _mocha to capture coverage 2014-10-09 14:35:14 -07:00
wltsmrz
900c4bbd2e Merge pull request #184 from geertweening/show_interest_flag
[FEATURE] add flag to show or hide interest in to_human/to_json
2014-10-09 14:31:43 -07:00
Geert Weening
947ec3edc2 [FEATURE] add flag to show or hide interest in to_human/to_json
The show_interest flag will default to true for interest bearing currencies and false for currencies without interest
2014-10-09 14:31:02 -07:00
Geert Weening
957f10d9f1 [TASK] bump version to 0.9.0-rc1 2014-10-09 09:40:31 -07:00
Geert Weening
89aa54dff8 [DOC] update release notes 2014-10-09 09:39:15 -07:00
Geert Weening
bb76530e4b Merge pull request #179 from geertweening/develop
[FEATURE] make maxLoops in seed.get_key() optional
2014-10-09 09:32:17 -07:00
wltsmrz
011e2cc1e3 Merge pull request #182 from shekenahglory/fix/attestation
[FIX] vault client: URI decode attestation token
2014-10-08 18:41:38 -07:00
Matthew Fettig
4c594f8964 [FIX] vault client: URI decode attestation token 2014-10-08 18:17:30 -07:00
Geert Weening
1fcfcf2392 [TASK] bump version to 0.8.3-rc1 2014-10-06 18:23:59 -07:00
Geert Weening
6311abff81 [DOC] update release notes 2014-10-06 18:23:05 -07:00
Geert Weening
ed2da57475 Merge pull request #181 from shekenahglory/feature/identity
Feature/identity
2014-10-06 18:15:38 -07:00
Matthew Fettig
778ccd4805 [TASK] vault client: tests for attestation routes and full summary 2014-10-06 18:03:52 -07:00
Matthew Fettig
327c35252f [FEATURE] vault client: update attestation and attestation summary 2014-10-03 17:17:09 -07:00
Matthew Fettig
5e7af2fba4 [TASK] switch to new attestation endpoint 2014-10-03 17:17:09 -07:00
Matthew Fettig
dce15bc579 [TASK] vault client: add parameters to attest 2014-10-03 17:17:08 -07:00
Matthew Fettig
d5e32db954 [CHORE] vault client: add type to attest parameters 2014-10-03 17:17:08 -07:00
Matthew Fettig
bdfa83592b [FEATURE] identity functions 2014-10-03 17:17:08 -07:00
Geert Weening
23e473b688 [FEATURE] make maxLoops in seed.get_key optional
default to 1 or the index of the requested account +1
2014-10-02 17:26:01 -07:00
wltsmrz
0dfd3a0ae0 Merge pull request #178 from ripplerm/develop
configurable maxAttempts
2014-10-01 02:07:04 -07:00
ripplerm
d107092540 configurable maxAttempts 2014-10-01 14:43:44 +08:00
wltsmrz
c2f379d3b3 Merge pull request #176 from shekenahglory/develop
[FIX] change handling of requestLedger options
2014-09-28 00:34:30 -07:00
Matthew Fettig
57b70300f5 [FIX] change handling of requestLedger options
ledger_index and ledger_hash were being set to true
instead of the specified value, and according to API
docs 'closed','validated', and 'current' can be
supplied to the ledger_index field to get the most
recent of the specified type
2014-09-26 21:41:22 -07:00
Geert Weening
eeba86f9c5 [TASK] bump version to 0.8.2 2014-09-25 10:05:57 -07:00
Geert Weening
e0d68e60ec Merge pull request #172 from booxood/modify-docs-guides-md
Update docs/GUIDES.md
2014-09-24 23:44:08 -07:00
Liucw
254248486b Update docs/GUIDES.md 2014-09-25 14:34:29 +08:00
Geert Weening
1b57cc6d35 [TASK] bump verison to 0.8.2-rc2 2014-09-22 23:59:12 -07:00
Geert Weening
77234f256d [DOC] update release notes 2014-09-22 23:58:49 -07:00
Geert Weening
795d31d2db Merge branch 'release' into develop 2014-09-22 23:50:45 -07:00
wltsmrz
f3f10fd9bd Fix requestLedger arguments 2014-09-18 02:03:29 -07:00
wltsmrz
7100b4be8d Emit server with subscription events 2014-09-18 01:41:23 -07:00
wltsmrz
b1a7200d1b Deprecate account_tx map/reduce/filter 2014-09-18 01:28:43 -07:00
123 changed files with 13278 additions and 8263 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'))
@@ -68,45 +78,6 @@ gulp.task('build', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({version: pkg.version}))
.pipe(gulp.dest('./dist/'));
});
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.src('./package.json')
.pipe(bump({type:argv.type}))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({version: pkg.version+'-beta'}))
.pipe(gulp.dest('./'));
});
gulp.task('build-min', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(uglify())
@@ -128,36 +99,103 @@ gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
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);
/**
* Generate a WebPack external for a given unavailable module which replaces
* that module's constructor with an error-thrower
*/
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 + ')';
function buildUseError(cons) {
return 'var {<CONS>:function(){throw new Error("Class is unavailable in this build: <CONS>")}}'
.replace(new RegExp('<CONS>', 'g'), cons);
};
while (col1.length < 8) {
col1 += ' ';
}
console.log(' ' + [ col1, col2, col3 ].join(' '));
}
});
}
callback(null, file);
}));
gulp.task('build-core', [ 'concat-sjcl' ], function(callback) {
webpack({
entry: [
'./src/js/ripple/remote.js'
],
externals: [
{
'./transaction': buildUseError('Transaction'),
'./orderbook': buildUseError('OrderBook'),
'./account': buildUseError('Account'),
'./serializedobject': buildUseError('SerializedObject')
}
],
output: {
library: 'ripple',
path: './build/',
filename: [ 'ripple-', '-core.js' ].join(pkg.version)
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({ version: pkg.version }))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);
// 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('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
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() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.src('./package.json')
.pipe(bump({ type: argv.type }))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({ version: pkg.version + '-beta' }))
.pipe(gulp.dest('./'));
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);

View File

@@ -1,7 +1,183 @@
##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)
+ [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)
+ **Important** `tef*` and `tel*` and errors will no longer be presented as
final. Rather than considering these errors final, ripple-lib will wait until
the `LastLedgerSequence` specified in the transaction is exceeded. This makes
failures more definitive, and ensures that no transaction will resubmit
indefinitely.
+ A new, final tej-class error is introduced to account for transactions that
are locally determined to have expired: `tejMaxLedger`.
+ [Allow per transaction fees to be set, `transaction.setFixedFee()`](https://github.com/ripple/ripple-lib/commit/9b22f279bcbe60ee6bcf4b7fa60a48e9c197a828)
+ [Improve memo support](https://github.com/ripple/ripple-lib/commit/1704ac4ae144c0ce54afad86f644c75a632080b1)
- Add `MemoFormat` property for memo
- Enforce `MemoFormat` and `MemoType` to be valid ASCII
- Support `text` and `json` MemoFormat
+ [Update jscl library](https://github.com/ripple/ripple-lib/commit/3204998fcb6f31d6c90532a737a4adb8a1e420f6)
- Improved entropy by taking advantage of platform crypto
- Use jscl's k256 curve instead of altering the c256 curve with k256 configuration
- **deprecated:** the c256 curve is linked to the k256 curve to provide backwards compatibility, this link will be removed in the future
+ [Fix empty queue check on reconnect](https://github.com/ripple/ripple-lib/commit/3c21994adcf72d1fbd87d453ceb917f9ad6df4ec)
##0.9.4
+ [Normalize offers from book_offers and transaction stream](https://github.com/ripple/ripple-lib/commit/86ed24b94cf7c8929c87db3a63e9bbea7f767e9c)
+ [Fix: Amount.to_human() precision rounding](https://github.com/ripple/ripple-lib/commit/e371cc2c3ceccb3c1cfdf18b98d80093147dd8b2)
+ [Fix: fractional drops in funded taker_pays setter](https://github.com/ripple/ripple-lib/commit/0d7fc0a573a144caac15dd13798b23eeb1f95fb4)
##0.9.3
+ [Change `presubmit` to emit immediately before transaction submit](https://github.com/ripple/ripple-lib/commit/7a1feaa89701bf861ab31ebd8ffdc8d8d1474e29)
+ [Add a "core" browser build of ripple-lib which has a subset of features and smaller file size](https://github.com/ripple/ripple-lib/pull/205)
+ [Update binformat with missing fields from rippled](https://github.com/ripple/ripple-lib/commit/cae980788efb00191bfd0988ed836d60cdf7a9a2)
+ [Wait for transaction validation before returning `tec` error](https://github.com/ripple/ripple-lib/commit/6bdd4b2670906588852fc4dda457607b4aac08e4)
+ [Change default `max_fee` on `Remote` to `1 XRP`](https://github.com/ripple/ripple-lib/commit/d6b1728c23ff85c3cc791bed6982a750641fd95f)
+ [Fix: Request ledger_accept should return the Remote](https://github.com/ripple/ripple-lib/pull/209)
##0.9.2
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add max_fee setter to transactions to set max fee the submitter is willing to pay] (https://github.com/ripple/ripple-lib/commit/24587fab9c8ad3840d7aa345a7037b48839e09d7)
+ [Fix: cap IOU Amounts to their max and min value] (https://github.com/ripple/ripple-lib/commit/f05941fbc46fdb7c6fe7ad72927af02d527ffeed)
Example on how to use paging with `account_offers`:
```
// A valid `ledger_index` or `ledger_hash` is required to provide a reliable result.
// Results can change between ledger closes, so the provided ledger will be used as base.
var options = {
account: < rippleAccount >,
limit: < Number between 10 and 400 >,
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;
}
var request = remote.requestAccountOffers(options);
```
[Full working example](https://github.com/geertweening/ripple-lib-scripts/blob/master/account_offers_paging.js)
##0.9.1
+ Switch account requests to use ledgerSelect rather than ledgerChoose ([278df90](https://github.com/ripple/ripple-lib/commit/278df9025a20228de22379a53c76ca12d40fa591))
+ **Deprecated** setting `ident` and `account_index` on account requests ([278df90](https://github.com/ripple/ripple-lib/commit/278df9025a20228de22379a53c76ca12d40fa591))
+ Change initial account transaction sequence to 1 ([a3c1d06](https://github.com/ripple/ripple-lib/commit/a3c1d06eba883dc84fe2bfe700e4309795c84cac))
+ Fix: instance transaction withoute remote ([d3b6b81](https://github.com/ripple/ripple-lib/commit/d3b6b8127c7b01e416b400c25abf1719bdd008ca))
+ Fix: account root request ledger argument ([bc1f9f8](https://github.com/ripple/ripple-lib/commit/bc1f9f8a286b187d36ebaf552694e31e73742293))
+ Fix: rsign.js local signing and example ([d3b6b81](https://github.com/ripple/ripple-lib/commit/d3b6b8127c7b01e416b400c25abf1719bdd008ca) and [f1004c6](https://github.com/ripple/ripple-lib/commit/f1004c6db2a0ce59bbabbb8f2b355a9fd9995fd8))
##0.9.0
+ Add routes to the vault client for KYC attestations ([ed2da574](https://github.com/ripple/ripple-lib/commit/ed2da57475acf5e9d2cf3373858f4274832bd83f))
+ Currency: add `show_interest` flag to show or hide interest in `Currency.to_human()` and `Currency.to_json()` [Example use in tests](https://github.com/ripple/ripple-lib/blob/947ec3edc2e7c8f1ef097e496bf552c74366e749/test/currency-test.js#L123)
+ Configurable maxAttempts for transaction submission ([d107092](https://github.com/ripple/ripple-lib/commit/d10709254061e9e4416d2cb78b5cac1ec0d7ffa5))
+ Binformat: added missing TransactionResult options ([6abed8d](https://github.com/ripple/ripple-lib/commit/6abed8dd5311765b2eb70505dadbdf5121439ca8))
+ **Breaking change:** make maxLoops in seed.get_key optional. [Example use in tests](https://github.com/ripple/ripple-lib/blob/23e473b6886c457781949c825b3ff48b3984e51f/test/seed-test.js) ([23e473b](https://github.com/ripple/ripple-lib/commit/23e473b6886c457781949c825b3ff48b3984e51f))
+ Shrinkwrap packages for dependency locking ([2dcd5f9](2dcd5f94fbc71200eb08a5044c76ef94f7971913))
+ Fix: Amount.to_human() precision bugs ([4be209e](https://github.com/ripple/ripple-lib/commit/4be209e286b5b209bec7bcd1212098985e15ff2f) and [7708c64](https://github.com/ripple/ripple-lib/commit/7708c64576e70ce3ac190442daceb30e4446aab7))
+ Fix: change handling of requestLedger options ([57b7030](https://github.com/ripple/ripple-lib/commit/57b70300f5f0c7534ede118ddbb5d8762668a4f8))
##0.8.2
+ Currency: Allow mixed letters and numbers in currencies
+ Deprecate account_tx map/reduce/filterg
+ Fix: correct requestLedger arguments
+ Fix: missing subscription on error events for some server methods
+ Fix: orderbook reset on reconnect
+ Fix: ripple-lib crashing. Add potential missing error handlers

View File

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

View File

@@ -56,7 +56,7 @@ function ready() {
function print_usage() {
console.log(
'Usage: rsign.js <secret> <json>\n\n',
'Example: rsign.js ssq55ueDob4yV3kPVnNQLHB6icwpC',
'Example: rsign.js ssq55ueDob4yV3kPVnNQLHB6icwpC','\''+
JSON.stringify({
TransactionType: 'Payment',
Account: 'r3P9vH81KBayazSTrQj6S25jW6kDb779Gi',
@@ -64,7 +64,7 @@ function print_usage() {
Amount: '200000000',
Fee: '10',
Sequence: 1
})
})+'\''
);
};

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

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

View File

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

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

185
npm-shrinkwrap.json generated Normal file
View File

@@ -0,0 +1,185 @@
{
"name": "ripple-lib",
"version": "0.12.0-rc1",
"dependencies": {
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
},
"bignumber.js": {
"version": "2.0.0",
"from": "bignumber.js@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.0.tgz"
},
"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",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
},
"ripple-wallet-generator": {
"version": "1.0.1",
"from": "ripple-wallet-generator@1.0.1",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.1.tgz"
},
"superagent": {
"version": "0.18.2",
"from": "superagent@>=0.18.0 <0.19.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-0.18.2.tgz",
"dependencies": {
"qs": {
"version": "0.6.6",
"from": "qs@0.6.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz"
},
"formidable": {
"version": "1.0.14",
"from": "formidable@1.0.14",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz"
},
"mime": {
"version": "1.2.11",
"from": "mime@1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"component-emitter": {
"version": "1.1.2",
"from": "component-emitter@1.1.2",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz"
},
"methods": {
"version": "1.0.1",
"from": "methods@1.0.1",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz"
},
"cookiejar": {
"version": "2.0.1",
"from": "cookiejar@2.0.1",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz"
},
"debug": {
"version": "1.0.4",
"from": "debug@>=1.0.1 <1.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
}
}
},
"reduce-component": {
"version": "1.0.1",
"from": "reduce-component@1.0.1",
"resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz"
},
"form-data": {
"version": "0.1.3",
"from": "form-data@0.1.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.7",
"from": "combined-stream@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
"from": "delayed-stream@0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
}
}
},
"readable-stream": {
"version": "1.0.27-1",
"from": "readable-stream@1.0.27-1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
}
},
"ws": {
"version": "0.7.1",
"from": "ws@>=0.7.1 <0.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.1.tgz",
"dependencies": {
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.1",
"from": "ultron@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.1.tgz"
},
"bufferutil": {
"version": "1.0.1",
"from": "bufferutil@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.0.1.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0"
},
"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,7 +1,7 @@
{
"name": "ripple-lib",
"version": "0.8.2-rc1",
"description": "Ripple JavaScript client library",
"version": "0.12.0-rc1",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
"bin/*",
@@ -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",
"gulp-bump": "~0.1.10",
"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",
"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();
}
}
};
@@ -132,7 +132,7 @@ Account.prototype.isValid = function() {
*/
Account.prototype.getInfo = function(callback) {
return this._remote.request_account_info(this._account_id, callback);
return this._remote.requestAccountInfo({account: this._account_id}, callback);
};
/**
@@ -174,8 +174,8 @@ Account.prototype.getNextSequence = function(callback) {
function accountInfo(err, info) {
if (isNotFound(err)) {
// New accounts will start out as sequence zero
callback(null, 0);
// New accounts will start out as sequence one
callback(null, 1);
} else if (err) {
callback(err);
} else {
@@ -211,7 +211,7 @@ Account.prototype.lines = function(callback) {
}
}
this._remote.requestAccountLines(this._account_id, accountLines);
this._remote.requestAccountLines({account: this._account_id}, accountLines);
return this;
};

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;

View File

@@ -2,6 +2,9 @@
* Data type map.
*
* Mapping of type ids to data types. The type id is specified by the high
*
* For reference, see rippled's definition:
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol/SField.cpp
*/
var TYPES_MAP = exports.types = [
void(0),
@@ -106,7 +109,8 @@ var FIELDS_MAP = exports.fields = {
16: 'BookDirectory',
17: 'InvoiceID',
18: 'Nickname',
19: 'Feature'
19: 'Amendment',
20: 'TicketID'
},
6: { // Amount
1: 'Amount',
@@ -135,7 +139,8 @@ var FIELDS_MAP = exports.fields = {
10: 'ExpireCode',
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData'
13: 'MemoData',
14: 'MemoFormat'
},
8: { // Account
1: 'Account',
@@ -187,7 +192,7 @@ var FIELDS_MAP = exports.fields = {
19: { // Vector256
1: 'Indexes',
2: 'Hashes',
3: 'Features'
3: 'Amendments'
}
};
@@ -373,7 +378,7 @@ exports.ledger = {
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]])
}
};
exports.metadata = [
[ 'TransactionIndex' , REQUIRED ],
@@ -382,23 +387,32 @@ exports.metadata = [
];
exports.ter = {
tesSUCCESS: 0,
tecCLAIM: 100,
tecPATH_PARTIAL: 101,
tecUNFUNDED_ADD: 102,
tecUNFUNDED_OFFER: 103,
tecUNFUNDED_PAYMENT: 104,
tecFAILED_PROCESSING: 105,
tecDIR_FULL: 121,
tecINSUF_RESERVE_LINE: 122,
tecINSUF_RESERVE_OFFER: 123,
tecNO_DST: 124,
tecNO_DST_INSUF_XRP: 125,
tecNO_LINE_INSUF_RESERVE: 126,
tecNO_LINE_REDUNDANT: 127,
tecPATH_DRY: 128,
tecUNFUNDED: 129,
tecMASTER_DISABLED: 130,
tecNO_REGULAR_KEY: 131,
tecOWNERS: 132
tesSUCCESS : 0,
tecCLAIM : 100,
tecPATH_PARTIAL : 101,
tecUNFUNDED_ADD : 102,
tecUNFUNDED_OFFER : 103,
tecUNFUNDED_PAYMENT : 104,
tecFAILED_PROCESSING : 105,
tecDIR_FULL : 121,
tecINSUF_RESERVE_LINE : 122,
tecINSUF_RESERVE_OFFER : 123,
tecNO_DST : 124,
tecNO_DST_INSUF_XRP : 125,
tecNO_LINE_INSUF_RESERVE : 126,
tecNO_LINE_REDUNDANT : 127,
tecPATH_DRY : 128,
tecUNFUNDED : 129, // Deprecated, old ambiguous unfunded.
tecMASTER_DISABLED : 130,
tecNO_REGULAR_KEY : 131,
tecOWNERS : 132,
tecNO_ISSUER : 133,
tecNO_AUTH : 134,
tecNO_LINE : 135,
tecINSUFF_FEE : 136,
tecFROZEN : 137,
tecNO_TARGET : 138,
tecNO_PERMISSION : 139,
tecNO_ENTRY : 140,
tecINSUFFICIENT_RESERVE : 141
};

File diff suppressed because it is too large Load Diff

View File

@@ -322,4 +322,12 @@ Crypt.base64UrlToBase64 = function(encodedData) {
return encodedData;
};
/**
* base64 to UTF8
*/
Crypt.decodeBase64 = function (data) {
return sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(data));
}
exports.Crypt = Crypt;

View File

@@ -53,11 +53,7 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
if (j instanceof this) {
return j.clone();
} else {
return (new this()).parse_json(j, shouldInterpretXrpAsIou);
}
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
};
Currency.from_human = function(j, opts) {
@@ -71,12 +67,9 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
switch (typeof j) {
case 'string':
if (!j || /^(0|XRP)$/.test(j)) {
if (shouldInterpretXrpAsIou) {
this.parse_hex(Currency.HEX_CURRENCY_BAD);
} else {
this.parse_hex(Currency.HEX_ZERO);
}
// if an empty string is given, fall back to XRP
if (!j || j === '0') {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
break;
}
@@ -86,6 +79,17 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
if (matches) {
var currencyCode = matches[1];
// for the currency 'XRP' case
// we drop everything else that could have been provided
// e.g. 'XRP - Ripple'
if (!currencyCode || /^(0|XRP)$/.test(currencyCode)) {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
// early break, we can't have interest on XRP
break;
}
// the full currency is matched as it is part of the valid currency format, but not stored
// var full_currency = matches[2] || '';
var interest = matches[3] || '';
@@ -270,7 +274,7 @@ Currency.prototype.has_interest = function() {
* @returns {number} - interest for provided interval, can be negative for demurred currencies
*/
Currency.prototype.get_interest_at = function(referenceDate, decimals) {
if (!this.has_interest) {
if (!this.has_interest()) {
return 0;
}
@@ -304,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) {
@@ -316,17 +320,17 @@ Currency.prototype.to_json = function(opts) {
var opts = opts || {};
var currency;
var fullName = opts && opts.full_name ? " - " + opts.full_name : "";
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
opts.show_interest = opts.show_interest !== void(0) ? opts.show_interest : this.has_interest();
// Any currency with standard properties and a valid code can be abbreviated
// in the JSON wire format as the three character code.
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code) && !this.has_interest()) {
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
currency = this._iso_code + fullName;
if (opts.show_interest) {
var decimals = !isNaN(opts.decimals) ? opts.decimals : void(0);
var interestPercentage = this.has_interest() ? this.get_interest_percentage_at(this._interest_start + 3600 * 24 * 365, decimals) : 0;
currency += ' (' + interestPercentage + '%pa)';
}
// If there is interest, append the annual interest to the full currency name
} else if (!opts.force_hex && this.has_interest()) {
var decimals = opts ? opts.decimals : undefined;
currency = this._iso_code + fullName + " (" + this.get_interest_percentage_at(this._interest_start + 3600 * 24 * 365, decimals) + "%pa)";
} else {
// Fallback to returning the raw currency hex

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

@@ -5,7 +5,7 @@ var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
function KeyPair() {
this._curve = sjcl.ecc.curves.c256;
this._curve = sjcl.ecc.curves.k256;
this._secret = null;
this._pubkey = null;
};
@@ -15,7 +15,7 @@ KeyPair.from_bn_secret = function(j) {
};
KeyPair.prototype.parse_bn_secret = function(j) {
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.c256, j);
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
return this;
};

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

View File

@@ -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;
};
@@ -208,6 +338,13 @@ Request.prototype.ledgerIndex = function(ledger_index) {
return this;
};
/**
* Set either ledger_index or ledger_hash based on heuristic
*
* @param {Number|String} ledger identifier
*/
Request.prototype.selectLedger =
Request.prototype.ledgerSelect = function(ledger) {
switch (ledger) {
case 'current':
@@ -215,12 +352,11 @@ Request.prototype.ledgerSelect = function(ledger) {
case 'validated':
this.message.ledger_index = ledger;
break;
default:
if (isNaN(ledger)) {
this.message.ledger_hash = ledger;
} else if ((ledger = Number(ledger))) {
this.message.ledger_index = ledger;
if (Number(ledger) && isFinite(Number(ledger))) {
this.message.ledger_index = Number(ledger);
} else if (/^[A-F0-9]{64}$/.test(ledger)) {
this.message.ledger_hash = ledger;
}
break;
}
@@ -229,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;
};
@@ -243,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;
};
@@ -279,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)
]
@@ -315,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,8 +13,7 @@ var UInt160 = require('./uint160').UInt160;
var KeyPair = require('./keypair').KeyPair;
var Seed = extend(function () {
// Internal form: NaN or BigInteger
this._curve = sjcl.ecc.curves.c256;
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;
}
@@ -93,9 +90,14 @@ function SHA256_RIPEMD160(bits) {
*
* {Uint160} (from_json able), specifies the address matching the KeyPair
* that is desired.
*
* @param maxLoops (optional)
* {Number} specifies the amount of attempts taken to generate
* a matching KeyPair
*/
Seed.prototype.get_key = function (account) {
Seed.prototype.get_key = function (account, maxLoops) {
var account_number = 0, address;
var max_loops = maxLoops || 1;
if (!this.is_valid()) {
throw new Error('Cannot generate keys from invalid seed!');
@@ -103,6 +105,7 @@ Seed.prototype.get_key = function (account) {
if (account) {
if (typeof account === 'number') {
account_number = account;
max_loops = account_number+1;
} else {
address = UInt160.from_json(account);
}
@@ -121,9 +124,9 @@ Seed.prototype.get_key = function (account) {
var sec;
var key_pair;
var max_loops = 1000; // TODO
do {
i = 0;
do {
@@ -135,15 +138,15 @@ Seed.prototype.get_key = function (account) {
sec = sec.add(private_gen).mod(curve.r);
key_pair = KeyPair.from_bn_secret(sec);
if (--max_loops <= 0) {
if (max_loops-- <= 0) {
// We are almost certainly looking for an account that would take same
// value of $too_long {forever, ...}
throw new Error('Too many loops looking for KeyPair yielding '+
address.to_json() +' from ' + this.to_json());
};
} while (address && !key_pair.get_address().equals(address));
}
return key_pair;
} while (address && !key_pair.get_address().equals(address));
return key_pair;
};
exports.Seed = Seed;

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({}, 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,51 +22,49 @@ var amount = require('./amount');
var Amount = amount.Amount;
var Currency = amount.Currency;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
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 serialize_hex(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 convert_bytes_to_hex (byte_array) {
function convertByteArrayToHex (byte_array) {
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array)).toUpperCase();
};
}
function convertStringToHex(string) {
var utf8String = sjcl.codec.utf8String.toBits(string);
return sjcl.codec.hex.fromBits(utf8String).toUpperCase();
}
function convertHexToString(hexString) {
return sjcl.codec.utf8String.fromBits(sjcl.codec.hex.toBits(hexString));
}
SerializedType.serialize_varint = function (so, val) {
if (val < 0) {
@@ -115,13 +114,13 @@ SerializedType.prototype.parse_varint = function (so) {
*
* The result is appended to the serialized object ('so').
*/
function append_byte_array(so, val, bytes) {
function convertIntegerToByteArray(val, bytes) {
if (!isNumber(val)) {
throw new Error('Value is not a number', 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 = [ ];
@@ -130,8 +129,8 @@ function append_byte_array(so, val, bytes) {
newBytes.unshift(val >>> (i * 8) & 0xff);
}
so.append(newBytes);
};
return newBytes;
}
// Convert a certain number of bytes from the serialized object ('so') into an integer.
function readAndSum(so, bytes) {
@@ -148,11 +147,11 @@ function readAndSum(so, bytes) {
// Convert to unsigned integer
return sum >>> 0;
};
}
var STInt8 = exports.Int8 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 1);
so.append(convertIntegerToByteArray(val, 1));
},
parse: function (so) {
return readAndSum(so, 1);
@@ -163,7 +162,7 @@ STInt8.id = 16;
var STInt16 = exports.Int16 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 2);
so.append(convertIntegerToByteArray(val, 2));
},
parse: function (so) {
return readAndSum(so, 2);
@@ -174,7 +173,7 @@ STInt16.id = 1;
var STInt32 = exports.Int32 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 4);
so.append(convertIntegerToByteArray(val, 4));
},
parse: function (so) {
return readAndSum(so, 4);
@@ -192,40 +191,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;
}
serialize_hex(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));
}
});
@@ -237,7 +221,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash128');
}
serialize_hex(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));
@@ -252,7 +236,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash256');
}
serialize_hex(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));
@@ -267,7 +251,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash160');
}
serialize_hex(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));
@@ -278,7 +262,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) {
@@ -294,7 +278,7 @@ var STCurrency = new SerializedType({
// UInt160 value and consider it valid. But it doesn't, so for the
// deserialization to be usable, we need to allow invalid results for now.
//if (!currency.is_valid()) {
// throw new Error('Invalid currency: '+convert_bytes_to_hex(bytes));
// throw new Error('Invalid currency: '+convertByteArrayToHex(bytes));
//}
return currency;
}
@@ -307,11 +291,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) {
@@ -322,7 +313,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.
@@ -344,10 +335,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]);
@@ -365,7 +362,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);
@@ -373,6 +369,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);
@@ -382,26 +380,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;
}
});
@@ -409,15 +404,16 @@ STAmount.id = 6;
var STVL = exports.VariableLength = exports.VL = new SerializedType({
serialize: function (so, val) {
if (typeof val === 'string') {
serialize_hex(so, val);
serializeHex(so, val);
} else {
throw new Error('Unknown datatype.');
}
},
parse: function (so) {
var len = this.parse_varint(so);
return convert_bytes_to_hex(so.read(len));
return convertByteArrayToHex(so.read(len));
}
});
@@ -429,7 +425,7 @@ var STAccount = exports.Account = new SerializedType({
if (!account.is_valid()) {
throw new Error('Invalid account!');
}
serialize_hex(so, account.to_hex());
serializeBits(so, account.to_bits());
},
parse: function (so) {
var len = this.parse_varint(so);
@@ -441,7 +437,6 @@ var STAccount = exports.Account = new SerializedType({
var result = UInt160.from_bytes(so.read(len));
result.set_version(Base.VER_ACCOUNT_ID);
//console.log('PARSED 160:', result.to_json());
if (false && !result.is_valid()) {
throw new Error('Invalid Account');
}
@@ -483,7 +478,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) {
@@ -492,7 +487,7 @@ var STPathSet = exports.PathSet = new SerializedType({
}
if (entry.issuer) {
so.append(UInt160.from_json(entry.issuer).to_bytes());
STHash160.serialize(so, entry.issuer);
}
}
}
@@ -532,6 +527,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');
@@ -539,6 +535,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');
@@ -546,6 +543,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');
@@ -553,9 +551,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.
@@ -575,7 +578,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]);
}
@@ -593,6 +596,109 @@ var STVector256 = exports.Vector256 = new SerializedType({
STVector256.id = 19;
// Internal
exports.STMemo = new SerializedType({
serialize: function(so, val, no_marker) {
var keys = [];
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// 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);
}
if (!no_marker) {
//Object ending marker
STInt8.serialize(so, 0xe1);
}
},
parse: function(so) {
var output = {};
while (so.peek(1)[0] !== 0xe1) {
var keyval = parse(so);
output[keyval[0]] = keyval[1];
}
if (output.MemoType !== void(0)) {
var parsedType = convertHexToString(output.MemoType);
if (parsedType !== 'unformatted_memo') {
output.parsed_memo_type = 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
}
} else if(output.parsed_memo_format === 'text') {
output.parsed_memo_data = convertHexToString(output.MemoData);
}
}
so.read(1);
return output;
}
});
exports.serialize = exports.serialize_whatever = serialize;
function serialize(so, field_name, value) {
@@ -622,9 +728,15 @@ function serialize(so, field_name, value) {
STInt8.serialize(so, field_bits);
}
// Get the serializer class (ST...) for a field based on the type bits.
var serialized_object_type = exports[binformat.types[type_bits]];
//do something with val[keys] and val[keys[i]];
// Get the serializer class (ST...)
var serialized_object_type;
if (field_name === 'Memo' && typeof value === 'object') {
// for Memo we override the default behavior with our STMemo serializer
serialized_object_type = exports.STMemo;
} else {
// for a field based on the type bits.
serialized_object_type = exports[binformat.types[type_bits]];
}
try {
serialized_object_type.serialize(so, value);
@@ -645,20 +757,23 @@ function parse(so) {
type_bits = so.read(1)[0];
}
// Get the parser class (ST...) for a field based on the type bits.
var type = exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
var field_bits = tag_byte & 0x0f;
var field_name = (field_bits === 0)
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
assert(field_name, 'Unknown field - header byte is 0x' + tag_byte.toString(16));
// Get the parser class (ST...) for a field based on the type bits.
var type = (field_name === 'Memo')
? exports.STMemo
: exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
return [ field_name, type.parse(so) ]; //key, value
};
}
function sort_fields(keys) {
function sort_field_compare(a, b) {
@@ -671,25 +786,27 @@ 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);
}
var STObject = exports.Object = new SerializedType({
serialize: function (so, val, no_marker) {
var keys = Object.keys(val);
var keys = [];
// Ignore lowercase field names - they're non-serializable fields by
// convention.
keys = keys.filter(function (key) {
return key[0] !== key[0].toLowerCase();
});
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
keys.forEach(function (key) {
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// Sort fields

View File

@@ -1,8 +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 Transaction = require('./transaction').Transaction;
var RangeSet = require('./rangeset').RangeSet;
var log = require('./log').internal.sub('server');
/**
@@ -33,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');
}
@@ -57,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;
@@ -118,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.
@@ -236,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.
*
@@ -329,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) {
@@ -409,7 +437,7 @@ Server.prototype.connect = function() {
if (ws === self._ws) {
self.emit('socket_open');
// Subscribe to events
self._request(self._remote._serverPrepareSubscribe());
self._request(self._remote._serverPrepareSubscribe(self));
}
};
@@ -421,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.
@@ -546,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);
};
@@ -564,7 +599,7 @@ Server.prototype._handleServerStatus = function(message) {
this._remote.emit('load', message, this);
var loadChanged = message.load_base !== this._load_base
|| message.load_factor !== this._load_factor
|| message.load_factor !== this._load_factor;
if (loadChanged) {
this._load_base = message.load_base;
@@ -621,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;
@@ -647,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');
}
@@ -751,6 +801,12 @@ Server.prototype._request = function(request) {
}
};
/**
* Get server connected status
*
* @return boolean
*/
Server.prototype.isConnected =
Server.prototype._isConnected = function() {
return this._connected;
@@ -760,22 +816,16 @@ Server.prototype._isConnected = function() {
* Calculate transaction fee
*
* @param {Transaction|Number} Fee units for a provided transaction
* @return {Number} Final fee in XRP for specified number of fee units
* @return {String} Final fee in XRP for specified number of fee units
* @api private
*/
Server.prototype._computeFee = function(transaction) {
var units;
if (transaction instanceof Transaction) {
units = transaction._getFeeUnits();
} else if (typeof transaction === 'number') {
units = transaction;
} else {
Server.prototype._computeFee = function(feeUnits) {
if (isNaN(feeUnits)) {
throw new Error('Invalid argument');
}
return this._feeTx(units).to_json();
return this._feeTx(Number(feeUnits)).to_json();
};
/**
@@ -831,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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,20 @@
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
/**
* Manager for pending transactions
*/
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
function TransactionQueue() {
this._queue = [ ];
this._idCache = LRU();
this._sequenceCache = LRU();
this._idCache = LRU({ max: 200 });
this._sequenceCache = LRU({ max: 200 });
};
/**
* Store received (validated) sequence
*
* @param {Number} sequence
*/
TransactionQueue.prototype.addReceivedSequence = function(sequence) {
@@ -23,6 +24,9 @@ TransactionQueue.prototype.addReceivedSequence = function(sequence) {
/**
* Check that sequence number has been consumed by a validated
* transaction
*
* @param {Number} sequence
* @return {Boolean}
*/
TransactionQueue.prototype.hasSequence = function(sequence) {
@@ -31,6 +35,9 @@ TransactionQueue.prototype.hasSequence = function(sequence) {
/**
* Store received (validated) ID transaction
*
* @param {String} transaction id
* @param {Transaction} transaction
*/
TransactionQueue.prototype.addReceivedId = function(id, transaction) {
@@ -39,6 +46,9 @@ TransactionQueue.prototype.addReceivedId = function(id, transaction) {
/**
* Get received (validated) transaction by ID
*
* @param {String} transaction id
* @return {Object}
*/
TransactionQueue.prototype.getReceived = function(id) {
@@ -48,6 +58,9 @@ TransactionQueue.prototype.getReceived = function(id) {
/**
* Get a submitted transaction by ID. Transactions
* may have multiple associated IDs.
*
* @param {String} transaction id
* @return {Transaction}
*/
TransactionQueue.prototype.getSubmission = function(id) {
@@ -63,8 +76,32 @@ TransactionQueue.prototype.getSubmission = function(id) {
return result;
};
/**
* Get earliest ledger in the pending queue
*
* @return {Number} ledger
*/
TransactionQueue.prototype.getMinLedger = function() {
var result = Infinity;
for (var i=0, tx; (tx=this._queue[i]); i++) {
if (tx.initialSubmitIndex < result) {
result = tx.initialSubmitIndex;
}
}
if (!isFinite(result)) {
result = -1;
}
return result;
};
/**
* Remove a transaction from the queue
*
* @param {String|Transaction} transaction or id
*/
TransactionQueue.prototype.remove = function(tx) {
@@ -87,14 +124,30 @@ TransactionQueue.prototype.remove = function(tx) {
}
};
/**
* Add a transaction to pending queue
*
* @param {Transaction} transaction
*/
TransactionQueue.prototype.push = function(tx) {
this._queue.push(tx);
};
/**
* Iterate over pending transactions
*
* @param {Function} iterator
*/
TransactionQueue.prototype.forEach = function(fn) {
this._queue.forEach(fn);
};
/**
* @return {Number} length of pending queue
*/
TransactionQueue.prototype.length =
TransactionQueue.prototype.getLength = function() {
return this._queue.length;

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,589 +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.updateProfile = blobClient.updateProfile;
VaultClient.prototype.recoverBlob = blobClient.recoverBlob;
VaultClient.prototype.deleteBlob = blobClient.deleteBlob;
VaultClient.prototype.requestToken = blobClient.requestToken;
VaultClient.prototype.verifyToken = blobClient.verifyToken;
//export by name
exports.VaultClient = VaultClient;

View File

@@ -65,7 +65,7 @@ sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(hash,
*
* @param {bitArray} hash
* @param {bitArray} signature
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['c256']] curve
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['k256']] curve
* @returns {sjcl.ecc.ecdsa.publicKey} Public key
*/
sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve) {
@@ -75,7 +75,7 @@ sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve)
}
if (!curve) {
curve = sjcl.ecc.curves['c256'];
curve = sjcl.ecc.curves['k256'];
}
// Convert hash to bits and determine encoding for output

View File

@@ -1,17 +1,16 @@
// ----- for secp256k1 ------
// Overwrite NIST-P256 with secp256k1
sjcl.ecc.curves.c256 = new sjcl.ecc.curve(
sjcl.bn.pseudoMersennePrime(256, [[0,-1],[4,-1],[6,-1],[7,-1],[8,-1],[9,-1],[32,-1]]),
"0x14551231950b75fc4402da1722fc9baee",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
);
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// Replace point addition and doubling algorithms
// NIST-P256 is a=-3, we need algorithms for a=0
//
// This is a custom point addition formula that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.add = function(T) {
var S = this;
if (S.curve !== T.curve) {
@@ -43,7 +42,7 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
var j = h.mul(i);
var r = s2.sub(S.y).doubleM();
var v = S.x.mul(i);
var x = r.square().subM(j).subM(v.copy().doubleM());
var y = r.mul(v.sub(x)).subM(S.y.mul(j).doubleM());
var z = S.z.add(h).square().subM(z1z1).subM(hh);
@@ -51,6 +50,9 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
return new sjcl.ecc.pointJac(this.curve,x,y,z);
};
// This is a custom doubling algorithm that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.doubl = function () {
if (this.isIdentity) { return this; }
@@ -66,7 +68,9 @@ sjcl.ecc.pointJac.prototype.doubl = function () {
return new sjcl.ecc.pointJac(this.curve, x, y, z);
};
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// DEPRECATED:
// previously the c256 curve was overridden with the secp256k1 curve
// since then, sjcl has been updated to support k256
// this override exist to keep supporting the old c256 with k256 behavior
// this will be removed in future release
sjcl.ecc.curves.c256 = sjcl.ecc.curves.k256;

2
src/js/sjcl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
core.js
core_closure.js

9
src/js/sjcl/.travis.yml Normal file
View File

@@ -0,0 +1,9 @@
before_script: ./configure --with-all
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
# 0.6 appears to be unreliable on Travis at the moment.
# - "0.6"

View File

@@ -54,10 +54,11 @@ lint: core.js core/*.js test/*.js browserTest/*.js lint/coding_guidelines.pl
lint/coding_guidelines.pl core/*.js test/*.js browserTest/*.js
TEST_COMMON= browserTest/rhinoUtil.js test/test.js
TEST_COMMON= browserTest/nodeUtil.js test/test.js
TEST_SCRIPTS= $(TEST_COMMON) \
test/aes_vectors.js test/aes_test.js \
test/bitArray_vectors.js test/bitArray_test.js \
test/ocb2_vectors.js test/ocb2_test.js \
test/ccm_vectors.js test/ccm_test.js \
test/cbc_vectors.js test/cbc_test.js \
@@ -70,18 +71,14 @@ TEST_SCRIPTS= $(TEST_COMMON) \
test/hmac_vectors.js test/hmac_test.js \
test/pbkdf2_test.js \
test/bn_vectors.js test/bn_test.js \
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js \
test/srp_vectors.js test/srp_test.js \
test/json_test.js
TEST_SCRIPTS_OPT= $(TEST_COMMON) \
test/srp_vectors.js test/srp_test.js
# Run all tests in node.js.
# Rhino fails at -O 0. Probably because the big files full of test vectors blow the
# bytecode limit. So, run most tests with -O -1. But modular exponentiation is
# currently very slow (on Rhino), so run the SRP test with optimizations on.
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_rhino.js
@rhino -O -1 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS)
@rhino -O 9 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS_OPT)
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_node.js
node test/run_tests_node.js $< $(TEST_SCRIPTS)
tidy:
find . -name '*~' -delete

21
src/js/sjcl/README.md Normal file
View File

@@ -0,0 +1,21 @@
sjcl
====
[![Build Status](https://travis-ci.org/bitwiseshiftleft/sjcl.png)](https://travis-ci.org/bitwiseshiftleft/sjcl)
Stanford Javascript Crypto Library
Security Advisories
===
* 12.02.2014: the current development version has a paranoia bug in the ecc module. The bug was introduced in commit [ac0b3fe0](https://github.com/bitwiseshiftleft/sjcl/commit/ac0b3fe0) and might affect ecc key generation on platforms without a platform random number generator.
*
Security Contact
====
Security Mail: sjcl@ovt.me
OpenPGP-Key Fingerprint: 0D54 3E52 87B4 EC06 3FA9 0115 72ED A6C7 7AAF 48ED
Keyserver: pool.sks-keyservers.net
Documentation
====
The documentation is available [here](http://bitwiseshiftleft.github.io/sjcl/doc/)

12
src/js/sjcl/bower.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "sjcl",
"version": "1.0.0",
"main": ["./sjcl.js"],
"ignore": [
"**/*",
"!README.md",
"!README/*",
"!bower.json",
"!sjcl.js"
]
}

View File

@@ -1,6 +1,6 @@
browserUtil = {};
browserUtil.isRhino = (typeof(window) === 'undefined');
browserUtil.isNodeJS = (typeof(window) === 'undefined');
/**
* Pause (for the graphics to update and the script timer to clear), then run the

View File

@@ -0,0 +1,88 @@
<html>
<head>
<title>Entropy Generator Progress</title>
<!-- ProgressBar source: http://stackoverflow.com/questions/7190898/progress-bar-with-html-and-css -->
<style>
#progressbar {
background-color: black;
border-radius: 13px; /* (height of inner div) / 2 + padding */
padding: 3px;
}
#progressbar > div {
background-color: orange;
width: 0%; /* Adjust with JavaScript */
height: 20px;
border-radius: 10px;
}
</style>
<script type="text/javascript" src="../sjcl.js">
</script>
<script type="text/javascript">
var busy = 0;
var collecting = 0;
function showprogress () {
var barwidth = document.getElementById ("progresswidth");
var paranoia = parseInt (document.getElementById ("paranoialevel").value);
var progress = 100 * sjcl.random.getProgress (paranoia);
barwidth.style.width = progress+"%";
if (!sjcl.random.isReady (paranoia)) {
setTimeout ("showprogress()", 10, "JavaScript");
} else {
busy = 0;
document.getElementById ("startbutton").style.disabled = 1;
}
}
function startup () {
if (collecting == 0) {
sjcl.random.startCollectors ();
collecting = 1;
}
if (busy == 0) {
busy = 1;
document.getElementById ("startbutton").style.disabled = 1;
showprogress ();
}
}
function consume (numbits) {
var collector = document.getElementById ("collector");
collector.value = "retrieving random data";
var paranoia = document.getElementById ("paranoialevel").value;
var numwords = Math.ceil (numbits / 32);
var bits = sjcl.random.randomWords (numwords, paranoia);
collector.value = '';
for (var i=0; i<numwords; i++) {
var hi = (bits [i] >> 16) & 0x0000ffff;
var lo = bits [i] & 0x0000ffff;
collector.value = collector.value + hi.toString (16) + lo.toString (16);
}
startup ();
}
</script>
</head>
<body>
<h1>Entropy Generator Progress</h1>
<p>Target: 192 bits, available at paranoia level 5.</p>
<p>Corresponding paranoia level from [0,1..10]: <input type="text" value="5" id="paranoialevel"/> <input type=button onclick="startup ()" id="startbutton" value=" Start &gt;&gt; "> (the idea being that you can see the progress bar advance gently from empty/black to full/yellow after you press this)</p>
<p><input type=button onclick="consume (192)" value=" Consume 192 bits &gt;&gt; "><input type=text id=collector size=50 value="" onkeypress="consume (192)"> (also consumes 192 bits with every keypress in the text field; use key repeat to consume swiftly)</p>
<div id="progressbar">
<div id="progresswidth"></div>
</div>
<p>Please move your mouse, play around and generally introduce entropy into your environment.</p>
</body>
</html>

View File

@@ -0,0 +1,44 @@
browserUtil = {
isNodeJS: true,
pauseAndThen: function (cb) { cb(); },
cpsIterate: function (f, start, end, pause, callback) {
function go() {
var called = false;
if (start >= end) {
callback && callback();
} else {
f(start, function () {
if (!called) { called = true; start++; go(); }
});
}
}
go (start);
},
cpsMap: function (map, list, pause, callback) {
browserUtil.cpsIterate(function (i, cb) { map(list[i], i, list.length, cb); },
0, list.length, pause, callback);
},
loadScripts: function(scriptNames, callback) {
for (i=0; i<scriptNames.length; i++) {
load(scriptNames[i]);
callback && callback();
}
},
write: function(type, message) {
console.log(message);
return { update: function (type2, message2) {
if (type2 === 'pass') { console.log(" + " + message2); }
else if (type2 === 'unimplemented') { console.log(" ? " + message2); }
else { console.log(" - " + message2); }
}};
},
writeNewline: function () { console.log(""); },
status: function(message) {}
};

View File

@@ -16,7 +16,7 @@ sub digitize {
}
while (<>) {
s/([^a-zA-Z0-9_])(\d+)/$1 . digitize $2/eg;
s/([^a-zA-Z0-9_"])(\d+)/$1 . digitize $2/eg;
print;
}

11
src/js/sjcl/configure vendored
View File

@@ -4,12 +4,13 @@ use strict;
my ($arg, $i, $j, $targ);
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase32 codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my %deps = ('aes'=>'sjcl',
'bitArray'=>'sjcl',
'codecString'=>'bitArray',
'codecHex'=>'bitArray',
'codecBase64'=>'bitArray',
'codecBase32'=>'bitArray',
'codecBytes'=>'bitArray',
'sha256'=>'codecString',
'sha512'=>'codecString',
@@ -32,10 +33,10 @@ my %enabled = ();
$enabled{$_} = 0 foreach (@targets);
# by default, all but codecBytes, srp, bn
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase32 codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
# argument parsing
while ($arg = shift @ARGV) {
while (my $arg = shift @ARGV) {
if ($arg =~ /^--?with-all$/) {
foreach (@targets) {
if ($enabled{$_} == 0) {
@@ -97,7 +98,7 @@ my $config = '';
my $pconfig;
# dependency analysis: forbidden
foreach $i (@targets) {
foreach my $i (@targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} == -1) {
@@ -114,7 +115,7 @@ foreach $i (@targets) {
}
# reverse
foreach $i (reverse @targets) {
foreach my $i (reverse @targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} < $enabled{$i}) {

View File

@@ -74,7 +74,7 @@ sjcl.bitArray = {
return a1.concat(a2);
}
var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
var last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
@@ -183,5 +183,19 @@ sjcl.bitArray = {
*/
_xor4: function(x,y) {
return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
},
/** byteswap a word array inplace.
* (does not handle partial words)
* @param {sjcl.bitArray} a word array
* @return {sjcl.bitArray} byteswapped array
*/
byteswapM: function(a) {
var i, v, m = 0xff00;
for (i = 0; i < a.length; ++i) {
v = a[i];
a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
}
return a;
}
};

View File

@@ -19,7 +19,7 @@ sjcl.bn.prototype = {
* Initializes this with it, either as a bn, a number, or a hex string.
*/
initWith: function(it) {
var i=0, k, n, l;
var i=0, k;
switch(typeof it) {
case "object":
this.limbs = it.limbs.slice(0);
@@ -328,7 +328,7 @@ sjcl.bn.prototype = {
carry = (l-m)*ipv;
}
if (carry === -1) {
limbs[i-1] -= this.placeVal;
limbs[i-1] -= pv;
}
return this;
},
@@ -370,7 +370,9 @@ sjcl.bn.prototype = {
}
};
/** @this { sjcl.bn } */
/** @memberOf sjcl.bn
* @this { sjcl.bn }
*/
sjcl.bn.fromBits = function(bits) {
var Class = this, out = new Class(), words=[], w=sjcl.bitArray, t = this.prototype,
l = Math.min(this.bitLength || 0x100000000, w.bitLength(bits)), e = l % t.radix || t.radix;
@@ -394,7 +396,9 @@ sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1;
* i.e. a prime of the form 2^e + sum(a * 2^b),where the sum is negative and sparse.
*/
sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
/** @constructor */
/** @constructor
* @private
*/
function p(it) {
this.initWith(it);
/*if (this.limbs[this.modOffset]) {
@@ -427,10 +431,11 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
ppr.modulus.cnormalize();
/** Approximate reduction mod p. May leave a number which is negative or slightly larger than p.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.reduce = function() {
var i, k, l, mo = this.modOffset, limbs = this.limbs, aff, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
var i, k, l, mo = this.modOffset, limbs = this.limbs, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
i = this.minOffset;
while (limbs.length > mo) {
@@ -452,7 +457,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
return this;
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr._strongReduce = (ppr.fullMask === -1) ? ppr.reduce : function() {
var limbs = this.limbs, i = limbs.length - 1, k, l;
this.reduce();
@@ -467,7 +474,8 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** mostly constant-time, very expensive full reduction.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.fullReduce = function() {
var greater, i;
@@ -501,7 +509,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.inverse = function() {
return (this.power(this.modulus.sub(2)));
};
@@ -512,18 +522,24 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
// a small Mersenne prime
var sbp = sjcl.bn.pseudoMersennePrime;
sjcl.bn.prime = {
p127: sjcl.bn.pseudoMersennePrime(127, [[0,-1]]),
p127: sbp(127, [[0,-1]]),
// Bernstein's prime for Curve25519
p25519: sjcl.bn.pseudoMersennePrime(255, [[0,-19]]),
p25519: sbp(255, [[0,-19]]),
// Koblitz primes
p192k: sbp(192, [[32,-1],[12,-1],[8,-1],[7,-1],[6,-1],[3,-1],[0,-1]]),
p224k: sbp(224, [[32,-1],[12,-1],[11,-1],[9,-1],[7,-1],[4,-1],[1,-1],[0,-1]]),
p256k: sbp(256, [[32,-1],[9,-1],[8,-1],[7,-1],[6,-1],[4,-1],[0,-1]]),
// NIST primes
p192: sjcl.bn.pseudoMersennePrime(192, [[0,-1],[64,-1]]),
p224: sjcl.bn.pseudoMersennePrime(224, [[0,1],[96,-1]]),
p256: sjcl.bn.pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sjcl.bn.pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sjcl.bn.pseudoMersennePrime(521, [[0,-1]])
p192: sbp(192, [[0,-1],[64,-1]]),
p224: sbp(224, [[0,1],[96,-1]]),
p256: sbp(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sbp(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sbp(521, [[0,-1]])
};
sjcl.bn.random = function(modulus, paranoia) {

View File

@@ -5,18 +5,18 @@
* @author Dan Boneh
*/
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
if (sjcl.beware === undefined) {
sjcl.beware = {};
}
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
] = function() {
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
sjcl.mode.cbc = {
/** The name of the mode.
* @constant
@@ -100,7 +100,7 @@ sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity.
/* check and remove the pad */
bi = output[i-1] & 255;
if (bi == 0 || bi > 16) {
if (bi === 0 || bi > 16) {
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
}
bo = bi * 0x1010101;

View File

@@ -25,7 +25,7 @@ sjcl.mode.ccm = {
* @return {bitArray} The encrypted data, an array of bytes.
*/
encrypt: function(prf, plaintext, iv, adata, tlen) {
var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
var L, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
tlen = tlen || 64;
adata = adata || [];
@@ -59,7 +59,7 @@ sjcl.mode.ccm = {
decrypt: function(prf, ciphertext, iv, adata, tlen) {
tlen = tlen || 64;
adata = adata || [];
var L, i,
var L,
w=sjcl.bitArray,
ivl = w.bitLength(iv) / 8,
ol = w.bitLength(ciphertext),
@@ -101,7 +101,7 @@ sjcl.mode.ccm = {
*/
_computeTag: function(prf, plaintext, iv, adata, tlen, L) {
// compute B[0]
var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
var mac, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
tlen /= 8;
@@ -161,7 +161,7 @@ sjcl.mode.ccm = {
* @private
*/
_ctrMode: function(prf, data, iv, tag, tlen, L) {
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, l = data.length, bl=w.bitLength(data);
// start the ctr
ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);

View File

@@ -0,0 +1,64 @@
/** @fileOverview Bit array codec implementations.
*
* @author Nils Kenneweg
*/
/** @namespace Base32 encoding/decoding */
sjcl.codec.base32 = {
/** The base32 alphabet.
* @private
*/
_chars: "0123456789abcdefghjkmnpqrstvwxyz",
/* bits in an array */
BITS: 32,
/* base to encode at (2^x) */
BASE: 5,
/* bits - base */
REMAINING: 27,
/** Convert from a bitArray to a base32 string. */
fromBits: function (arr, _noEquals) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = "", i, bits=0, c = sjcl.codec.base32._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
for (i=0; out.length * BASE <= bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> REMAINING);
if (bits < BASE) {
ta = arr[i] << (BASE-bits);
bits += REMAINING;
i++;
} else {
ta <<= BASE;
bits -= BASE;
}
}
return out;
},
/** Convert from a base32 string to a bitArray */
toBits: function(str) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = [], i, bits=0, c = sjcl.codec.base32._chars, ta=0, x;
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {
throw new sjcl.exception.invalid("this isn't base32!");
}
if (bits > REMAINING) {
bits -= REMAINING;
out.push(ta ^ x>>>bits);
ta = x << (BITS-bits);
} else {
bits += BASE;
ta ^= x << (BITS-bits);
}
}
if (bits&56) {
out.push(sjcl.bitArray.partial(bits&56, ta, 1));
}
return out;
}
};

View File

@@ -15,7 +15,9 @@ sjcl.codec.base64 = {
/** Convert from a bitArray to a base64 string. */
fromBits: function (arr, _noEquals, _url) {
var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; out.length * 6 < bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
if (bits < 6) {
@@ -35,7 +37,9 @@ sjcl.codec.base64 = {
toBits: function(str, _url) {
str = str.replace(/\s|=/g,'');
var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {

View File

@@ -9,7 +9,7 @@
sjcl.codec.hex = {
/** Convert from a bitArray to a hex string. */
fromBits: function (arr) {
var out = "", i, x;
var out = "", i;
for (i=0; i<arr.length; i++) {
out += ((arr[i]|0)+0xF00000000000).toString(16).substr(4);
}

View File

@@ -15,13 +15,13 @@
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext.
* @return {Object} The cipher raw data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
_encrypt: function (password, plaintext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
j.defaults), tmp, prp, adata;
j._add(p, params);
@@ -32,7 +32,7 @@
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -41,7 +41,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json encrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -58,39 +58,52 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* return the json data */
j._add(rp, p);
rp.key = password;
/* do the encryption */
p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
//return j.encode(j._subtract(p, j.defaults));
return p;
},
/** Simple encryption function.
* @param {String|bitArray} password The password or key.
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext serialized data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
var j = sjcl.json, p = j._encrypt.apply(j, arguments);
return j.encode(p);
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} ciphertext The cipher raw data to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
_decrypt: function (password, ciphertext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp, adata=p.adata;
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),ciphertext), params, true), ct, tmp, prp, adata=p.adata;
if (typeof p.salt === "string") {
p.salt = sjcl.codec.base64.toBits(p.salt);
}
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -100,7 +113,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json decrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -112,15 +125,33 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* do the decryption */
ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
/* return the json data */
j._add(rp, p);
rp.key = password;
return sjcl.codec.utf8String.fromBits(ct);
if (params.raw === 1) {
return ct;
} else {
return sjcl.codec.utf8String.fromBits(ct);
}
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
var j = sjcl.json;
return j._decrypt(password, j.decode(ciphertext), params, rp);
},
/** Encode a flat structure into a JSON string.
@@ -138,23 +169,23 @@
}
out += comma + '"' + i + '":';
comma = ',';
switch (typeof obj[i]) {
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
}
}
}
@@ -174,13 +205,15 @@
}
var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
for (i=0; i<a.length; i++) {
if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
if (!(m=a[i].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))) {
throw new sjcl.exception.invalid("json decode: this isn't json!");
}
if (m[3]) {
out[m[2]] = parseInt(m[3],10);
} else {
} else if (m[4]) {
out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
} else if (m[5]) {
out[m[2]] = m[5] === 'true';
}
}
return out;
@@ -213,13 +246,13 @@
*/
_subtract: function (plus, minus) {
var out = {}, i;
for (i in plus) {
if (plus.hasOwnProperty(i) && plus[i] !== minus[i]) {
out[i] = plus[i];
}
}
return out;
},
@@ -262,7 +295,7 @@ sjcl.misc._pbkdf2Cache = {};
/** Cached PBKDF2 key derivation.
* @param {String} password The password.
* @param {Object} [params] The derivation params (iteration count and optional salt).
* @param {Object} [obj] The derivation params (iteration count and optional salt).
* @return {Object} The derived data in key, the salt in salt.
*/
sjcl.misc.cachedPbkdf2 = function (password, obj) {

View File

@@ -1,3 +1,6 @@
/**
* base class for all ecc operations.
*/
sjcl.ecc = {};
/**
@@ -152,7 +155,9 @@ sjcl.ecc.pointJac.prototype = {
a = y2.mul(this.x.mul(4)),
b = y2.square().mul(8),
z2 = this.z.square(),
c = this.x.sub(z2).mul(3).mul(this.x.add(z2)),
c = this.curve.a.toString() == (new sjcl.bn(-3)).toString() ?
this.x.sub(z2).mul(3).mul(this.x.add(z2)) :
this.x.square().mul(3).add(z2.square().mul(this.curve.a)),
x = c.square().subM(a).subM(a),
y = a.sub(x).mul(c).subM(b),
z = this.y.add(this.y).mul(this.z);
@@ -162,7 +167,7 @@ sjcl.ecc.pointJac.prototype = {
/**
* Returns a copy of this point converted to affine coordinates.
* @return {sjcl.ecc.point} The converted point.
*/
*/
toAffine: function() {
if (this.isIdentity || this.z.equals(0)) {
return new sjcl.ecc.point(this.curve);
@@ -250,7 +255,7 @@ sjcl.ecc.pointJac.prototype = {
*/
sjcl.ecc.curve = function(Field, r, a, b, x, y) {
this.field = Field;
this.r = Field.prototype.modulus.sub(r);
this.r = new sjcl.bn(r);
this.a = new Field(a);
this.b = new Field(b);
this.G = new sjcl.ecc.point(this, new Field(x), new Field(y));
@@ -269,7 +274,7 @@ sjcl.ecc.curve.prototype.fromBits = function (bits) {
sjcl.ecc.curves = {
c192: new sjcl.ecc.curve(
sjcl.bn.prime.p192,
"0x662107c8eb94364e4b2dd7ce",
"0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
-3,
"0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
"0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
@@ -277,7 +282,7 @@ sjcl.ecc.curves = {
c224: new sjcl.ecc.curve(
sjcl.bn.prime.p224,
"0xe95c1f470fc1ec22d6baa3a3d5c4",
"0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
-3,
"0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
"0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
@@ -285,7 +290,7 @@ sjcl.ecc.curves = {
c256: new sjcl.ecc.curve(
sjcl.bn.prime.p256,
"0x4319055358e8617b0c46353d039cdaae",
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
-3,
"0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
@@ -293,71 +298,135 @@ sjcl.ecc.curves = {
c384: new sjcl.ecc.curve(
sjcl.bn.prime.p384,
"0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c",
"0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
-3,
"0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
"0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"),
k192: new sjcl.ecc.curve(
sjcl.bn.prime.p192k,
"0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d",
0,
3,
"0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d",
"0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d"),
k224: new sjcl.ecc.curve(
sjcl.bn.prime.p224k,
"0x010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7",
0,
5,
"0xa1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c",
"0x7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5"),
k256: new sjcl.ecc.curve(
sjcl.bn.prime.p256k,
"0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")
};
/* Diffie-Hellman-like public-key system */
sjcl.ecc._dh = function(cn) {
sjcl.ecc[cn] = {
/** @constructor */
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
}
},
/** @constructor */
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
this.get = function() {
return this._exponent.toBits();
}
},
/** @constructor */
generateKeys: function(curve, paranoia, sec) {
if (curve === undefined) {
curve = 256;
}
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
if (sec === undefined) {
var sec = sjcl.bn.random(curve.r, paranoia);
}
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
/** our basicKey classes
*/
sjcl.ecc.basicKey = {
/** ecc publicKey.
* @constructor
* @param {curve} curve the elliptic curve
* @param {point} point the point on the curve
*/
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
};
/** get this keys point data
* @return x and y as bitArrays
*/
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
};
},
/** ecc secretKey
* @constructor
* @param {curve} curve the elliptic curve
* @param exponent
*/
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
/** get this keys exponent data
* @return {bitArray} exponent
*/
this.get = function () {
return this._exponent.toBits();
};
}
};
sjcl.ecc._dh("elGamal");
/** @private */
sjcl.ecc.basicKey.generateKeys = function(cn) {
return function generateKeys(curve, paranoia, sec) {
curve = curve || 256;
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
sec = sec || sjcl.bn.random(curve.r, paranoia);
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
};
};
/** elGamal keys */
sjcl.ecc.elGamal = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("elGamal"),
/** elGamal publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
publicKey: function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
},
/** elGamal secretKey
* @constructor
* @augments sjcl.ecc.basicKey.secretKey
*/
secretKey: function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
}
};
sjcl.ecc.elGamal.publicKey.prototype = {
/** Kem function of elGamal Public Key
* @param paranoia paranoia to use for randomization.
* @return {object} key and tag. unkem(tag) with the corresponding secret key results in the key returned.
*/
kem: function(paranoia) {
var sec = sjcl.bn.random(this._curve.r, paranoia),
tag = this._curve.G.mult(sec).toBits(),
@@ -367,34 +436,58 @@ sjcl.ecc.elGamal.publicKey.prototype = {
};
sjcl.ecc.elGamal.secretKey.prototype = {
/** UnKem function of elGamal Secret Key
* @param {bitArray} tag The Tag to decrypt.
* @return {bitArray} decrypted key.
*/
unkem: function(tag) {
return sjcl.hash.sha256.hash(this._curve.fromBits(tag).mult(this._exponent).toBits());
},
/** Diffie-Hellmann function
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} diffie-hellmann result for this key combination.
*/
dh: function(pk) {
return sjcl.hash.sha256.hash(pk._point.mult(this._exponent).toBits());
}
},
/** Diffie-Hellmann function, compatible with Java generateSecret
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} undigested X value, diffie-hellmann result for this key combination,
* compatible with Java generateSecret().
*/
dhJavaEc: function(pk) {
return pk._point.mult(this._exponent).x.toBits();
}
};
sjcl.ecc._dh("ecdsa");
sjcl.ecc.ecdsa.secretKey.prototype = {
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
/** ecdsa keys */
sjcl.ecc.ecdsa = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("ecdsa")
};
/** ecdsa publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.publicKey = function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
};
/** specific functions for ecdsa publicKey. */
sjcl.ecc.ecdsa.publicKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to verify.
* @param {bitArray} rs signature bitArray.
* @param {boolean} fakeLegacyVersion use old legacy version
*/
verify: function(hash, rs, fakeLegacyVersion) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
@@ -418,3 +511,33 @@ sjcl.ecc.ecdsa.publicKey.prototype = {
return true;
}
};
/** ecdsa secretKey
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.secretKey = function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
};
/** specific functions for ecdsa secretKey. */
sjcl.ecc.ecdsa.secretKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to sign.
* @param {int} paranoia paranoia for random number generation
* @param {boolean} fakeLegacyVersion use old legacy version
*/
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
};

View File

@@ -120,7 +120,7 @@ sjcl.mode.gcm = {
* @param {Number} tlen The length of the tag, in bits.
*/
_ctrMode: function(encrypt, prf, data, adata, iv, tlen) {
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray, xor=w._xor4;
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray;
// Calculate data lengths
l = data.length;

View File

@@ -27,13 +27,35 @@ sjcl.misc.hmac = function (key, Hash) {
this._baseHash[0].update(exKey[0]);
this._baseHash[1].update(exKey[1]);
this._resultHash = new Hash(this._baseHash[0]);
};
/** HMAC with the specified hash function. Also called encrypt since it's a prf.
* @param {bitArray|String} data The data to mac.
*/
sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {
var w = new (this._hash)(this._baseHash[0]).update(data).finalize();
return new (this._hash)(this._baseHash[1]).update(w).finalize();
if (!this._updated) {
this.update(data);
return this.digest(data);
} else {
throw new sjcl.exception.invalid("encrypt on already updated hmac called!");
}
};
sjcl.misc.hmac.prototype.reset = function () {
this._resultHash = new this._hash(this._baseHash[0]);
this._updated = false;
};
sjcl.misc.hmac.prototype.update = function (data) {
this._updated = true;
this._resultHash.update(data);
};
sjcl.misc.hmac.prototype.digest = function () {
var w = this._resultHash.finalize(), result = new (this._hash)(this._baseHash[1]).update(w).finalize();
this.reset();
return result;
};

View File

@@ -12,7 +12,7 @@
* This is the method specified by RSA's PKCS #5 standard.
*
* @param {bitArray|String} password The password.
* @param {bitArray} salt The salt. Should have lots of entropy.
* @param {bitArray|String} salt The salt. Should have lots of entropy.
* @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
* @param {Number} [length] The length of the derived key. Defaults to the
output size of the hash function.
@@ -30,6 +30,10 @@ sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
password = sjcl.codec.utf8String.toBits(password);
}
if (typeof salt === "string") {
salt = sjcl.codec.utf8String.toBits(salt);
}
Prff = Prff || sjcl.misc.hmac;
var prf = new Prff(password),

View File

@@ -3,12 +3,13 @@
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
* @author Michael Brooks
*/
/** @constructor
* @class Random number generator
*
* @description
* <b>Use sjcl.random as a singleton for this class!</b>
* <p>
* This random number generator is a derivative of Ferguson and Schneier's
* generator Fortuna. It collects entropy from various events into several
@@ -74,10 +75,11 @@ sjcl.prng = function(defaultParanoia) {
this._PARANOIA_LEVELS = [0,48,64,96,128,192,256,384,512,768,1024];
this._MILLISECONDS_PER_RESEED = 30000;
this._BITS_PER_RESEED = 80;
}
};
sjcl.prng.prototype = {
/** Generate several random words, and return them in an array
/** Generate several random words, and return them in an array.
* A word consists of 32 bits (4 bytes)
* @param {Number} nwords The number of words to generate.
*/
randomWords: function (nwords, paranoia) {
@@ -102,7 +104,11 @@ sjcl.prng.prototype = {
return out.slice(0,nwords);
},
setDefaultParanoia: function (paranoia) {
setDefaultParanoia: function (paranoia, allowZeroParanoia) {
if (paranoia === 0 && allowZeroParanoia !== "Setting paranoia=0 will ruin your security; use it only for testing") {
throw "Setting paranoia=0 will ruin your security; use it only for testing";
}
this._defaultParanoia = paranoia;
},
@@ -119,7 +125,7 @@ sjcl.prng.prototype = {
i, tmp,
t = (new Date()).valueOf(),
robin = this._robins[source],
oldReady = this.isReady(), err = 0;
oldReady = this.isReady(), err = 0, objName;
id = this._collectorIds[source];
if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; }
@@ -137,7 +143,7 @@ sjcl.prng.prototype = {
break;
case "object":
var objName = Object.prototype.toString.call(data);
objName = Object.prototype.toString.call(data);
if (objName === "[object Uint32Array]") {
tmp = [];
for (i = 0; i < data.length; i++) {
@@ -149,7 +155,7 @@ sjcl.prng.prototype = {
err = 1;
}
for (i=0; i<data.length && !err; i++) {
if (typeof(data[i]) != "number") {
if (typeof(data[i]) !== "number") {
err = 1;
}
}
@@ -234,14 +240,25 @@ sjcl.prng.prototype = {
startCollectors: function () {
if (this._collectorsStarted) { return; }
this._eventListener = {
loadTimeCollector: this._bind(this._loadTimeCollector),
mouseCollector: this._bind(this._mouseCollector),
keyboardCollector: this._bind(this._keyboardCollector),
accelerometerCollector: this._bind(this._accelerometerCollector),
touchCollector: this._bind(this._touchCollector)
};
if (window.addEventListener) {
window.addEventListener("load", this._loadTimeCollector, false);
window.addEventListener("mousemove", this._mouseCollector, false);
window.addEventListener("load", this._eventListener.loadTimeCollector, false);
window.addEventListener("mousemove", this._eventListener.mouseCollector, false);
window.addEventListener("keypress", this._eventListener.keyboardCollector, false);
window.addEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.addEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.attachEvent) {
document.attachEvent("onload", this._loadTimeCollector);
document.attachEvent("onmousemove", this._mouseCollector);
}
else {
document.attachEvent("onload", this._eventListener.loadTimeCollector);
document.attachEvent("onmousemove", this._eventListener.mouseCollector);
document.attachEvent("keypress", this._eventListener.keyboardCollector);
} else {
throw new sjcl.exception.bug("can't attach event");
}
@@ -253,12 +270,17 @@ sjcl.prng.prototype = {
if (!this._collectorsStarted) { return; }
if (window.removeEventListener) {
window.removeEventListener("load", this._loadTimeCollector, false);
window.removeEventListener("mousemove", this._mouseCollector, false);
} else if (window.detachEvent) {
window.detachEvent("onload", this._loadTimeCollector);
window.detachEvent("onmousemove", this._mouseCollector);
window.removeEventListener("load", this._eventListener.loadTimeCollector, false);
window.removeEventListener("mousemove", this._eventListener.mouseCollector, false);
window.removeEventListener("keypress", this._eventListener.keyboardCollector, false);
window.removeEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.removeEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.detachEvent) {
document.detachEvent("onload", this._eventListener.loadTimeCollector);
document.detachEvent("onmousemove", this._eventListener.mouseCollector);
document.detachEvent("keypress", this._eventListener.keyboardCollector);
}
this._collectorsStarted = false;
},
@@ -275,23 +297,30 @@ sjcl.prng.prototype = {
/** remove an event listener for progress or seeded-ness */
removeEventListener: function (name, cb) {
var i, j, cbs=this._callbacks[name], jsTemp=[];
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
jsTemp.push(j);
}
}
for (i=0; i<jsTemp.length; i++) {
j = jsTemp[i];
delete cbs[j];
}
},
_bind: function (func) {
var that = this;
return function () {
func.apply(that, arguments);
};
},
/** Generate 4 random words, no reseed, no gate.
* @private
*/
@@ -363,44 +392,131 @@ sjcl.prng.prototype = {
this._reseed(reseedData);
},
_keyboardCollector: function () {
this._addCurrentTimeToEntropy(1);
},
_mouseCollector: function (ev) {
var x = ev.x || ev.clientX || ev.offsetX || 0, y = ev.y || ev.clientY || ev.offsetY || 0;
sjcl.random.addEntropy([x,y], 2, "mouse");
var x, y;
try {
x = ev.x || ev.clientX || ev.offsetX || 0;
y = ev.y || ev.clientY || ev.offsetY || 0;
} catch (err) {
// Event originated from a secure element. No mouse position available.
x = 0;
y = 0;
}
if (x != 0 && y!= 0) {
sjcl.random.addEntropy([x,y], 2, "mouse");
}
this._addCurrentTimeToEntropy(0);
},
_touchCollector: function(ev) {
var touch = ev.touches[0] || ev.changedTouches[0];
var x = touch.pageX || touch.clientX,
y = touch.pageY || touch.clientY;
sjcl.random.addEntropy([x,y],1,"touch");
this._addCurrentTimeToEntropy(0);
},
_loadTimeCollector: function (ev) {
sjcl.random.addEntropy((new Date()).valueOf(), 2, "loadtime");
_loadTimeCollector: function () {
this._addCurrentTimeToEntropy(2);
},
_addCurrentTimeToEntropy: function (estimatedEntropy) {
if (typeof window !== 'undefined' && window.performance && typeof window.performance.now === "function") {
//how much entropy do we want to add here?
sjcl.random.addEntropy(window.performance.now(), estimatedEntropy, "loadtime");
} else {
sjcl.random.addEntropy((new Date()).valueOf(), estimatedEntropy, "loadtime");
}
},
_accelerometerCollector: function (ev) {
var ac = ev.accelerationIncludingGravity.x||ev.accelerationIncludingGravity.y||ev.accelerationIncludingGravity.z;
if(window.orientation){
var or = window.orientation;
if (typeof or === "number") {
sjcl.random.addEntropy(or, 1, "accelerometer");
}
}
if (ac) {
sjcl.random.addEntropy(ac, 2, "accelerometer");
}
this._addCurrentTimeToEntropy(0);
},
_fireEvent: function (name, arg) {
var j, cbs=sjcl.random._callbacks[name], cbsTemp=[];
/* TODO: there is a race condition between removing collectors and firing them */
/* TODO: there is a race condition between removing collectors and firing them */
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j)) {
if (cbs.hasOwnProperty(j)) {
cbsTemp.push(cbs[j]);
}
}
}
for (j=0; j<cbsTemp.length; j++) {
cbsTemp[j](arg);
cbsTemp[j](arg);
}
}
};
/** an instance for the prng.
* @see sjcl.prng
*/
sjcl.random = new sjcl.prng(6);
(function(){
try {
// get cryptographically strong entropy in Webkit
var ab = new Uint32Array(32);
crypto.getRandomValues(ab);
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} catch (e) {
// no getRandomValues :-(
// function for getting nodejs crypto module. catches and ignores errors.
function getCryptoModule() {
try {
return require('crypto');
}
catch (e) {
return null;
}
}
})();
try {
var buf, crypt, ab;
// get cryptographically strong entropy depending on runtime environment
if (typeof module !== 'undefined' && module.exports && (crypt = getCryptoModule()) && crypt.randomBytes) {
buf = crypt.randomBytes(1024/8);
buf = new Uint32Array(new Uint8Array(buf).buffer);
sjcl.random.addEntropy(buf, 1024, "crypto.randomBytes");
} else if (typeof window !== 'undefined' && typeof Uint32Array !== 'undefined') {
ab = new Uint32Array(32);
if (window.crypto && window.crypto.getRandomValues) {
window.crypto.getRandomValues(ab);
} else if (window.msCrypto && window.msCrypto.getRandomValues) {
window.msCrypto.getRandomValues(ab);
} else {
return;
}
// get cryptographically strong entropy in Webkit
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} else {
// no getRandomValues :-(
}
} catch (e) {
if (typeof window !== 'undefined' && window.console) {
console.log("There was an error collecting entropy from the browser:");
console.log(e);
//we do not want the library to fail due to randomness not being maintained.
}
}
}());

View File

@@ -138,8 +138,7 @@ sjcl.hash.sha1.prototype = {
_block:function (words) {
var t, tmp, a, b, c, d, e,
w = words.slice(0),
h = this._h,
k = this._key;
h = this._h;
a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4];

View File

@@ -299,7 +299,7 @@ sjcl.hash.sha512.prototype = {
t1h += chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
t1l += krl;
t1h += krh + ((t1l >>> 0) < (krl >>> 0) ? 1 : 0);
t1l += wrl;
t1l = t1l + wrl|0; // FF32..FF34 perf issue https://bugzilla.mozilla.org/show_bug.cgi?id=1054972
t1h += wrh + ((t1l >>> 0) < (wrl >>> 0) ? 1 : 0);
// t2 = sigma0 + maj

View File

@@ -10,7 +10,7 @@
"use strict";
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
/*global document, window, escape, unescape */
/*global document, window, escape, unescape, module, require, Uint32Array */
/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
var sjcl = {
@@ -68,6 +68,11 @@ var sjcl = {
}
};
if(typeof module != 'undefined' && module.exports){
if(typeof module !== 'undefined' && module.exports){
module.exports = sjcl;
}
if (typeof define === "function") {
define([], function () {
return sjcl;
});
}

View File

@@ -36,7 +36,7 @@
<h2>Key Derivation</h2>
<div class="section">
<div>
<label for="salt"">Salt:</label>
<label for="salt">Salt:</label>
<a class="random floatright" href="javascript:randomize('salt',2,0)">random</a>
</div>
<input type="text" id="salt" class="wide hex" autocomplete="off" size="17" maxlength="35"/>

16
src/js/sjcl/package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "sjcl",
"version": "1.0.1",
"description": "Stanford Javascript Crypto Library",
"main": "sjcl.js",
"author": "bitwiseshiftleft",
"keywords": ["encryption", "high-level", "crypto"],
"repository" : {
"type": "git",
"url": "https://github.com/bitwiseshiftleft/sjcl.git"
},
"scripts": {
"test": "make test"
},
"engines": { "node": "*" }
}

View File

@@ -1,48 +1,54 @@
"use strict";function q(a){throw a;}var t=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
"undefined"!=typeof module&&module.exports&&(module.exports=sjcl);
sjcl.cipher.aes=function(a){this.j[0][0][0]||this.D();var b,c,d,e,f=this.j[0][4],g=this.j[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
sjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
255]]};
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},j:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.j[0],b=this.j[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.a[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.j[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s}
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.O(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.O(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
c},O:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
c},P:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},l:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
sjcl.codec.base64={I:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.I,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.I,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.D();a?(this.q=a.q.slice(0),this.m=a.m.slice(0),this.g=a.g):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.q=this.M.slice(0);this.m=[];this.g=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.m=sjcl.bitArray.concat(this.m,a);b=this.g;a=this.g=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.m,c=this.q,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.g/
4294967296));for(b.push(this.g|0);b.length;)z(this,b.splice(0,16));this.reset();return c},M:[],a:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.M[b]=a(Math.pow(c,0.5)));this.a[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.q,h=a.a,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
sjcl.codec.base64={J:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.J,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/
4294967296));for(b.push(this.h|0);b.length;)z(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
g[6]+r|0;g[7]=g[7]+v|0}
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.K(a,b,c,d,e,f);g=sjcl.mode.ccm.n(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.n(a,l,c,k,e,b);a=sjcl.mode.ccm.K(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},K:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.k;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},n:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},p:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.l;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!l)return{tag:d,data:[]};for(g=0;g<l;g+=4)c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.G,l=sjcl.bitArray,k=l.k,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.G,h=sjcl.bitArray,l=h.k,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.G,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},G:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.n(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.n(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},U:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.k;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},f:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.U(b,a);return b},n:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.f(g,[0,0,0,0],e),e=sjcl.mode.gcm.f(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.f(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.f(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.f(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.f(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.L=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.o=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.o[0].update(c[0]);this.o[1].update(c[1])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.L(this.o[0])).update(a).finalize();return(new this.L(this.o[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.b=[new sjcl.hash.sha256];this.h=[0];this.F=0;this.t={};this.C=0;this.J={};this.N=this.c=this.i=this.T=0;this.a=[0,0,0,0,0,0,0,0];this.e=[0,0,0,0];this.A=t;this.B=a;this.p=u;this.z={progress:{},seeded:{}};this.l=this.S=0;this.u=1;this.w=2;this.Q=0x10000;this.H=[0,48,64,96,128,192,0x100,384,512,768,1024];this.R=3E4;this.P=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.l&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.w){d=!(d&this.u);e=[];var f=0,g;this.N=e[0]=(new Date).valueOf()+this.R;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.b.length&&!(e=e.concat(this.b[g].finalize()),f+=this.h[g],this.h[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.b.length&&(this.b.push(new sjcl.hash.sha256),this.h.push(0));this.c-=f;f>this.i&&(this.i=f);this.F++;
this.a=sjcl.hash.sha256.hash(this.a.concat(e));this.A=new sjcl.cipher.aes(this.a);for(d=0;4>d&&!(this.e[d]=this.e[d]+1|0,this.e[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.Q&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a){this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.t[c],h=this.isReady(),l=0;d=this.J[c];d===t&&(d=this.J[c]=this.T++);g===t&&(g=this.t[c]=0);this.t[c]=(this.t[c]+1)%this.b.length;switch(typeof a){case "number":b===
t&&(b=1);this.b[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!=typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e>>>=1;this.b[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.b[g].update([d,this.C++,3,b,f,a.length]);this.b[g].update(a);
break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.h[g]+=b;this.c+=b;h===this.l&&(this.isReady()!==this.l&&C("seeded",Math.max(this.i,this.c)),C("progress",this.getProgress()))},isReady:function(a){a=this.H[a!==t?a:this.B];return this.i&&this.i>=a?this.h[0]>this.P&&(new Date).valueOf()>this.N?this.w|this.u:this.u:this.c>=a?this.w|this.l:this.l},getProgress:function(a){a=this.H[a?a:this.B];return this.i>=a?1:this.c>a?1:this.c/
a},startCollectors:function(){this.p||(window.addEventListener?(window.addEventListener("load",this.r,u),window.addEventListener("mousemove",this.s,u)):document.attachEvent?(document.attachEvent("onload",this.r),document.attachEvent("onmousemove",this.s)):q(new sjcl.exception.bug("can't attach event")),this.p=!0)},stopCollectors:function(){this.p&&(window.removeEventListener?(window.removeEventListener("load",this.r,u),window.removeEventListener("mousemove",this.s,u)):window.detachEvent&&(window.detachEvent("onload",
this.r),window.detachEvent("onmousemove",this.s)),this.p=u)},addEventListener:function(a,b){this.z[a][this.S++]=b},removeEventListener:function(a,b){var c,d,e=this.z[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},s:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},r:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")}};
function C(a,b){var c,d=sjcl.random.z[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function A(a){a.a=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.a)}function B(a){for(var b=0;4>b&&!(a.e[b]=a.e[b]+1|0,a.e[b]);b++);return a.A.encrypt(a.e)}sjcl.random=new sjcl.prng(6);try{var D=new Uint32Array(32);crypto.getRandomValues(D);sjcl.random.addEntropy(D,1024,"crypto['getRandomValues']")}catch(E){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.d({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.d(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
4<f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.d(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},
decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.d(e.d(e.d({},e.defaults),e.decode(b)),c,!0);var f;c=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(f=sjcl.misc.cachedPbkdf2(a,b),a=f.key.slice(0,b.ks/32),b.salt=f.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.d(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||
q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));a=a.replace(/^\{|\}$/g,"").split(/,/);var b=
{},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},d:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},X:function(a,
b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.V={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.V,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.H,l=sjcl.bitArray,k=l.l,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.H,h=sjcl.bitArray,l=h.l,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.H,e=sjcl.bitArray,f=e.l,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.Z(b,a);return b},p:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.g(g,[0,0,0,0],e),e=sjcl.mode.gcm.g(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.g(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.g(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.g(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.g(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.n=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.n[0].update(c[0]);this.n[1].update(c[1]);this.G=new b(this.n[0])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){this.Q&&q(new sjcl.exception.invalid("encrypt on already updated hmac called!"));this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.G=new this.M(this.n[0]);this.Q=u};sjcl.misc.hmac.prototype.update=function(a){this.Q=!0;this.G.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.G.finalize(),a=(new this.M(this.n[1])).update(a).finalize();this.reset();return a};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.i=[0];this.F=0;this.s={};this.C=0;this.K={};this.O=this.d=this.j=this.W=0;this.b=[0,0,0,0,0,0,0,0];this.f=[0,0,0,0];this.A=t;this.B=a;this.q=u;this.w={progress:{},seeded:{}};this.m=this.V=0;this.t=1;this.u=2;this.S=0x10000;this.I=[0,48,64,96,128,192,0x100,384,512,768,1024];this.T=3E4;this.R=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.m&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.u){d=!(d&this.t);e=[];var f=0,g;this.O=e[0]=(new Date).valueOf()+this.T;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.c.length&&!(e=e.concat(this.c[g].finalize()),f+=this.i[g],this.i[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.i.push(0));this.d-=f;f>this.j&&(this.j=f);this.F++;
this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.S&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b&&q("Setting paranoia=0 will ruin your security; use it only for testing");this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),
g=this.s[c],h=this.isReady(),l=0;d=this.K[c];d===t&&(d=this.K[c]=this.W++);g===t&&(g=this.s[c]=0);this.s[c]=(this.s[c]+1)%this.c.length;switch(typeof a){case "number":b===t&&(b=1);this.c[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!==typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,
e>>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.j,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.I[a!==t?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&&
(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector,u),window.addEventListener("keypress",
this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",
this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&&
e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},$:function(){E(1)},ba:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&sjcl.random.addEntropy([b,c],2,"mouse");E(0)},aa:function(){E(2)},U:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&sjcl.random.addEntropy(b,1,"accelerometer")}a&&sjcl.random.addEntropy(a,
2,"accelerometer");E(0)}};function C(a,b){var c,d=sjcl.random.w[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function E(a){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?sjcl.random.addEntropy(window.performance.now(),a,"loadtime"):sjcl.random.addEntropy((new Date).valueOf(),a,"loadtime")}function A(a){a.b=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.b)}
function B(a){for(var b=0;4>b&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);
else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4<
f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,
b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f=sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.e(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.X(a,e.decode(b),c,d)},encode:function(a){var b,
c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));
a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},e:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),
a[d]=b[d]);return a},ea:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},da:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.ca={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.ca,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};

View File

@@ -0,0 +1,115 @@
(function() {
function word2hex(w) {
return "0x" + ((w|0)+0xF00000000000).toString(16).substr(4);
}
var b0 = sjcl.bitArray.partial(1, 0);
var b1 = sjcl.bitArray.partial(1, 1);
function concatbits(s) {
var j, b, a = [];
for (j = 0; j < s.length; ++j) {
b = (s[j] == '1' ? b1 : b0);
a = sjcl.bitArray.concat(a, [b]);
}
return a;
}
new sjcl.test.TestCase("bitArray single bits", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
this.require((b0|0) === (0x00000000|0), "bitstring '0': " + word2hex(b0));
this.require((b1|0) === (0x80000000|0), "bitstring '1': " + word2hex(b1));
cb && cb();
});
new sjcl.test.TestCase("bitArray concat small bitstrings", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.bits, tv, a, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = concatbits(tv[0]);
bitlen = sjcl.bitArray.bitLength(a);
t = "bitstring '" + tv[0] + "': ";
this.require(1 === a.length, t + "array length is 1: " + a.length);
this.require(bitlen === tv[0].length, t + "length " + bitlen + " matches input length " + tv[0].length);
b = sjcl.bitArray.partial(tv[0].length, tv[1]);
this.require(a[0] === b, t + "array matches shifted number: " + word2hex(a[0]) + " == " + word2hex(b));
b = 0 | (a[0] >>> (32 - tv[0].length)); // unsigned shift, convert to signed word
this.require(b === (tv[1]|0), t + "array entry shifted is number: " + word2hex(b) + " == " + word2hex(tv[1]));
}
cb && cb();
});
new sjcl.test.TestCase("bitArray concat, slicing, shifting and clamping", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, j, kat = sjcl.test.vector.bitArray.slices, tv, a, a1, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = [];
b = [];
bitlen = 0;
for (j=0; j<tv[0].length; j++) {
b[j] = concatbits(tv[0][j]);
a = sjcl.bitArray.concat(a, b[j]);
bitlen += tv[0][j].length;
}
// shift last array entry and set partial length on it
a1 = tv[1]; a1 = a1.slice(0, a1.length);
bitlen &= 31;
if (0 !== bitlen) a1[a1.length-1] = sjcl.bitArray.partial(bitlen, a1[a1.length-1]);
this.require(sjcl.bitArray.equal(a, a1), "concat: [" + a + "] == [" + a1 + "]");
t = 0;
for (j=0; j<tv[0].length; j++) {
bitlen = sjcl.bitArray.bitLength(b[j]);
this.require(bitlen === tv[0][j].length, "bitstring length");
a1 = sjcl.bitArray.bitSlice(a, t, t + bitlen);
this.require(sjcl.bitArray.equal(b[j], a1), "slice after concat: [" + b[j] + "] == [" + a1 + "]");
t += bitlen;
}
}
cb && cb();
});
new sjcl.test.TestCase("bitArray byteswap", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.byteswap, tv, a;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = tv[1];
this.require(sjcl.bitArray.equal(tv[0], sjcl.bitArray.byteswapM(a.slice(0, a.length))));
}
cb && cb();
});
})();

View File

@@ -0,0 +1,163 @@
sjcl.test.vector.bitArray = {};
// random test cases generated with ruby String#to_i(radix) and Fixnum#to_s(radix)
// bitstrings (<= 32 bits) encoding a number
sjcl.test.vector.bitArray.bits = [
[ "00" , 0|0x0 ],
[ "01" , 0|0x1 ],
[ "10" , 0|0x2 ],
[ "11" , 0|0x3 ],
[ "000" , 0|0x0 ],
[ "011" , 0|0x3 ],
[ "101" , 0|0x5 ],
[ "110" , 0|0x6 ],
[ "0100" , 0|0x4 ],
[ "0101" , 0|0x5 ],
[ "1000" , 0|0x8 ],
[ "1100" , 0|0xc ],
[ "00101" , 0|0x5 ],
[ "01010" , 0|0xa ],
[ "10011" , 0|0x13 ],
[ "11010" , 0|0x1a ],
[ "001100" , 0|0xc ],
[ "001110" , 0|0xe ],
[ "010100" , 0|0x14 ],
[ "100111" , 0|0x27 ],
[ "0001011" , 0|0xb ],
[ "0001101" , 0|0xd ],
[ "1000100" , 0|0x44 ],
[ "1101011" , 0|0x6b ],
[ "00100001" , 0|0x21 ],
[ "00100111" , 0|0x27 ],
[ "00101000" , 0|0x28 ],
[ "10101111" , 0|0xaf ],
[ "000100000" , 0|0x20 ],
[ "100100011" , 0|0x123 ],
[ "100111001" , 0|0x139 ],
[ "111010011" , 0|0x1d3 ],
[ "0001001011" , 0|0x4b ],
[ "0001100110" , 0|0x66 ],
[ "0010110111" , 0|0xb7 ],
[ "1011101111" , 0|0x2ef ],
[ "01000010110" , 0|0x216 ],
[ "01001101000" , 0|0x268 ],
[ "10111101001" , 0|0x5e9 ],
[ "11111100000" , 0|0x7e0 ],
[ "000101010001" , 0|0x151 ],
[ "010101111111" , 0|0x57f ],
[ "101010001110" , 0|0xa8e ],
[ "110101100010" , 0|0xd62 ],
[ "0010001111010" , 0|0x47a ],
[ "1000000001100" , 0|0x100c ],
[ "1100011000000" , 0|0x18c0 ],
[ "1110011000011" , 0|0x1cc3 ],
[ "00111101111110" , 0|0xf7e ],
[ "01101011001001" , 0|0x1ac9 ],
[ "10111000111101" , 0|0x2e3d ],
[ "11101010011110" , 0|0x3a9e ],
[ "010111101010110" , 0|0x2f56 ],
[ "100011010110000" , 0|0x46b0 ],
[ "110001001100100" , 0|0x6264 ],
[ "111011000100110" , 0|0x7626 ],
[ "0100101111001100" , 0|0x4bcc ],
[ "1000000001101100" , 0|0x806c ],
[ "1001000100110110" , 0|0x9136 ],
[ "1101010000100011" , 0|0xd423 ],
[ "01001001001101110" , 0|0x926e ],
[ "01111001111000010" , 0|0xf3c2 ],
[ "10011011011000011" , 0|0x136c3 ],
[ "10101011001110000" , 0|0x15670 ],
[ "010000000101000110" , 0|0x10146 ],
[ "011000100101110001" , 0|0x18971 ],
[ "101100100110110111" , 0|0x2c9b7 ],
[ "101110100100101111" , 0|0x2e92f ],
[ "0010101100101000000" , 0|0x15940 ],
[ "1011010010000101010" , 0|0x5a42a ],
[ "1011100111011011000" , 0|0x5ced8 ],
[ "1111011110011111110" , 0|0x7bcfe ],
[ "00101000011011111111" , 0|0x286ff ],
[ "01111001100011000100" , 0|0x798c4 ],
[ "11111001001110101011" , 0|0xf93ab ],
[ "11111001111001101001" , 0|0xf9e69 ],
[ "000110100000110010101" , 0|0x34195 ],
[ "011110000101101101111" , 0|0xf0b6f ],
[ "101111010011001100110" , 0|0x17a666 ],
[ "111101001011110010001" , 0|0x1e9791 ],
[ "1001111100011011100001" , 0|0x27c6e1 ],
[ "1011110101000101010110" , 0|0x2f5156 ],
[ "1100000100011110001011" , 0|0x30478b ],
[ "1100001010010110111111" , 0|0x30a5bf ],
[ "01001111001101000111101" , 0|0x279a3d ],
[ "10110011101111110000001" , 0|0x59df81 ],
[ "11000001101100110100011" , 0|0x60d9a3 ],
[ "11011000010110110010110" , 0|0x6c2d96 ],
[ "010011110100100110010100" , 0|0x4f4994 ],
[ "011101110001100111111110" , 0|0x7719fe ],
[ "011110001010011011100011" , 0|0x78a6e3 ],
[ "111100010001011101111110" , 0|0xf1177e ],
[ "0010110000010110001000010" , 0|0x582c42 ],
[ "0100100011001001101110000" , 0|0x919370 ],
[ "1000111001010110111010110" , 0|0x11cadd6 ],
[ "1111001100101000010010101" , 0|0x1e65095 ],
[ "00110010011100010101111111" , 0|0xc9c57f ],
[ "00111101011011010100111110" , 0|0xf5b53e ],
[ "01100000111011111010011100" , 0|0x183be9c ],
[ "11010001100110101111010111" , 0|0x3466bd7 ],
[ "010000001111100110000110011" , 0|0x207cc33 ],
[ "011010010000110000101011111" , 0|0x348615f ],
[ "011010110001110110001110010" , 0|0x358ec72 ],
[ "110110100001001001001110000" , 0|0x6d09270 ],
[ "0101000000000001000011100101" , 0|0x50010e5 ],
[ "0110011001001100011111111100" , 0|0x664c7fc ],
[ "1001011010000001100110111101" , 0|0x96819bd ],
[ "1011011101000000111000000010" , 0|0xb740e02 ],
[ "00100111111011011111010101101" , 0|0x4fdbead ],
[ "00110000011001110110101110010" , 0|0x60ced72 ],
[ "10111110111000010010010111101" , 0|0x17dc24bd ],
[ "11010001000001110010101000010" , 0|0x1a20e542 ],
[ "001001010001010111111101010111" , 0|0x9457f57 ],
[ "100110011101100000110111111010" , 0|0x26760dfa ],
[ "100111000100011001010011111011" , 0|0x271194fb ],
[ "111011110110101110110111010101" , 0|0x3bdaedd5 ],
[ "0111100111010100101010000111100" , 0|0x3cea543c ],
[ "1000010011010100111001110000100" , 0|0x426a7384 ],
[ "1001110000000100001011010001010" , 0|0x4e02168a ],
[ "1101000000000110110010011010100" , 0|0x680364d4 ],
[ "00000011100100001011101011100111", 0|0x390bae7 ],
[ "10011110110111111000010010010011", 0|0x9edf8493 ],
[ "11000101100000110001011010111100", 0|0xc58316bc ],
[ "11111010101110011001010001011000", 0|0xfab99458 ]
];
// concat some bitstrings into an array (the last array entry here is not "high" shifted yet)
sjcl.test.vector.bitArray.slices = [
// lengths: 17, 16, 15, 14, 13
[ ["00100010111010110", "0010000111100001", "101111111100010", "10111011110000", "1100100100001" ], [0|0x22eb10f0, 0|0xdfe2bbc3, 0|0x121] ],
[ ["00001010100110010", "0000100011110001", "111110101100100", "10110111000101", "0000100111011" ], [0|0x0a990478, 0|0xfd64b714, 0|0x13b] ],
[ ["10110101101100001", "0000110110010100", "110100011010100", "01011010010111", "0101101011011" ], [0|0xb5b086ca, 0|0x68d45a5d, 0|0x35b] ],
[ ["01010010001010110", "1110010000100111", "001010110101110", "10110111101000", "0100000010100" ], [0|0x522b7213, 0|0x95aeb7a1, 0|0x014] ],
[ ["10011001001100110", "0100100011100010", "000110011001001", "00101101101000", "0001111101100" ], [0|0x99332471, 0|0x0cc92da0, 0|0x3ec] ],
[ ["01111100000110000", "1000101001000010", "110101001111111", "01001010110110", "0111100000001" ], [0|0x7c184521, 0|0x6a7f4ad9, 0|0x701] ],
[ ["00110011010100110", "0100101101110000", "110101110100100", "00111010010110", "0110011011100" ], [0|0x335325b8, 0|0x6ba43a59, 0|0x4dc] ],
[ ["11111110011100100", "1111101001010011", "001000100110010", "01101101100001", "0001011101011" ], [0|0xfe727d29, 0|0x91326d84, 0|0x2eb] ],
[ ["00100011001001000", "1001010100000100", "010111001100111", "11011111000000", "0100111101110" ], [0|0x23244a82, 0|0x2e67df01, 0|0x1ee] ],
[ ["10111111100000010", "0111110011010001", "001000101001110", "11101010011001", "0101111101110" ], [0|0xbf813e68, 0|0x914eea65, 0|0x3ee] ],
// lengths: 17, 15, 32, 10, 20
[ ["00001111000110001", "110100011111001", "10010110001110010011011000011111", "1001101101", "01110010001111001110" ], [0|0x0f18e8f9, 0|0x9639361f, 0|0x26d723ce] ],
[ ["11011111010101100", "101010100101100", "11101101011001011011010111101011", "0010110010", "11110011110110000010" ], [0|0xdf56552c, 0|0xed65b5eb, 0|0x0b2f3d82] ],
[ ["10100100001000101", "100111001100011", "00111100101000110010000011101001", "1001110001", "11000000001111111110" ], [0|0xa422ce63, 0|0x3ca320e9, 0|0x271c03fe] ],
[ ["01110001111111010", "000101001101110", "00001100011010100100001011111001", "1011111010", "00011001110111111001" ], [0|0x71fd0a6e, 0|0x0c6a42f9, 0|0x2fa19df9] ],
[ ["01001100010011001", "000111011000100", "10100000110101110110011110001100", "0011000001", "11001101010101011100" ], [0|0x4c4c8ec4, 0|0xa0d7678c, 0|0x0c1cd55c] ],
[ ["01010010100110110", "110111110001000", "10011100001100010100000110000000", "1100111101", "00010001110100111101" ], [0|0x529b6f88, 0|0x9c314180, 0|0x33d11d3d] ],
[ ["11010100011101101", "110101011011010", "01000110000011001111101110100001", "0001001110", "01001100010010001010" ], [0|0xd476eada, 0|0x460cfba1, 0|0x04e4c48a] ],
[ ["11000001010001101", "001111011110111", "10000010100111010111011000100001", "0011000011", "11011110110000101000" ], [0|0xc1469ef7, 0|0x829d7621, 0|0x0c3dec28] ],
[ ["11000101100010100", "000110111111110", "11111011110010001100001100010000", "0000101010", "10000011010011100011" ], [0|0xc58a0dfe, 0|0xfbc8c310, 0|0x02a834e3] ],
[ ["11111111110100101", "110101000011001", "10101010010100111010100010110100", "0010011000", "10100110001000000010" ], [0|0xffd2ea19, 0|0xaa53a8b4, 0|0x098a6202] ]
];
sjcl.test.vector.bitArray.byteswap = [
[ [ 0xdab1a44e, 0x73fdc3de, 0xed6e6f00, 0x5d221b85, 0xadb987a4, 0xc20eda76, 0xa0d40d0c, 0xd1da9657, 0xd85eebf0, 0xd3f9c06d, 0xca1e39b0, 0xccd7eaeb, 0xb1dd6bd0, 0x7315a9e6, 0x77cfcac4, 0xf7914c19, 0x1bc15354, 0x935afabc, 0x3ee9d375, 0xd18a095f]
, [ 0x4ea4b1da, 0xdec3fd73, 0x006f6eed, 0x851b225d, 0xa487b9ad, 0x76da0ec2, 0x0c0dd4a0, 0x5796dad1, 0xf0eb5ed8, 0x6dc0f9d3, 0xb0391eca, 0xebead7cc, 0xd06bddb1, 0xe6a91573, 0xc4cacf77, 0x194c91f7, 0x5453c11b, 0xbcfa5a93, 0x75d3e93e, 0x5f098ad1]
]
];

View File

@@ -7,7 +7,7 @@ new sjcl.test.TestCase("ECC convenience test", function (cb) {
try {
var keys = sjcl.ecc.elGamal.generateKeys(192,0);
var ciphertext = sjcl.encrypt(keys.pub, "hello world");
var plaintext = sjcl.decrypt(keys.sec, ciphertext);

View File

@@ -5,11 +5,23 @@ new sjcl.test.TestCase("HMAC official test vectors", function (cb) {
return;
}
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out;
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out, data, mac;
for (i=0; i<kat.length; i++) {
tv = kat[i];
out = h.fromBits((new sjcl.misc.hmac(h.toBits(tv.key))).mac(h.toBits(tv.data)));
data = h.toBits(tv.data);
mac = new sjcl.misc.hmac(h.toBits(tv.key));
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac #"+i);
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
mac.update(sjcl.bitArray.bitSlice(data, 0, sjcl.bitArray.bitLength(data)/2));
mac.update(sjcl.bitArray.bitSlice(data, sjcl.bitArray.bitLength(data)/2));
out = h.fromBits(mac.digest());
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
}
cb && cb();
});

View File

@@ -1,4 +1,5 @@
/* Official HMAC test vectors. */
//Nilos: http://tools.ietf.org/html/draft-nystrom-smime-hmac-sha-02 for
sjcl.test.vector.hmac = [
{ key: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
data: "4869205468657265",

View File

@@ -0,0 +1,126 @@
/*
* Asserts that an object can be encoded to an expected string.
*/
new sjcl.test.TestCase("JSON Encode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
//Build up a standard object for encoding, this includes a nice wide variety of properties.
var obj = new Object();
obj.int = 4;
obj.nint = -5;
obj.str = 'string';
obj.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj.truth = true;
obj.lie = false;
try {
var str = sjcl.json.encode(obj);
this.require(!(!str)); //Check for non-'falsey'
}
catch (e) {
//The standard object should encode just fine, so this is out of place. Fail.
this.fail(e);
}
cb && cb();
});
/*
* Asserts that a JSON string can be decoded to an expected object.
*/
new sjcl.test.TestCase("JSON Decode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var str = ''; var i;
str = '{"int":4,"nint":-5,"str":"string","iv":"/////wAAAAAAAAABAAAAAg==","truth":true,"lie":false}';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
str = '{ "int" : 4, "nint" : -5,"str":"string", "iv": "/////wAAAAAAAAABAAAAAg==","truth": true,"lie": false }';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
//Tests passed, return.
cb && cb();
});
/*
* Asserts that an Object can be Encoded to a string that can be decoded to an equivalent object
* as well as the converse.
*/
new sjcl.test.TestCase("JSON Commutative Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var obj1 = new Object();
obj1.int = 4;
obj1.nint = -5;
obj1.str = 'string';
obj1.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj1.truth = true;
obj1.lie = false;
var str1 = '';
var str2 = '';
var obj2;
try {
str1 = sjcl.json.encode(obj1);
obj2 = sjcl.json.decode(str1);
str2 = sjcl.json.encode(obj2);
}
catch (e) {
this.fail(e);
}
try {
this.require(str1 === str2);
this.require(obj1.int == obj2.int);
this.require(obj1.str == obj2.str);
this.require(obj1.lie == obj2.lie);
this.require(obj1.nint == obj2.nint);
this.require(obj1.truth == obj2.truth);
var i;
for(i in obj1.iv)
{
this.require(obj1.iv[i] == obj2.iv[i]);
}
}
catch (e) {
this.fail(e);
}
//Tests passed.
cb && cb();
});

View File

@@ -0,0 +1,16 @@
var fs = require('fs');
var vm = require('vm');
var load = function(path) {
vm.runInThisContext(fs.readFileSync(path));
};
// Assume we're run using `make test`.
// That means argv[0] is `node` and argv[1] is this file.
process.argv.slice(2).map(load);
sjcl.test.run(undefined, function(){
if(!browserUtil.allPassed) {
process.exit(1);
}
});

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

View File

@@ -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() {
@@ -16,13 +18,123 @@ describe('Amount', function() {
assert(Amount.from_json('1').is_positive());
});
});
describe('Positives', function() {
it('Number 1', function() {
assert(Amount.from_json('1').is_positive());
});
});
// also tested extensively in other cases
describe('to_human', function() {
it('12345.6789 XAU', function() {
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_human(), '12,345.6789');
});
it('12345.678901234 XAU', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human(), '12,345.678901234');
});
it('to human, precision -1, should be ignored, precision needs to be >= 0', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:-1}), '12,346');
});
it('to human, precision 0', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:0}), '12,346');
});
it('to human, precision 1', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:1}), '12,345.7');
});
it('to human, precision 2', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:2}), '12,345.68');
});
it('to human, precision 3', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:3}), '12,345.679');
});
it('to human, precision 4', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:4}), '12,345.6789');
});
it('to human, precision 5', function() {
assert.strictEqual(Amount.from_human("12345.678901234 XAU").to_human({precision:5}), '12,345.67890');
});
it('to human, precision -1, should be ignored, precision needs to be >= 0', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:-1}), '0');
});
it('to human, precision 0', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:0}), '0');
});
it('to human, precision 1', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:1}), '0.0');
});
it('to human, precision 2', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:2}), '0.00');
});
it('to human, precision 5', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:5}), '0.00012');
});
it('to human, precision 6', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:6}), '0.000123');
});
it('to human, precision 16', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16}), '0.00012345');
});
it('to human, precision 16, min_precision 16', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16, min_precision:16}), '0.0001234500000000');
});
it('to human, precision 16, min_precision 12', function() {
assert.strictEqual(Amount.from_human("0.00012345 XAU").to_human({precision:16, min_precision:12}), '0.000123450000');
});
it('to human, precision 0, first decimal 4', function() {
assert.strictEqual(Amount.from_human("0.4 XAU").to_human({precision:0}), '0');
});
it('to human, precision 0, first decimal 5', function() {
assert.strictEqual(Amount.from_human("0.5 XAU").to_human({precision:0}), '1');
});
it('to human, precision 0, first decimal 8', function() {
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
});
it('to human, precision 0, precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:16}), '0');
});
it('to human, precision 0, precision 8, min_precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:8, min_precision:16}), '0.0000000000000000');
});
it('to human, precision 0, first decimal 8', function() {
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
});
it('to human, precision 6, min_precision 6, max_sig_digits 20', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision: 6, min_precision: 6, max_sig_digits: 20}), '0.000000');
});
it('to human, precision 16, min_precision 6, max_sig_digits 20', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision: 16, min_precision: 6, max_sig_digits: 20}), '0.000000');
});
it('to human rounding edge case, precision 2, 1', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:1}), '1.0');
});
it('to human rounding edge case, precision 2, 2', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:2}), '0.99');
});
it('to human rounding edge case, precision 2, 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3}), '0.99');
});
it('to human rounding edge case, precision 2, 3 min precision 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3, min_precision:3}), '0.990');
});
it('to human rounding edge case, precision 3, 2', function() {
assert.strictEqual(Amount.from_human("0.999 XAU").to_human({precision:2}), '1.00');
});
});
describe('from_human', function() {
it('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');
});
@@ -177,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());
@@ -227,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');
@@ -869,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());
@@ -941,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');
});
});
@@ -976,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');
});
@@ -1076,4 +1190,77 @@ describe('Amount', function() {
assert.strictEqual(demAmount.to_human_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
describe('amount limits', function() {
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_max.toString(), '100000000000000000');
});
it ('max JSON wire limite', function() {
assert.strictEqual(Amount.bi_xns_min.toString(), '-100000000000000000');
});
it('max mantissa value', function() {
assert.strictEqual(Amount.bi_man_max_value.toString(), '9999999999999999');
});
it('min mantissa value', function() {
assert.strictEqual(Amount.bi_man_min_value.toString(), '1000000000000000');
});
it ('from_json minimum XRP', function() {
var amt = Amount.from_json('-100000000000000000');
assert.strictEqual(amt.to_json(), '-100000000000000000');
});
it ('from_json maximum XRP', function() {
var amt = Amount.from_json('100000000000000000');
assert.strictEqual(amt.to_json(), '100000000000000000');
});
it ('from_json less than minimum XRP', function() {
assert.throws(function() {
Amount.from_json('-100000000000000001');
});
});
it ('from_json more than maximum XRP', function() {
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.to_text(), '-1000000000000000e-96');
assert.strictEqual(amt.to_text(), Amount.min_value);
});
it('from_json exceed minimum IOU', function() {
assert.throws(function() {
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
}, 'Exceeding min value of ' + Amount.min_value);
});
it ('from_json maximum IOU', function() {
var amt = Amount.from_json('9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt.to_text(), '9999999999999999e80');
});
it ('from_json exceed maximum IOU', function() {
assert.throws(function() {
Amount.from_json('9999999999999999e81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
}, 'Exceeding max value of ' + Amount.max_value);
});
it ('from_json normalize mantissa to valid max range, lost significant digits', function() {
var amt = Amount.from_json('99999999999999999999999999999999/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(amt.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.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,6 +1,6 @@
var assert = require('assert');
var utils = require('./testutils');
var currency = utils.load_module('currency').Currency;
var currency = require('ripple-lib').Currency;
var timeUtil = require('ripple-lib').utils.time;
describe('Currency', function() {
describe('json_rewrite', function() {
@@ -16,11 +16,22 @@ describe('Currency', function() {
});
});
describe('from_json', function() {
it('from_json().to_json() == "XRP"', function() {
var r = currency.from_json();
assert(!r.is_valid());
assert.strictEqual('XRP', r.to_json());
});
it('from_json(NaN).to_json() == "XRP"', function() {
var r = currency.from_json(NaN);
assert(!r.is_valid());
assert.strictEqual('XRP', r.to_json());
});
it('from_json().to_json("") == "XRP"', function() {
var r = currency.from_json('');
assert(r.is_valid());
assert(r.is_native());
assert.strictEqual('XRP', r.to_json());
});
it('from_json("XRP").to_json() == "XRP"', function() {
var r = currency.from_json('XRP');
assert(r.is_valid());
@@ -82,6 +93,9 @@ describe('Currency', function() {
var cur = currency.from_human('EUR (0.5361%pa)');
assert.strictEqual(cur.to_json(), 'EUR (0.54%pa)');
assert.strictEqual(cur.to_json({decimals:4, full_name:'Euro'}), 'EUR - Euro (0.5361%pa)');
assert.strictEqual(cur.to_json({decimals:void(0), full_name:'Euro'}), 'EUR - Euro (0.54%pa)');
assert.strictEqual(cur.to_json({decimals:undefined, full_name:'Euro'}), 'EUR - Euro (0.54%pa)');
assert.strictEqual(cur.to_json({decimals:'henk', full_name:'Euro'}), 'EUR - Euro (0.54%pa)');
assert.strictEqual(cur.get_interest_percentage_at(undefined, 4), 0.5361);
});
it('From human "TYX - 30-Year Treasuries (1.5%pa)"', function() {
@@ -100,6 +114,16 @@ describe('Currency', function() {
var cur = currency.from_human('INR - 30 Indian Rupees');
assert.strictEqual(cur.to_json(), 'INR');
});
it('From human "XRP"', function() {
var cur = currency.from_human('XRP');
assert.strictEqual(cur.to_json(), 'XRP');
assert(cur.is_native(), true);
});
it('From human "XRP - Ripples"', function() {
var cur = currency.from_human('XRP - Ripples');
assert.strictEqual(cur.to_json(), 'XRP');
assert(cur.is_native(), true);
});
});
@@ -111,8 +135,28 @@ describe('Currency', function() {
assert.strictEqual('XRP', currency.from_json(NaN).to_human());
});
it('"015841551A748AD2C1F76FF6ECB0CCCD00000000") == "015841551A748AD2C1F76FF6ECB0CCCD00000000"', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human(),
'XAU (-0.5%pa)');
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human(), 'XAU (-0.5%pa)');
});
it('"015841551A748AD2C1F76FF6ECB0CCCD00000000") == "015841551A748AD2C1F76FF6ECB0CCCD00000000"', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human({full_name:'Gold'}), 'XAU - Gold (-0.5%pa)');
});
it('to_human interest XAU with full name, do not show interest', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human({full_name:'Gold', show_interest:false}), 'XAU - Gold');
});
it('to_human interest XAU with full name, show interest', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human({full_name:'Gold', show_interest:true}), 'XAU - Gold (-0.5%pa)');
});
it('to_human interest XAU, do show interest', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human({show_interest:true}), 'XAU (-0.5%pa)');
});
it('to_human interest XAU, do not show interest', function() {
assert.strictEqual(currency.from_json("015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human({show_interest:false}), 'XAU');
});
it('to_human with full_name "USD - US Dollar show interest"', function() {
assert.strictEqual(currency.from_json('USD').to_human({full_name:'US Dollar', show_interest:true}), 'USD - US Dollar (0%pa)');
});
it('to_human with full_name "USD - US Dollar do not show interest"', function() {
assert.strictEqual(currency.from_json('USD').to_human({full_name:'US Dollar', show_interest:false}), 'USD - US Dollar');
});
it('to_human with full_name "USD - US Dollar"', function() {
assert.strictEqual('USD - US Dollar', currency.from_json('USD').to_human({full_name:'US Dollar'}));
@@ -128,6 +172,7 @@ describe('Currency', function() {
var cur = currency.from_json("TIM");
assert.strictEqual(cur.to_human({full_name: null}), "TIM");
});
});
describe('from_hex', function() {
@@ -138,10 +183,30 @@ describe('Currency', function() {
assert.strictEqual(cur.to_json(), cur.to_human());
});
});
describe('parse_json(currency obj)', function() {
assert.strictEqual('USD', new currency().parse_json(currency.from_json('USD')).to_json());
describe('parse_json', function() {
it('should parse a currency object', function() {
assert.strictEqual('USD', new currency().parse_json(currency.from_json('USD')).to_json());
assert.strictEqual('USD (0.5%pa)', new currency().parse_json(currency.from_json('USD (0.5%pa)')).to_json());
});
it('should clone for parse_json on itself', function() {
var cur = currency.from_json('USD');
var cur2 = currency.from_json(cur);
assert.strictEqual(cur.to_json(), cur2.to_json());
assert.strictEqual('USD (0.5%pa)', new currency().parse_json(currency.from_json('USD (0.5%pa)')).to_json());
cur = currency.from_hex('015841551A748AD2C1F76FF6ECB0CCCD00000000');
cur2 = currency.from_json(cur);
assert.strictEqual(cur.to_json(), cur2.to_json());
});
it('should parse json 0', function() {
var cur = currency.from_json(0);
assert.strictEqual(cur.to_json(), 'XRP');
assert.strictEqual(cur.get_iso(), 'XRP');
});
it('should parse json 0', function() {
var cur = currency.from_json('0');
assert.strictEqual(cur.to_json(), 'XRP');
assert.strictEqual(cur.get_iso(), 'XRP');
});
});
describe('is_valid', function() {
@@ -183,14 +248,16 @@ describe('Currency', function() {
return +(Math.round(num + "e+"+precision) + "e-"+precision);
}
describe('get_interest_at', function() {
it('returns demurred value for demurrage currency', function() {
it('should return demurred value for demurrage currency', function() {
var cur = currency.from_json('015841551A748AD2C1F76FF6ECB0CCCD00000000');
// At start, no demurrage should occur
assert.equal(1, cur.get_interest_at(443845330));
assert.equal(1, precision(cur.get_interest_at(new Date(timeUtil.fromRipple(443845330))), 14));
// After one year, 0.5% should have occurred
assert.equal(0.995, precision(cur.get_interest_at(443845330 + 31536000), 14));
assert.equal(0.995, precision(cur.get_interest_at(new Date(timeUtil.fromRipple(443845330 + 31536000))), 14));
// After one demurrage period, 1/e should have occurred
assert.equal(1/Math.E, cur.get_interest_at(443845330 + 6291418827.05));
@@ -201,6 +268,11 @@ describe('Currency', function() {
// One demurrage period before start, rate should be e
assert.equal(Math.E, cur.get_interest_at(443845330 - 6291418827.05));
});
it('should return 0 for currency without interest', function() {
var cur = currency.from_json('USD - US Dollar');
assert.equal(0, cur.get_interest_at(443845330));
assert.equal(0, cur.get_interest_at(443845330 + 31536000));
});
});
describe('get_iso', function() {
it('should get "XRP" iso_code', function() {

View File

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

Some files were not shown because too many files have changed in this diff Show More