Compare commits

...

168 Commits

Author SHA1 Message Date
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
Geert Weening
5d8bb541c6 [TASK] bump version to 0.8.2-rc1 2014-09-17 15:02:44 -07:00
Geert Weening
b51c59b23a [TASK] update release notes 2014-09-17 15:02:21 -07:00
wltsmrz
2cd434e861 Fix orderbook reset on reconnect 2014-09-17 13:42:24 -07:00
wltsmrz
1599eb9629 Add potential missing error handlers 2014-09-17 13:40:49 -07:00
wltsmrz
8ef7481858 Allow mixed letters and numbers in currencies 2014-09-17 13:39:49 -07:00
Geert Weening
344d478b3f [TASK] bump version to 0.8.1 2014-09-15 16:52:23 -07:00
Geert Weening
39b7e27aa6 [TASK] bump version to 0.8.1-rc3 2014-09-12 14:26:25 -07:00
Geert Weening
b1876b4f77 [DOC] update release notes 2014-09-12 14:26:02 -07:00
wltsmrz
db3b41d1ba Merge pull request #167 from ximinez/ripd-549
[BUG] Fix Amount.to_human_full()
2014-09-12 12:16:03 -07:00
Edward Hennis
02b5d14d0f [BUG] Fix Amount.to_human_full()
* Amount.is_native is a function, but was referenced as a bool.
* Duplicate to_text_full tests to test to_human_full.
2014-09-12 10:52:46 -04:00
wltsmrz
0120044c96 Fix undefined fee states when connecting to a rippled that is syncing 2014-09-11 23:44:21 -07:00
wltsmrz
ad6304e857 Merge pull request #166 from geertweening/develop
[DOC] update README install instructions
2014-09-10 18:54:45 -07:00
Geert Weening
7cba84b8cf [DOC] update README install instructions 2014-09-10 18:51:59 -07:00
Geert Weening
5a9a4be163 [TASK] bump version to 0.8.1-rc2 2014-09-10 17:54:11 -07:00
Geert Weening
4d1a31d3c9 Revert "[FOLD] Add postinstall step to build the sjcl.js"
This reverts commit 42c853dbf4.
2014-09-10 17:53:29 -07:00
Geert Weening
6e3ceec4e5 [TASK] update publish scripts 2014-09-10 17:33:52 -07:00
Geert Weening
bc7d3c0af8 [TASK] add release candidate publish script 2014-09-10 17:32:03 -07:00
Geert Weening
519ddee092 [TASK] bump version to 0.8.1-rc1 2014-09-10 17:28:34 -07:00
Geert Weening
3e0fcc5b8b [DOC] update release notes 2014-09-10 17:28:00 -07:00
wltsmrz
b1972985c4 Merge pull request #164 from geertweening/develop
[FIX] return null for _getServer if there's no connected server
2014-09-08 12:04:22 -07:00
Geert Weening
51c42e9257 [FIX] return null for _getServer if there's no connected server 2014-09-08 11:54:48 -07:00
Geert Weening
86dcbcc671 [TASK] add exit_on_error calls to publish script 2014-09-08 11:54:47 -07:00
wltsmrz
3b7cd9d84f Merge pull request #163 from stevenzeiler/wallet
[TASK] Update package.json for ripple-wallet-generator patch.
2014-09-06 02:18:08 -07:00
Steven Zeiler
1073ec6214 [TASK] Update package.json for ripple-wallet-generator patch.
Removed node_modules from git repo of ripple-wallet-generator
2014-09-05 21:19:09 -07:00
wltsmrz
14a5e42a63 Merge pull request #162 from sublimator/develop
Fix #160, Seed.parse_json of hex
2014-09-05 20:56:53 -07:00
Nicholas Dudfield
b4564a86b4 Fix #160, Seed.parse_json of hex 2014-09-06 10:45:26 +07:00
wltsmrz
03386a61e9 Merge pull request #161 from stevenzeiler/wallet
[FEATURE] Add Wallet class that generates wallets.
2014-09-05 18:22:55 -07:00
Steven Zeiler
8bb2623360 [FEATURE] Add Wallet class that generates wallets.
[DOC] Comment out logged wallet.

[FIX] Use var instead of const.
2014-09-05 18:20:46 -07:00
wltsmrz
ab0e4188b3 Merge pull request #159 from ximinez/ripd-549
Clean up
2014-09-05 13:03:48 -07:00
Edward Hennis
42c853dbf4 [FOLD] Add postinstall step to build the sjcl.js 2014-09-05 14:50:29 -04:00
Geert Weening
ce48a1793b Merge branch 'release' into develop 2014-09-05 10:24:05 -07:00
Geert Weening
6177543d98 [TASK] add publish script 2014-09-05 10:23:59 -07:00
Geert Weening
9697bfa817 [TASK] add publish to bower script 2014-09-05 10:23:59 -07:00
Geert Weening
70425ab5c8 [TASK] bump version to 0.8.0 2014-09-05 10:19:58 -07:00
wltsmrz
7cccb451d2 Show field name in serialization failure 2014-09-04 20:13:04 -07:00
wltsmrz
a39fb9d551 Minor cleanup 2014-09-04 19:53:16 -07:00
wltsmrz
8f7cdc6e4f Prevent setting LastLedgerSequence of NaN. Fixes a potential ambiguous serialization error 2014-09-04 19:50:56 -07:00
wltsmrz
8f7e365b03 Wait until remote is connected before emitting ledger_closed events 2014-09-04 18:51:00 -07:00
Edward Hennis
64735e523f Clean up
* Make npm test runnable in Windows.
* Fix paths in README.md
* Ignore all build output files
2014-09-04 20:05:04 -04:00
Geert Weening
f126610219 [TASK] add publish to bower script 2014-09-04 16:11:59 -07:00
Geert Weening
2caef539ce [TASK] bump version to 0.8.0-rc3 2014-09-04 15:43:01 -07:00
Geert Weening
468fb87749 [DOC] update release notes 2014-09-04 15:42:36 -07:00
Geert Weening
4f4808ff15 Merge pull request #158 from justmoon/amount_constants
Amount: Constants should be static fields on the class, not a separate export.
2014-09-04 15:30:12 -07:00
Stefan Thomas
e6bbca7df1 [TASK] Amount: Constants should be static fields on the class, not a separate export. 2014-09-04 15:12:14 -07:00
Geert Weening
e7d1095be2 Merge pull request #157 from geertweening/release
Prepare 0.8.0-rc2 release
2014-09-04 10:16:14 -07:00
Geert Weening
a08d5ce6e5 [TASK] bump version to 0.8.0-rc2 2014-09-04 10:15:47 -07:00
Geert Weening
fec2f5578d [DOC] update release notes 2014-09-04 10:14:45 -07:00
Geert Weening
4869e30914 Merge branch 'release' into develop 2014-09-04 10:10:42 -07:00
Geert Weening
e1f31765e7 Merge pull request #156 from justmoon/max_value
Add a constant for the maximum number representable as a Ripple value
2014-09-04 09:45:38 -07:00
Stefan Thomas
a3668defa8 [TASK] Add a constant for the maximum number representable as a Ripple value. 2014-09-04 09:27:36 -07:00
wltsmrz
765ff9fa32 Merge pull request #155 from ripple/develop
Update documentation, fix owner_funds not updated in orderbook
2014-08-27 13:49:27 -07:00
wltsmrz
dd04177f83 Update owner_funds 2014-08-27 13:05:54 -07:00
wltsmrz
2e2ab6bffc Update docs 2014-08-26 13:41:27 -07:00
wltsmrz
934cacfc1b Merge pull request #154 from justmoon/new_seed_test
Improve Seed#get_key test cases.
2014-08-25 15:49:58 -07:00
Stefan Thomas
9800fd8f11 [TEST] Previous test cases were using the functionality being tested.
The previous version of these tests was using `get_address` just like the code being tested. It's better to check if the code in question has actually been able to correctly find the public key (which means it also found the correct private key.)
2014-08-25 15:25:07 -07:00
wltsmrz
3e84996788 Merge pull request #153 from ripple/develop
Unfunded orderbook update, add option for disallowing partial server history
2014-08-25 13:27:30 -07:00
wltsmrz
5a3f55d774 Merge pull request #152 from shekenahglory/develop
[CHORE] vault client: get 2fa HMAC instead of ECDSA
2014-08-25 13:11:18 -07:00
Matthew Fettig
dbddc314a6 [CHORE] vault client: get 2fa HMAC instead of ECDSA 2014-08-25 10:45:43 -07:00
wltsmrz
c98f875811 Merge pull request #151 from ripple/incomplete-server-history
Add checks for partial server history
2014-08-22 14:06:29 -07:00
wltsmrz
29a1ffb3b8 Merge pull request #150 from ripple/unfunded-orders
Fix set funded amount with unfunded native currency
2014-08-22 13:25:29 -07:00
wltsmrz
17770ad4c9 Fix set funded amount with unfunded native currency 2014-08-22 13:23:05 -07:00
wltsmrz
cc9ed435eb Add checks for partial server history 2014-08-21 17:21:17 -07:00
wltsmrz
27a723b453 Update readme 2014-08-21 16:05:00 -07:00
wltsmrz
af6c9b6bd2 Merge pull request #147 from ripple/develop
Update unfunded order tracking - fix native/non-native currency discrepancies
2014-08-21 15:08:08 -07:00
wltsmrz
2d3bbecb05 Merge pull request #148 from ripple/unfunded-orders
Fix setting funded amount
2014-08-21 14:32:52 -07:00
wltsmrz
51e4cb15b4 Fix setting funded amount 2014-08-21 14:18:55 -07:00
wltsmrz
5ce91a027c Merge pull request #146 from ripple/unfunded-orders
Update unfunded order tracking - fix native/non-native currency discrepancies
2014-08-20 16:00:08 -07:00
wltsmrz
3cb337e7ec Uncomment tests 2014-08-20 15:53:03 -07:00
wltsmrz
c29f92f05b Fix native/non-native currency discrepancies 2014-08-20 15:50:20 -07:00
wltsmrz
01903cc6d2 Update unfunded order tracking 2014-08-20 14:33:00 -07:00
Geert Weening
fff7a6bc9e Merge pull request #127 from orzFly/patch-1
[FIX] Cannot use demmurage currencies in path_find
2014-08-20 19:29:58 +02:00
Yeechan Lu
678c67622d [FIX] Cannot use demmurage currencies in path_find
Use hex format instead of json for currencies.
2014-08-21 00:32:56 +08:00
wltsmrz
2a6aec94fb Merge pull request #145 from ripple/develop
Update unfunded order tracking
2014-08-19 15:19:10 -07:00
wltsmrz
bc52f33e9c Merge pull request #144 from ripple/unfunded-orders
Convert to IOU format before comparing amounts during updateOfferFunds
2014-08-19 15:18:31 -07:00
wltsmrz
006beeb5f9 Convert to IOU format before comparing amounts during updateOfferFunds 2014-08-19 15:17:21 -07:00
wltsmrz
ff85b3c4c9 Merge pull request #143 from ripple/develop
Update unfunded order tracking
2014-08-19 14:40:41 -07:00
wltsmrz
6c7b2b17dc Merge pull request #142 from ripple/unfunded-orders
Unfunded orders
2014-08-19 14:36:00 -07:00
wltsmrz
131de6661c Update orderbook test 2014-08-19 14:28:26 -07:00
wltsmrz
d416f31801 Fix total account funds being emitted on offer_funds_changed rather than the amount set as taker_gets_funded 2014-08-19 14:28:11 -07:00
wltsmrz
8885a9e3e5 Merge pull request #141 from ripple/develop
Update unfunded order tracking
2014-08-18 18:16:53 -07:00
wltsmrz
27e100f4ee Merge pull request #140 from ripple/unfunded-orders
Unfunded orders
2014-08-18 18:11:51 -07:00
wltsmrz
40dc49bd63 Check listeners length before removeAllListeners, fixing possible WebPack EventEmitter bug 2014-08-18 17:21:08 -07:00
wltsmrz
989509dc07 Revert unfunded offer filtering 2014-08-18 15:34:26 -07:00
wltsmrz
9c3f5fbcd2 Ignore offers with TakerGets of 0 2014-08-18 15:15:16 -07:00
wltsmrz
0917f66cb2 Update orderbook test 2014-08-18 12:37:32 -07:00
wltsmrz
66c56df7dc Fix requestOffers not returning an EventEmitter 2014-08-18 12:22:41 -07:00
wltsmrz
b5fdfa2604 Merge pull request #139 from ripple/develop
Update unfunded order tracking
2014-08-18 11:40:32 -07:00
wltsmrz
a0d4a3c84d Merge pull request #138 from ripple/unfunded-orders
Adjust taker_pays_funded
2014-08-18 11:20:13 -07:00
wltsmrz
d8374b2f49 Improve test coverage 2014-08-16 02:46:55 -07:00
wltsmrz
a2a2162f48 Change how TransferRate is requested 2014-08-16 02:46:32 -07:00
wltsmrz
d845d094db Merge branch 'develop' into unfunded-orders 2014-08-16 02:45:35 -07:00
wltsmrz
81e805fcb9 Fix typo in invalid MemoData error 2014-08-15 20:18:47 -07:00
wltsmrz
81283eeb84 Adjust taker_pays_funded 2014-08-15 20:05:57 -07:00
wltsmrz
60069d0a28 Merge pull request #137 from shekenahglory/develop
[FIX] crypt: add entropy to random words request
2014-08-15 15:51:31 -07:00
Matthew Fettig
7c0561d17f [FIX] crypt: add entropy to random words request 2014-08-15 14:52:11 -07:00
wltsmrz
45ac10b215 Merge pull request #136 from ripple/memos
Add Memo setter
2014-08-14 15:54:46 -07:00
wltsmrz
47f5943cf7 Reverse order of arguments in JSDoc 2014-08-14 13:30:00 -07:00
wltsmrz
73d30242c9 Add Memo setter 2014-08-14 13:26:52 -07:00
wltsmrz
5a4e33a02d Merge pull request #130 from shekenahglory/develop
[FEATURE] blob: force send phone verification token via SMS
2014-08-14 12:04:32 -07:00
Matthew Fettig
edbbbec8f3 [FEATURE] blob: force send phone verification token via SMS 2014-08-13 17:13:54 -07:00
41 changed files with 2927 additions and 5390 deletions

4
.gitignore vendored
View File

@@ -17,7 +17,7 @@
# Ignore object files.
*.o
build/ripple*.js
build/*.js
tags
bin/rippled
Debug/*.*
@@ -51,4 +51,4 @@ test/config.js
npm-debug.log
# Ignore dist folder, build for bower
dist/
dist/

View File

@@ -1,9 +1,73 @@
##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
##0.8.1
+ Wallet: Add Wallet class that generates wallets
+ Make npm test runnable in Windows.
+ Fix several stability issues, see merged PR's for details
+ Fix bug in Amount.to_human_full()
+ Fix undefined fee states when connecting to a rippled that is syncing
##0.8.0
+ Orderbook: Added tracking of offer funds for determining when offers are not funded
+ Orderbook: Added tests
+ Orderbook: Update owner funds
+ Transactions: If transaction errs with `tefALREADY`, wait until all possible submissions err with the same before emitting `error`. Fixes a client "Transaction malformed" bug.
+ Transactions: Track submissions, don't bother submitting to unconnected servers
@@ -14,6 +78,11 @@
+ Server: Acquire host information from server without additional request
+ Amount: Add a constant for the maximum canonical value that can be expressed as a Ripple value
+ Amount: Make Constants static fields on the class, instead of a seperate export
##0.7.39
+ Improvements to multi-server support. Fixed an issue where a server's score was not reset and connections would keep dropping after being connected for a significant amount of time.

View File

@@ -1,37 +1,36 @@
#The Ripple JavaScript Library
#ripple-lib
JavaScript client for [rippled](https://github.com/ripple/rippled)
[![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)
[![NPM](https://nodei.co/npm/ripple-lib.png)](https://www.npmjs.org/package/ripple-lib)
`ripple-lib` connects to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser.
###Features
###Use ripple-lib for:
+ Connect to a rippled server in JavaScript (Node.js or browser)
+ Issue [rippled API](https://ripple.com/wiki/JSON_Messages) requests
+ Listen to events on the Ripple network (transaction, ledger, etc.)
+ Sign and submit transactions to the Ripple network
+ Connecting to a local or remote rippled in JavaScript (Node.js or browser)
+ Issuing [rippled API](https://ripple.com/wiki/JSON_Messages) requests
+ Listening to events on the Ripple network (transaction, ledger, etc.)
+ Signing and submitting transactions to the Ripple network
###In this file
###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. Overview
2. [Getting `ripple-lib`](README.md#getting-ripple-lib)
3. [Quickstart](README.md#quickstart)
4. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
###Additional documentation
###For additional documentation see:
1. [Guides](docs/GUIDES.md)
2. [API Reference](docs/REFERENCE.md)
3. [Wiki](https://ripple.com/wiki/Ripple_JavaScript_library)
1. [The `ripple-lib` Guides (docs/GUIDES.md)](docs/GUIDES.md)
2. [The `ripple-lib` API Reference (docs/REFERENCE.md)](docs/REFERENCE.md)
3. https://ripple.com/wiki/Ripple_JavaScript_library
###Also see
###Also see:
+ [The Ripple wiki](https://ripple.com/wiki)
+ [ripple.com](https://ripple.com)
+ https://ripple.com/wiki
+ https://ripple.com
##Getting `ripple-lib`
##Installation
**Via npm for Node.js**
@@ -39,7 +38,16 @@
$ npm install ripple-lib
```
**Building ripple-lib for browser client**
**Via bower (for browser use)**
```
$ bower install ripple
```
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions
**Building ripple-lib from github**
```
$ git clone https://github.com/ripple/ripple-lib
@@ -47,11 +55,11 @@
$ npm run build
```
Then use the minified `build/ripple-*-min.js` in your webpage
Then use the minified `build/ripple-*-min.js`
##Quickstart
`Remote` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the module responsible for managing connections to `rippled` servers:
`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
```js
/* Loading ripple-lib with Node.js */
@@ -62,35 +70,24 @@ var Remote = require('ripple-lib').Remote;
var remote = new Remote({
// see the API Reference for available options
trusted: true,
local_signing: true,
local_fee: true,
fee_cushion: 1.5,
servers: [
{
host: 's1.ripple.com'
, port: 443
, secure: true
}
]
servers: [ 'wss://s1.ripple.com:443' ]
});
remote.connect(function() {
/* remote connected */
remote.request('server_info', function(err, info) {
// see the API Reference for available functions
});
});
```
See [The `ripple-lib` Guides](docs/GUIDES.md) and [The `ripple-lib` API Reference](docs/REFERENCE.md) for walkthroughs and details about all of the available functions and options.
##Running tests
1. Clone the repository
2. `cd` into the repository and install dependencies with `npm install`
3. `npm test` or `make test` or `node_modules\.bin\mocha test\*-test.js`
3. `npm test` or `node_modules/.bin/mocha test/*-test.js`
**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
})
})+'\''
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,36 @@
#`ripple-lib` Guides
#Guides
This file provides step-by-step walkthroughs for some of the most common usages of `ripple-lib`.
###Guides in this document:
###In this document
1. [Connecting to the Ripple network with `Remote`](GUIDES.md#1-connecting-to-the-ripple-network-with-remote)
2. [Using `Remote` functions and `Request` objects](GUIDES.md#2-using-remote-functions-and-request-objects)
3. [Submitting a payment to the network](GUIDES.md#3-submitting-a-payment-to-the-network)
1. [Connecting to the Ripple network with `Remote`](GUIDES.md#connecting-to-the-ripple-network)
2. [Using `Remote` functions and `Request` objects](GUIDES.md#sending-rippled-API-requests)
3. [Listening to the network](GUIDES.md#listening-to-the-network)
4. [Submitting a payment to the network](GUIDES.md#submitting-a-payment-to-the-network)
* [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees)
4. [Submitting a trade offer to the network](GUIDES.md#4-submitting-a-trade-offer-to-the-network)
5. [Listening to the network](GUIDES.md#5-listening-to-the-network)
5. [Submitting a trade offer to the network](GUIDES.md#submitting-a-trade-offer-to-the-network)
###Also see
###Also see:
1. [The ripple-lib README](../README.md)
2. [The ripple-lib API Reference](REFERENCE.md)
1. [The `ripple-lib` README](../README.md)
2. [The `ripple-lib` API Reference](REFERENCE.md)
##Generating a new Ripple Wallet
##1. Connecting to the Ripple network with `Remote`
```js
var Wallet = require('ripple-lib').Wallet;
1. [Get `ripple-lib`](README.md#getting-ripple-lib)
2. Load the `ripple-lib` module into a Node.js file or webpage:
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)
2. Load the ripple-lib module into a Node.js file or webpage:
```js
/* Loading ripple-lib with Node.js */
var Remote = require('ripple-lib').Remote;
@@ -37,32 +47,36 @@ This file provides step-by-step walkthroughs for some of the most common usages
});
```
__NOTE:__ See the API Reference for available [`Remote` options](REFERENCE.md#1-remote-options)
4. You're connected! Read on to see what to do now.
##2. Using `Remote` functions and `Request` objects
##Sending rippled API requests
All `Remote` functions return 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 to the `Remote` function.
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()`, of how `Remote` functions can be used with event listeners (the first code block) or with a callback (the second block):
Here is an example, using [request_server_info](https://ripple.com/wiki/JSON_Messages#server_info).
+ Using a `Remote` function with `Request` event listeners:
+ Constructing a `Request` with event listeners
```js
var request = remote.request_server_info();
request.on('success', function(res) {
var request = remote.request('server_info');
request.on('success', function onSuccess(res) {
//handle success
});
request.on('error', function(err) {
request.on('error', function onError(err) {
//handle error
});
request.request(); // this triggers the request if it has not already been sent to the server
request.request();
```
+ Using a `Remote` function with a callback:
+ Using a callback:
```js
remote.request_server_info(function(err, res) {
remote.request('server_info', function(err, res) {
if (err) {
//handle error
} else {
@@ -74,9 +88,44 @@ remote.request_server_info(function(err, res) {
__NOTE:__ See the API Reference for available [`Remote` functions](REFERENCE.md#2-remote-functions)
##Listening to the network
See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on subscription requests.
##3. Submitting a payment to the network
```js
/* Loading ripple-lib with Node.js */
var Remote = require('ripple-lib').Remote;
/* Loading ripple-lib in a webpage */
// var Remote = ripple.Remote;
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
});
request.on('transaction', function onTransacstion(transaction) {
//handle transaction
});
request.request(function(err) {
if (err) {
} else {
}
});
});
```
* https://ripple.com/wiki/RPC_API#transactions_stream_messages
* https://ripple.com/wiki/RPC_API#ledger_stream_messages
##Submitting a payment to the network
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.
@@ -97,13 +146,11 @@ var AMOUNT = Amount.from_human('1XRP');
var remote = new Remote({ /* Remote options */ });
remote.connect(function() {
remote.set_secret(MY_ADDRESS, MY_SECRET);
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.transaction();
transaction.payment({
from: MY_ADDRESS,
to: RECIPIENT,
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: RECIPIENT,
amount: AMOUNT
});
@@ -139,27 +186,17 @@ 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 */ });
remote.connect(function() {
remote.set_secret(MY_ADDRESS, MY_SECRET);
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.transaction();
transaction.offer_create({
from: MY_ADDRESS,
buy: BUY_AMOUNT,
sell: SELL_AMOUNT,
expiration: EXPIRATION
var transaction = remote.createTransaction('OfferCreate', {
account: MY_ADDRESS,
taker_pays: '1',
taker_gets: '1/USD/' + GATEWAY
});
transaction.submit(function(err, res) {
@@ -167,35 +204,3 @@ remote.connect(function() {
});
});
```
##5. Listening to the network
In some (relatively rare) cases you may want to subscribe to the network event feed and listen for transactions and the ledger closings. [Ripple.com](http://www.ripple.com) uses this feature of `ripple-lib` to display the live feed on the top of each page and the ledger closing visualization on the [Developers page](http://ripple.com/devs).
```js
/* Loading ripple-lib with Node.js */
var Remote = require('ripple-lib').Remote;
/* Loading ripple-lib in a webpage */
// var Remote = ripple.Remote;
var remote = new Remote({options});
remote.connect(function() {
remote.on('transaction_all', transactionListener);
remote.on('ledger_closed', ledgerListener);
});
function transactionListener (transaction_data) {
// handle transaction_data
// see https://ripple.com/wiki/RPC_API#transactions_stream_messages for the format of transaction_data
}
function ledgerListener (ledger_data) {
// handle ledger_data
// see https://ripple.com/wiki/RPC_API#ledger_stream_messages for the format of ledger_data
}
```
* https://ripple.com/wiki/RPC_API#transactions_stream_messages
* https://ripple.com/wiki/RPC_API#ledger_stream_messages

View File

@@ -1,28 +1,26 @@
#`ripple-lib` API Reference
#API Reference
__(More examples coming soon!)__
###In this document:
1. [`Remote` options](REFERENCE.md#1-remote-options)
2. [`Remote` functions](REFERENCE.md#2-remote-functions)
+ [Server info functions](REFERENCE.md#server-info-functions)
+ [Ledger query functions](REFERENCE.md#ledger-query-functions)
+ [Transaction query functions](REFERENCE.md#transaction-query-functions)
+ [Account query functions](REFERENCE.md#account-query-functions)
+ [Order book query functions](REFERENCE.md#order-book-query-functions)
+ [Transaction submission functions](REFERENCE.md#transaction-submission-functions)
3. [`Transaction` events](REFERENCE.md#3-transaction-events)
4. [`Amount` objects](REFERENCE.md#4-amount-objects)
1. [`Remote` options](REFERENCE.md#remote-options)
2. [`Request` constructors](REFERENCE.md#request-constructor-functions)
+ [Server requests](REFERENCE.md#server-requests)
+ [Ledger requests](REFERENCE.md#ledger-requests)
+ [Transaction requests](REFERENCE.md#transaction-requests)
+ [Account requests](REFERENCE.md#account-requests)
+ [Orderbook requests](REFERENCE.md#orderbook-requests)
+ [Transaction requests](REFERENCE.md#transaction-requests)
3. [`Transaction` constructors](REFERENCE.md#transaction-constructors)
+ [Transaction events](REFERENCE.md#transaction-events)
###Also see:
1. [The `ripple-lib` README](../README.md)
2. [The `ripple-lib` GUIDES](GUIDES.md)
1. [The ripple-lib README](../README.md)
2. [The ripple-lib GUIDES](GUIDES.md)
#1. `Remote` options
#Remote options
```js
/* Loading ripple-lib with Node.js */
@@ -31,98 +29,86 @@ var Remote = require('ripple-lib').Remote;
/* Loading ripple-lib in a webpage */
// var Remote = ripple.Remote;
var remote = new Remote({options});
var options = { };
var remote = new Remote(options);
```
A new `Remote` can be created with the following options:
+ `trace` Log all of the events emitted (boolean)
+ `max_listeners` Set maxListeners for remote; prevents EventEmitter warnings (number)
+ `connection_offset` Connect to remote servers on supplied interval (number in seconds)
+ `trusted` truthy, if remote is trusted (boolean)
+ `local_fee` Set whether the transaction fee range will be set locally (boolean, default is true, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
+ `fee_cushion` Extra fee multiplier to account for async fee changes (number, e.g. 1.5, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
+ `max_fee` Maximum acceptable transaction fee (number in [XRP drops](https://ripple.com/wiki/Ripple_credits#Notes_on_drops), see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
+ `servers` Array of server objects of the following form:
+ `trace` *boolean default: false* Log all of the events emitted
+ `max_listeners` *number default: 0* Set maxListeners for servers
+ `trusted` *boolean default: false*, if remote is trusted (boolean)
+ `local_signing` *boolean default: true*
+ `local_fee` *boolean default: true* Set whether the transaction fee range will be set locally, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
+ `fee_cushion` *number default: 1.2* Extra fee multiplier to account for async fee changes, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
+ `max_fee` *number default: Infinity* Maximum acceptable transaction fee, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees)
+ `servers` *array* Array of server objects of the following form:
```js
{
host: <string>
, port: <number>
, secure: <boolean>
{
host: <string>,
port: <number>,
secure: <boolean>
}
```
+ `local_signing`
#2. `Remote` functions
##Server info functions
**[requestServerInfo([callback])](https://ripple.com/wiki/RPC_API#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.set_server`. Example:
or
```js
var request = remote.request_server_info();
request.set_server('my.hostname');
request.callback(function(err, res) {
});
request.request();
'wss://host:port'
```
**[requestUnlList([callback])](https://ripple.com/wiki/RPC_API#unl_list)**
#Request constructor functions
**[requestUnlAdd(addr, comment, [callback])](https://ripple.com/wiki/RPC_API#unl_add)**
##Server requests
**[requestUnlDelete(node, [callback])](https://ripple.com/wiki/RPC_API#unl_delete)**
**[server_info([callback])](https://ripple.com/wiki/JSON_Messages#server_info)**
**[requestPeers([callback])](https://ripple.com/wiki/RPC_API#peers)**
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');
request.setServer('wss://s1.ripple.com');
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)**
**[requestConnect(ip, port, [callback])](https://ripple.com/wiki/RPC_API#connect)**
**[connect(ip, port, [callback])](https://ripple.com/wiki/JSON_Messages#connect)**
##Ledger requests
**[ledger(ledger, [opts], [callback])](https://ripple.com/wiki/JSON_Messages#ledger)**
##Ledger query functions
**ledger_header([callback])**
**[requestLedger(ledger, [opts], [callback])](https://ripple.com/wiki/RPC_API#ledger)**
**[ledger_current([callback])](https://ripple.com/wiki/JSON_Messages#ledger_current)**
**requestLedgerHeader([callback])**
**[ledger_entry(type, [callback])](https://ripple.com/wiki/JSON_Messages#ledger_entry)**
**[requestLedgerCurrent([callback])](https://ripple.com/wiki/RPC_API#ledger_current)**
**[requestLedgerEntry(type, [callback])](https://ripple.com/wiki/RPC_API#ledger_entry)**
**[requestSubscribe(streams, [callback])](https://ripple.com/wiki/RPC_API#subscribe)**
**[subscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#subscribe)**
Start receiving selected streams from the server.
**[requestUnsubscribe(streams, [callback])](https://ripple.com/wiki/RPC_API#unsubscribe)**
**[unsubscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#unsubscribe)**
Stop receiving selected streams from the server.
##Account requests
##Transaction query functions
**[requestTransactionEntry(hash, [ledger_hash], [callback])](https://ripple.com/wiki/RPC_API#transaction_entry)**
Searches a particular ledger for a transaction hash. Default ledger is the open ledger.
**[requestTx(hash, [callback])](https://ripple.com/wiki/RPC_API#tx)**
Searches ledger history for validated transaction hashes.
##Account query functions
**[requestAccountInfo(account, [callback])](https://ripple.com/wiki/RPC_API#account_info)**
**[account_info(account, [callback])](https://ripple.com/wiki/JSON_Messages#account_info)**
Return information about the specified account.
@@ -143,13 +129,13 @@ Return information about the specified account.
}
```
**[requestAccountLines(accountID, account_index, current, [callback])](https://ripple.com/wiki/RPC_API#account_lines)**
**[account_lines(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_lines)**
**[requestAccountOffers(accountID, account_index, current, [callback])](https://ripple.com/wiki/RPC_API#account_offers)**
**[account_offers(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_offers)**
Return the specified account's outstanding offers.
**[requestAccountTx(opts, [callback])](https://ripple.com/wiki/RPC_API#account_tx)**
**[account_tx(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_tx)**
Fetch a list of transactions that applied to this account.
@@ -167,92 +153,150 @@ Options:
+ `fwd_marker`
+ `rev_marker`
**[requestWalletAccounts(seed, [callback])](https://ripple.com/wiki/RPC_API#wallet_accounts)**
**[wallet_accounts(seed, [callback])](https://ripple.com/wiki/JSON_Messages#wallet_accounts)**
Return a list of accounts for a wallet.
Return a list of accounts for a wallet. *Requires trusted remote*
+ requires trusted remote
**requestAccountBalance(account, ledger, [callback])**
**account_balance(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.
**requestAccountFlags(account, current, [callback])**
**account_flags(account, [ledger], [callback])**
Return the flags for an account.
**requestOwnerCount(account, current, [callback])**
**owner_count(account, [ledger], [callback])**
Return the owner count for an account.
**requestRippleBalance(account, issuer, currency, current, [callback])**
**ripple_balance(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)**
##Order book query functions
**[requestBookOffers(gets, pays, taker, [callback])](https://ripple.com/wiki/RPC_API#book_offers)**
Return the offers for an order book as one or more pages.
Return the offers for an order book, also called a *snapshot*
```js
var request = remote.request_book_offers({
gets: {
var request = remote.request('book_offers', {
taker_gets: {
'currency':'XRP'
},
pays: {
taker_pays: {
'currency':'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
});
request.request();
request.request(function(err, offers) {
//handle offers
});
```
##Transaction requests
**[transaction_entry(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.
##Transaction submission functions
**[tx(hash, [callback])](https://ripple.com/wiki/JSON_Messages#tx)**
**[requestSign(secret, tx_json, [callback])](https://ripple.com/wiki/RPC_API#sign)**
Searches ledger history for validated transaction hashes.
Sign a transaction.
**[sign(secret, tx_json, [callback])](https://ripple.com/wiki/JSON_Messages#sign)**
+ requires trusted remote
Sign a transaction. *Requires trusted remote*
**[requestSubmit([callback])](https://ripple.com/wiki/RPC_API#submit)**
**[submit([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)**
**[requestRipplePathFind(src_account, dst_account, dst_amount, src_currencies, [callback])](https://ripple.com/wiki/RPC_API#path_find)**
#Transaction constructors
Use `remote.createTransaction('TransactionType', [options])` to construct a transaction. To submit, use `transaction.submit([callback])`.
**transaction([destination], [source], [amount], [callback])**
**Payment**
Returns a [Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) object
```js
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: DEST_ADDRESS,
amount: AMOUNT
});
```
**AccountSet**
#3. Transaction events
```js
var transaction = remote.createTransaction('AccountSet', {
account: MY_ADDRESS,
set: 'RequireDest',
clear: 'RequireAuth'
});
```
**TrustSet**
```js
var transaction = remote.createTransaction('TrustSet', {
account: MY_ADDRESS,
limit: '1/USD/rrrrrrrrrrrrrrrrrrrrBZbvji'
});
```
**OfferCreate**
```js
var transaction = remote.createTransaction('OfferCreate', {
account: MY_ADDRESS,
taker_pays: '1',
taker_gets: '1/USD/rrrrrrrrrrrrrrrrrrrrBZbvji'
});
```
##Transaction events
[Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) objects are EventEmitters. They may emit the following events.
+ `final` Transaction has erred or succeeded. This event indicates that the transaction has finished processing.
+ `error` Transaction has erred. This event is a final state.
+ `success` Transaction succeeded. This event is a final state.
+ `presubmit` Immediately before transaction is submitted
+ `postsubmit` Immediately after transaction is submitted
+ `submitted` Transaction has been submitted to the network. The submission may result in a remote error or success.
+ `resubmitted` Transaction is beginning resubmission.
+ `proposed` Transaction has been submitted *successfully* to the network. The transaction at this point is awaiting validation in a ledger.
+ `timeout` Transaction submission timed out. The transaction will be resubmitted.
+ `resubmit` Transaction is beginning resubmission.
+ `fee_adjusted` Transaction fee has been adjusted during its pending state. The transaction fee will only be adjusted if the remote is configured for local fees, which it is by default.
+ `abort` Transaction has been aborted. Transactions are only aborted by manual calls to `#abort`.
+ `missing` Four ledgers have closed without detecting validated transaction
+ `lost` Eight ledgers have closed without detecting validated transaction. Consider the transaction lost and err/finalize.
##Complete payment example
#4. Amount objects
```js
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: DEST_ADDRESS,
amount: AMOUNT
});
transaction.on('resubmitted', function() {
// initial submission failed, resubmitting
});
transaction.submit(function(err, res) {
// submission has finalized with either an error or success.
// the transaction will not be retried after this point
});
```
#Amount objects
Coming Soon

142
npm-shrinkwrap.json generated Normal file
View File

@@ -0,0 +1,142 @@
{
"name": "ripple-lib",
"version": "0.9.0-rc5",
"dependencies": {
"async": {
"version": "0.8.0",
"from": "async@>=0.8.0 <0.9.0"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0"
},
"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",
"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",
"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.5",
"from": "combined-stream@>=0.0.4 <0.1.0",
"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"
}
}
},
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0"
}
}
},
"readable-stream": {
"version": "1.0.27-1",
"from": "readable-stream@1.0.27-1",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0"
},
"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"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0"
}
}
}
}
},
"ws": {
"version": "0.4.32",
"from": "ws@>=0.4.31 <0.5.0",
"dependencies": {
"commander": {
"version": "2.1.0",
"from": "commander@>=2.1.0 <2.2.0"
},
"nan": {
"version": "1.0.0",
"from": "nan@>=1.0.0 <1.1.0"
},
"tinycolor": {
"version": "0.0.1",
"from": "tinycolor@>=0.0.0 <1.0.0"
},
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5"
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.8.0-beta",
"version": "0.9.1",
"description": "Ripple JavaScript client library",
"files": [
"src/js/*",
@@ -20,7 +20,7 @@
"extend": "~1.2.1",
"lru-cache": "~2.5.0",
"superagent": "^0.18.0",
"gulp-bump": "~0.1.10"
"ripple-wallet-generator": "1.0.1"
},
"devDependencies": {
"mocha": "~1.14.0",
@@ -29,6 +29,7 @@
"gulp-jshint": "~1.5.5",
"gulp-uglify": "~0.3.0",
"gulp-rename": "~1.2.0",
"gulp-bump": "~0.1.10",
"webpack": "~1.1.11",
"map-stream": "~0.1.0",
"istanbul": "~0.2.10",
@@ -39,7 +40,7 @@
"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/.bin/_mocha -- --reporter spec test/*-test.js",
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/mocha/bin/_mocha -- --reporter spec test/*-test.js",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
},
"repository": {

43
scripts/publish Normal file
View File

@@ -0,0 +1,43 @@
echo "PUBLISH"
function exit_on_error {
res=$?
[[ ${res:-99} -eq 0 ]] || exit $res
}
rm -rf build
npm install
gulp
npm test
exit_on_error
echo ""
echo "publish to npm"
npm publish
exit_on_error
rm -rf dist
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist
gulp bower
exit_on_error
cd dist
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
exit_on_error
git commit -m "[TASK] add v$version"
exit_on_error
git tag "v$version"
exit_on_error
git push origin master
git push --tags origin master
cd ..

43
scripts/publish_rc Normal file
View File

@@ -0,0 +1,43 @@
echo "PUBLISH RELEASE CANDIDATE"
function exit_on_error {
res=$?
[[ ${res:-99} -eq 0 ]] || exit $res
}
rm -rf build
npm install
gulp
npm test
exit_on_error
echo ""
echo "publish rc to npm"
npm publish --tag beta
exit_on_error
rm -rf dist
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist
gulp bower
exit_on_error
cd dist
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
exit_on_error
git commit -m "[TASK] add v$version"
exit_on_error
git tag "v$version"
exit_on_error
git push origin master
git push --tags origin master
cd ..

12
scripts/publish_to_bower Normal file
View File

@@ -0,0 +1,12 @@
rm -rf dist
git clone git@github.com:ripple/bower-ripple.git dist
gulp bower
cd dist
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
git commit -m "[TASK] add v$version"
git tag "v$version"
git push origin master
git push --tags origin master
cd ..

View File

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

View File

@@ -12,7 +12,25 @@ var UInt160 = require('./uint160').UInt160;
var Seed = require('./seed').Seed;
var Currency = require('./currency').Currency;
var consts = exports.consts = {
//
// Amount class in the style of Java's BigInteger class
// http://docs.oracle.com/javase/1.3/docs/api/java/math/BigInteger.html
//
function Amount() {
// Json format:
// integer : XRP
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
this._value = new BigInteger(); // NaN for bad value. Always positive.
this._offset = 0; // Always 0 for XRP.
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
this._is_negative = false;
this._currency = new Currency();
this._issuer = new UInt160();
};
var consts = {
currency_xns: 0,
currency_one: 1,
xns_precision: 6,
@@ -33,26 +51,17 @@ var consts = exports.consts = {
cMinOffset: -96,
cMaxOffset: 80,
// Maximum possible amount for non-XRP currencies using the maximum mantissa
// with maximum exponent. Corresponds to hex 0xEC6386F26FC0FFFF.
max_value: '9999999999999999e80'
};
// Add constants to Amount class
extend(Amount, consts);
//
// Amount class in the style of Java's BigInteger class
// http://docs.oracle.com/javase/1.3/docs/api/java/math/BigInteger.html
//
function Amount() {
// Json format:
// integer : XRP
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
this._value = new BigInteger(); // NaN for bad value. Always positive.
this._offset = 0; // Always 0 for XRP.
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
this._is_negative = false;
this._currency = new Currency();
this._issuer = new UInt160();
};
// DEPRECATED: Use Amount instead, e.g. Amount.currency_xns
exports.consts = consts;
// Given '100/USD/mtgox' return the a string with mtgox remapped.
Amount.text_full_rewrite = function(j) {
@@ -132,12 +141,12 @@ Amount.prototype.add = function(v) {
var o2 = v._offset;
while (o1 < o2) {
v1 = v1.divide(consts.bi_10);
v1 = v1.divide(Amount.bi_10);
o1 += 1;
}
while (o2 < o1) {
v2 = v2.divide(consts.bi_10);
v2 = v2.divide(Amount.bi_10);
o2 += 1;
}
@@ -185,22 +194,22 @@ Amount.prototype.multiply = function(v) {
var o2 = v._offset;
if (this.is_native()) {
while (v1.compareTo(consts.bi_man_min_value) < 0) {
v1 = v1.multiply(consts.bi_10);
while (v1.compareTo(Amount.bi_man_min_value) < 0) {
v1 = v1.multiply(Amount.bi_10);
o1 -= 1;
}
}
if (v.is_native()) {
while (v2.compareTo(consts.bi_man_min_value) < 0) {
v2 = v2.multiply(consts.bi_10);
while (v2.compareTo(Amount.bi_man_min_value) < 0) {
v2 = v2.multiply(Amount.bi_10);
o2 -= 1;
}
}
result = new Amount();
result._offset = o1 + o2 + 14;
result._value = v1.multiply(v2).divide(consts.bi_1e14).add(consts.bi_7);
result._value = v1.multiply(v2).divide(Amount.bi_1e14).add(Amount.bi_7);
result._is_native = this._is_native;
result._is_negative = this._is_negative !== v._is_negative;
result._currency = this._currency;
@@ -234,8 +243,8 @@ Amount.prototype.divide = function(d) {
if (_n.is_native()) {
_n = _n.clone();
while (_n._value.compareTo(consts.bi_man_min_value) < 0) {
_n._value = _n._value.multiply(consts.bi_10);
while (_n._value.compareTo(Amount.bi_man_min_value) < 0) {
_n._value = _n._value.multiply(Amount.bi_10);
_n._offset -= 1;
}
}
@@ -245,15 +254,15 @@ Amount.prototype.divide = function(d) {
if (_d.is_native()) {
_d = _d.clone();
while (_d._value.compareTo(consts.bi_man_min_value) < 0) {
_d._value = _d._value.multiply(consts.bi_10);
while (_d._value.compareTo(Amount.bi_man_min_value) < 0) {
_d._value = _d._value.multiply(Amount.bi_10);
_d._offset -= 1;
}
}
result = new Amount();
result._offset = _n._offset - _d._offset - 17;
result._value = _n._value.multiply(consts.bi_1e17).divide(_d._value).add(consts.bi_5);
result._value = _n._value.multiply(Amount.bi_1e17).divide(_d._value).add(Amount.bi_5);
result._is_native = _n._is_native;
result._is_negative = _n._is_negative !== _d._is_negative;
result._currency = _n._currency;
@@ -327,7 +336,7 @@ Amount.prototype.ratio_human = function(denominator, opts) {
// To compensate, we multiply the numerator by 10^xns_precision.
if (denominator._is_native) {
numerator = numerator.clone();
numerator._value = numerator._value.multiply(consts.bi_xns_unit);
numerator._value = numerator._value.multiply(Amount.bi_xns_unit);
numerator.canonicalize();
}
@@ -385,7 +394,7 @@ Amount.prototype.product_human = function(factor, opts) {
//
// See also Amount#ratio_human.
if (factor._is_native) {
product._value = product._value.divide(consts.bi_xns_unit);
product._value = product._value.divide(Amount.bi_xns_unit);
product.canonicalize();
}
@@ -398,7 +407,7 @@ Amount.prototype.product_human = function(factor, opts) {
* @private
*/
Amount.prototype._invert = function() {
this._value = consts.bi_1e32.divide(this._value);
this._value = Amount.bi_1e32.divide(this._value);
this._offset = -32 - this._offset;
this.canonicalize();
@@ -428,12 +437,12 @@ Amount.prototype.canonicalize = function() {
// Normalize _offset to 0.
while (this._offset < 0) {
this._value = this._value.divide(consts.bi_10);
this._value = this._value.divide(Amount.bi_10);
this._offset += 1;
}
while (this._offset > 0) {
this._value = this._value.multiply(consts.bi_10);
this._value = this._value.multiply(Amount.bi_10);
this._offset -= 1;
}
}
@@ -445,13 +454,13 @@ Amount.prototype.canonicalize = function() {
} else {
// Normalize mantissa to valid range.
while (this._value.compareTo(consts.bi_man_min_value) < 0) {
this._value = this._value.multiply(consts.bi_10);
while (this._value.compareTo(Amount.bi_man_min_value) < 0) {
this._value = this._value.multiply(Amount.bi_10);
this._offset -= 1;
}
while (this._value.compareTo(consts.bi_man_max_value) > 0) {
this._value = this._value.divide(consts.bi_10);
while (this._value.compareTo(Amount.bi_man_max_value) > 0) {
this._value = this._value.divide(Amount.bi_10);
this._offset += 1;
}
}
@@ -669,14 +678,14 @@ Amount.prototype.parse_human = function(j, opts) {
fraction += '0';
}
this._is_native = true;
this._value = this._value.multiply(consts.bi_xns_unit).add(new BigInteger(fraction));
this._value = this._value.multiply(Amount.bi_xns_unit).add(new BigInteger(fraction));
} else {
// Other currencies have arbitrary precision
fraction = fraction.replace(/0+$/, '');
precision = fraction.length;
this._is_native = false;
var multiplier = consts.bi_10.clone().pow(precision);
var multiplier = Amount.bi_10.clone().pow(precision);
this._value = this._value.multiply(multiplier).add(new BigInteger(fraction));
this._offset = -precision;
@@ -875,8 +884,8 @@ Amount.prototype.parse_native = function(j) {
this._value = new BigInteger(m[2]);
} else {
// Float notation : values multiplied by 1,000,000.
var int_part = (new BigInteger(m[2])).multiply(consts.bi_xns_unit);
var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+consts.xns_precision-m[3].length))));
var int_part = (new BigInteger(m[2])).multiply(Amount.bi_xns_unit);
var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+Amount.xns_precision-m[3].length))));
this._value = int_part.add(fraction_part);
}
@@ -885,7 +894,7 @@ Amount.prototype.parse_native = function(j) {
this._offset = 0;
this._is_negative = !!m[1] && this._value.compareTo(BigInteger.ZERO) !== 0;
if (this._value.compareTo(consts.bi_xns_max) > 0) {
if (this._value.compareTo(Amount.bi_xns_max) > 0) {
this._value = NaN;
}
} else {
@@ -927,7 +936,7 @@ Amount.prototype.parse_value = function(j) {
var fraction = new BigInteger(d[3]);
var precision = d[3].length;
this._value = integer.multiply(consts.bi_10.clone().pow(precision)).add(fraction);
this._value = integer.multiply(Amount.bi_10.clone().pow(precision)).add(fraction);
this._offset = -precision;
this._is_negative = !!d[1];
@@ -978,7 +987,7 @@ Amount.prototype.to_text = function(allow_nan) {
var result = NaN;
if (this._is_native) {
if (this.is_valid() && this._value.compareTo(consts.bi_xns_max) <= 0){
if (this.is_valid() && this._value.compareTo(Amount.bi_xns_max) <= 0){
result = this._value.toString();
}
} else if (this.is_zero()) {
@@ -1086,8 +1095,8 @@ Amount.prototype.to_human = function(opts) {
ref = this.applyInterest(opts.reference_date);
}
var order = ref._is_native ? consts.xns_precision : -ref._offset;
var denominator = consts.bi_10.clone().pow(order);
var order = ref._is_native ? Amount.xns_precision : -ref._offset;
var denominator = Amount.bi_10.clone().pow(order);
var int_part = ref._value.divide(denominator).toString();
var fraction_part = ref._value.mod(denominator).toString();
@@ -1100,12 +1109,26 @@ Amount.prototype.to_human = function(opts) {
fraction_part = fraction_part.replace(/0*$/, '');
if (fraction_part.length || !opts.skip_empty_fraction) {
// Enforce the maximum number of decimal digits (precision)
if (typeof opts.precision === 'number') {
if (opts.precision === 0 && fraction_part.charCodeAt(0) >= 53) {
int_part = (Number(int_part) + 1).toString();
if (opts.precision <= 0) {
// increment the int_part if the first decimal is 5 or higher
if (fraction_part.charCodeAt(0) >= 53) {
int_part = (Number(int_part) + 1).toString();
}
fraction_part = '';
} else {
var precision = Math.min(opts.precision, fraction_part.length);
fraction_part = Math.round(fraction_part / Math.pow(10, fraction_part.length - precision)).toString();
// because the division above will cut off the leading 0's we have to add them back again
// XXX look for a more elegant alternative
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
}
fraction_part = fraction_part.slice(0, opts.precision);
}
// Limit the number of significant digits (max_sig_digits)
@@ -1170,7 +1193,7 @@ Amount.prototype.to_human_full = function(opts) {
var a = this.to_human(opts);
var c = this._currency.to_human();
var i = this._issuer.to_json(opts);
var o = this.is_native ? (o = a + '/' + c) : (o = a + '/' + c + '/' + i);
var o = this.is_native() ? (o = a + '/' + c) : (o = a + '/' + c + '/' + i);
return o;
};

View File

@@ -382,23 +382,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
};

View File

@@ -119,7 +119,9 @@ BlobObj.prototype.init = function(fn) {
self.revision = resp.body.revision;
self.encrypted_secret = resp.body.encrypted_secret;
self.identity_id = resp.body.identity_id;
self.missing_fields = resp.body.missing_fields;
//self.attestations = resp.body.attestation_summary;
if (!self.decrypt(resp.body.blob)) {
return fn(new Error('Error while decrypting blob'));
@@ -527,17 +529,17 @@ BlobObj.prototype.postUpdate = function(op, pointer, params, fn) {
};
/**
* get2FA - ECDSA signed request
* get2FA - HMAC signed request
*/
BlobObj.prototype.get2FA = function (masterkey, fn) {
BlobObj.prototype.get2FA = function (fn) {
var config = {
method : 'GET',
url : this.url + '/v1/blob/' + this.id + '/2FA?device_id=' + this.device_id,
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signAsymmetric(masterkey, this.data.account_id, this.id);
var signed = signedRequest.signHmac(this.data.auth_secret, this.id);
request.get(signed.url)
.end(function(err, resp) {
@@ -561,7 +563,6 @@ BlobObj.prototype.get2FA = function (masterkey, fn) {
* @params {boolean} options.enabled
* @params {string} options.phone
* @params {string} options.country_code
* @params {string} options.via //sms, etc
*/
BlobObj.prototype.set2FA = function(options, fn) {
@@ -572,8 +573,7 @@ BlobObj.prototype.set2FA = function(options, fn) {
data : {
enabled : options.enabled,
phone : options.phone,
country_code : options.country_code,
via : options.via
country_code : options.country_code
}
};
@@ -916,14 +916,22 @@ BlobClient.get = function (options, fn) {
* request new token to be sent for 2FA
* @param {string} url
* @param {string} id
* @param {string} force_sms
*/
BlobClient.requestToken = function (url, id, fn) {
BlobClient.requestToken = function (url, id, force_sms, fn) {
var config = {
method : 'GET',
url : url + '/v1/blob/' + id + '/2FA/requestToken'
};
if (force_sms && force_sms instanceof Function) {
fn = force_sms;
} else if (force_sms) {
config.url += "?force_sms=true";
}
request.get(config.url)
.end(function(err, resp) {
if (err) {
@@ -1107,48 +1115,6 @@ BlobClient.recoverBlob = function (opts, fn) {
};
};
/**
* updateProfile
* update information stored outside the blob - HMAC signed
* @param {object}
* @param {string} opts.url
* @param {string} opts.username
* @param {string} opts.auth_secret
* @param {srring} opts.blob_id
* @param {object} opts.profile
* @param {string} opts.profile.phone - optional
* @param {string} opts.profile.country - optional
* @param {string} opts.profile.region - optional
* @param {string} opts.profile.city - optional
*/
BlobClient.updateProfile = function (opts, fn) {
var config = {
method: 'POST',
url: opts.url + '/v1/user/' + opts.username + '/profile',
dataType: 'json',
data: opts.profile
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.post(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('updateProfile:', err);
fn(new Error('Failed to update profile - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body) {
log.error('updateProfile:', resp.body);
} else {
fn(new Error('Failed to update profile'));
}
});
};
/**
* updateKeys
@@ -1312,6 +1278,7 @@ BlobClient.create = function(options, fn) {
if (err) {
fn(err);
} else if (resp.body && resp.body.result === 'success') {
blob.identity_id = resp.body.identity_id;
fn(null, blob, resp.body);
} else if (resp.body && resp.body.result === 'error') {
fn(new Error(resp.body.message));
@@ -1355,4 +1322,263 @@ BlobClient.deleteBlob = function(options, fn) {
});
};
/*** identity related functions ***/
/**
* updateProfile
* update information stored outside the blob - HMAC signed
* @param {object}
* @param {string} opts.url
* @param {string} opts.auth_secret
* @param {srring} opts.blob_id
* @param {object} opts.profile
* @param {array} opts.profile.attributes (optional, array of attribute objects)
* @param {array} opts.profile.addresses (optional, array of address objects)
*
* @param {string} attribute.id ... id of existing attribute
* @param {string} attribute.name ... attribute name i.e. ripple_address
* @param {string} attribute.type ... optional, sub-type of attribute
* @param {string} attribute.value ... value of attribute
* @param {string} attribute.domain ... corresponding domain
* @param {string} attribute.status ... “current”, “removed”, etc.
* @param {string} attribute.visibitlity ... “public”, ”private”
*/
BlobClient.updateProfile = function (opts, fn) {
var config = {
method: 'POST',
url: opts.url + '/v1/profile/',
dataType: 'json',
data: opts.profile
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.post(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('updateProfile:', err);
fn(new Error('Failed to update profile - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body) {
log.error('updateProfile:', resp.body);
fn(new Error('Failed to update profile'));
} else {
fn(new Error('Failed to update profile'));
}
});
};
/**
* getProfile
* @param {Object} opts
* @param {string} opts.url
* @param {string} opts.auth_secret
* @param {srring} opts.blob_id
*/
BlobClient.getProfile = function (opts, fn) {
var config = {
method: 'GET',
url: opts.url + '/v1/profile/'
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.get(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('getProfile:', err);
fn(new Error('Failed to get profile - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body) {
log.error('getProfile:', resp.body);
fn(new Error('Failed to get profile'));
} else {
fn(new Error('Failed to get profile'));
}
});
};
/**
* getAttestation
* @param {Object} opts
* @param {string} opts.url
* @param {string} opts.auth_secret
* @param {string} opts.blob_id
* @param {string} opts.type (email,phone,basic_identity)
* @param {object} opts.phone (required for type 'phone')
* @param {string} opts.email (required for type 'email')
*/
BlobClient.getAttestation = function (opts, fn) {
var params = { };
if (opts.phone) params.phone = opts.phone;
if (opts.email) params.email = opts.email;
var config = {
method: 'POST',
url: opts.url + '/v1/attestation/' + opts.type,
dataType: 'json',
data: params
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.post(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('attest:', err);
fn(new Error('attestation error - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
if (resp.body.attestation) {
resp.body.decoded = BlobClient.parseAttestation(resp.body.attestation);
}
fn(null, resp.body);
} else if (resp.body) {
log.error('attestation:', resp.body);
fn(new Error('attestation error: ' + resp.body.message || ""));
} else {
fn(new Error('attestation error'));
}
});
};
/**
* getAttestationSummary
* @param {Object} opts
* @param {string} opts.url
* @param {string} opts.auth_secret
* @param {string} opts.blob_id
*/
BlobClient.getAttestationSummary = function (opts, fn) {
var config = {
method: 'GET',
url: opts.url + '/v1/attestation/summary',
dataType: 'json'
};
if (opts.full) config.url += '?full=true';
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.get(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('attest:', err);
fn(new Error('attestation error - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
if (resp.body.attestation) {
resp.body.decoded = BlobClient.parseAttestation(resp.body.attestation);
}
fn(null, resp.body);
} else if (resp.body) {
log.error('attestation:', resp.body);
fn(new Error('attestation error: ' + resp.body.message || ""));
} else {
fn(new Error('attestation error'));
}
});
};
/**
* updateAttestation
* @param {Object} opts
* @param {string} opts.url
* @param {string} opts.auth_secret
* @param {string} opts.blob_id
* @param {string} opts.type (email,phone,profile,identity)
* @param {object} opts.phone (required for type 'phone')
* @param {object} opts.profile (required for type 'profile')
* @param {string} opts.email (required for type 'email')
* @param {string} opts.answers (required for type 'identity')
* @param {string} opts.token (required for completing email or phone attestations)
*/
BlobClient.updateAttestation = function (opts, fn) {
var params = { };
if (opts.phone) params.phone = opts.phone;
if (opts.profile) params.profile = opts.profile;
if (opts.email) params.email = opts.email;
if (opts.token) params.token = opts.token;
if (opts.answers) params.answers = opts.answers;
var config = {
method: 'POST',
url: opts.url + '/v1/attestation/' + opts.type + '/update',
dataType: 'json',
data: params
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.post(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('attest:', err);
fn(new Error('attestation error - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
if (resp.body.attestation) {
resp.body.decoded = BlobClient.parseAttestation(resp.body.attestation);
}
fn(null, resp.body);
} else if (resp.body) {
log.error('attestation:', resp.body);
fn(new Error('attestation error: ' + resp.body.message || ""));
} else {
fn(new Error('attestation error'));
}
});
};
/**
* parseAttestation
* @param {Object} attestation
*/
BlobClient.parseAttestation = function (attestation) {
var segments = decodeURIComponent(attestation).split('.');
var decoded;
// base64 decode and parse JSON
try {
decoded = {
header : JSON.parse(crypt.decodeBase64(segments[0])),
payload : JSON.parse(crypt.decodeBase64(segments[1])),
signature : segments[2]
};
} catch (e) {
console.log("invalid attestation:", e);
}
return decoded;
};
exports.BlobClient = BlobClient;

View File

@@ -56,6 +56,18 @@ function keyHash(key, token) {
return sjcl.codec.hex.fromBits(sjcl.bitArray.bitSlice(hmac.encrypt(token), 0, 256));
};
/**
* add entropy at each call to get random words
* @param {number} nWords
*/
function randomWords (nWords) {
for (var i = 0; i < 8; i++) {
sjcl.random.addEntropy(Math.random(), 32, "Math.random()");
}
return sjcl.random.randomWords(nWords);
}
/****** exposed functions ******/
/**
@@ -213,7 +225,7 @@ Crypt.isValidAddress = function (address) {
*/
Crypt.createSecret = function (nWords) {
return sjcl.codec.hex.fromBits(sjcl.random.randomWords(nWords));
return sjcl.codec.hex.fromBits(randomWords(nWords));
};
/**
@@ -221,7 +233,7 @@ Crypt.createSecret = function (nWords) {
*/
Crypt.createMaster = function () {
return base.encode_check(33, sjcl.codec.bytes.fromBits(sjcl.random.randomWords(4)));
return base.encode_check(33, sjcl.codec.bytes.fromBits(randomWords(4)));
};
@@ -310,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

@@ -50,7 +50,7 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
* \s*$ // end with any amount of whitespace
*
*/
Currency.prototype.human_RE = /^\s*([a-zA-Z]{3}|[0-9]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
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) {
@@ -316,17 +316,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

@@ -18,6 +18,7 @@ exports.RippleTxt = require('./rippletxt').RippleTxt;
exports.binformat = require('./binformat');
exports.utils = require('./utils');
exports.Server = require('./server').Server;
exports.Wallet = require('./wallet');
// 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

View File

@@ -78,18 +78,20 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
});
this.on('unsubscribe', function() {
self._ownerFunds = { };
self.resetCache();
self._remote.removeListener('transaction', updateFundedAmounts);
self._remote.removeListener('transaction', updateTransferRate);
});
this._remote.on('prepare_subscribe', function() {
this._remote.once('prepare_subscribe', function() {
self.subscribe();
});
this._remote.on('disconnect', function() {
self._ownerFunds = { };
self._synchronized = false;
self.resetCache();
self._remote.once('prepare_subscribe', function() {
self.subscribe();
});
});
function updateFundedAmounts(message) {
@@ -153,10 +155,20 @@ OrderBook.prototype.subscribe = function() {
log.info('subscribing', this._key);
}
this.requestTransferRate();
var steps = [
function(callback) {
self.requestTransferRate(callback);
},
function(callback) {
self.requestOffers(callback);
},
function(callback) {
self.subscribeTransactions(callback);
}
];
this.requestOffers().once('success', function() {
self.subscribeTransactions();
async.series(steps, function(err) {
//XXX What now?
});
};
@@ -176,12 +188,24 @@ OrderBook.prototype.unsubscribe = function() {
this._shouldSubscribe = false;
OrderBook.EVENTS.forEach(function(event) {
this.removeAllListeners(event);
}, this);
if (self.listeners(event).length > 0) {
self.removeAllListeners(event);
}
});
this.emit('unsubscribe');
};
/**
* Reset cached owner funds, offer counts
*/
OrderBook.prototype.resetCache = function() {
this._ownerFunds = { };
this._offerCounts = { };
this._synchronized = false;
};
/**
* Check that the funds for offer owner have been cached
*
@@ -284,10 +308,8 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
return balance;
}
var adjustedBalance = Amount.from_json(balance
+ '/' + this._currencyGets.to_json()
+ '/' + this._issuerGets
)
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
var adjustedBalance = Amount.from_json(balance + iouSuffix)
.divide(transferRate)
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
.to_json()
@@ -304,7 +326,6 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
OrderBook.prototype.requestTransferRate = function(callback) {
var self = this;
var issuer = this._issuerGets;
this.once('transfer_rate', function(rate) {
@@ -352,22 +373,56 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
assert.strictEqual(typeof offer, 'object', 'Offer is invalid');
assert(!isNaN(fundedAmount), 'Funds is invalid');
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
if (fundedAmount === '0') {
offer.taker_gets_funded = '0';
offer.taker_pays_funded = '0';
offer.is_fully_funded = false;
return offer;
}
var takerGetsValue = (typeof offer.TakerGets === 'object')
var iouSuffix = '/' + this._currencyGets.to_json()
+ '/' + this._issuerGets;
offer.is_fully_funded = Amount.from_json(
this._currencyGets.is_native() ? fundedAmount : fundedAmount + iouSuffix
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
if (offer.is_fully_funded) {
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
offer.taker_pays_funded = Amount.from_json(offer.TakerPays).to_text();
return offer;
}
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 takerGets = Amount.from_json(takerGetsValue + iouSuffix);
var takerPays = Amount.from_json(
takerPaysValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
offer.is_fully_funded = Amount.from_json(
fundedAmount + iouSuffix
).compareTo(takerGets) >= 0;
var takerGets = Amount.from_json(
takerGetsValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
if (offer.is_fully_funded) {
offer.taker_gets_funded = takerGetsValue;
var fundedPays = Amount.from_json(
fundedAmount + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var rate = takerPays.divide(takerGets);
fundedPays = fundedPays.multiply(rate);
if (fundedPays.compareTo(takerPays) < 0) {
offer.taker_pays_funded = fundedPays.to_json().value;
} else {
offer.taker_gets_funded = fundedAmount;
offer.taker_pays_funded = takerPays.to_json().value;
}
return offer;
@@ -568,6 +623,8 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
this.once('transfer_rate', function() {
self.updateFundedAmounts(message);
});
this.requestTransferRate();
return;
}
@@ -586,12 +643,8 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
var result = this.getBalanceChange(node);
if (result.isValid) {
var account = result.account;
var balance = result.balance;
if (this.hasCachedFunds(account)) {
var fundedAmount = this.applyTransferRate(balance);
this.updateOfferFunds(account, fundedAmount);
if (this.hasCachedFunds(result.account)) {
this.updateOfferFunds(result.account, result.balance);
}
}
}
@@ -628,11 +681,15 @@ OrderBook.prototype.updateTransferRate = function(message) {
* Request orderbook entries from server
*/
OrderBook.prototype.requestOffers = function() {
OrderBook.prototype.requestOffers = function(callback) {
var self = this;
if (typeof callback !== 'function') {
callback = function(){};
}
if (!this._shouldSubscribe) {
return;
return callback(new Error('Should not request offers'));
}
if (this._remote.trace) {
@@ -642,26 +699,18 @@ OrderBook.prototype.requestOffers = function() {
function handleOffers(res) {
if (!Array.isArray(res.offers)) {
// XXX What now?
return;
}
if (!self._currencyGets.is_native() && !self._issuerTransferRate) {
// Defer until transfer rate is requested
if (self._remote.trace) {
log.info('waiting for transfer rate');
}
self.once('transfer_rate', function() {
handleOffers(res);
});
return;
return callback(new Error('Invalid response'));
}
if (self._remote.trace) {
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
}
self._offers = res.offers.map(function addOffer(offer) {
// 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)) {
@@ -673,13 +722,14 @@ OrderBook.prototype.requestOffers = function() {
self.setFundedAmount(offer, fundedAmount);
self.incrementOfferCount(offer.Account);
return offer;
});
self._offers.push(offer);
}
self._synchronized = true;
self.emit('model', self._offers);
callback(null, self._offers);
};
function handleError(err) {
@@ -687,6 +737,8 @@ OrderBook.prototype.requestOffers = function() {
if (self._remote.trace) {
log.info('failed to request offers', self._key, err);
}
callback(err);
};
var request = this._remote.requestBookOffers(this.toJSON());
@@ -701,28 +753,37 @@ OrderBook.prototype.requestOffers = function() {
* Subscribe to transactions stream
*/
OrderBook.prototype.subscribeTransactions = function() {
OrderBook.prototype.subscribeTransactions = function(callback) {
var self = this;
if (typeof callback !== 'function') {
callback = function(){};
}
if (!this._shouldSubscribe) {
return;
return callback('Should not subscribe');
}
if (this._remote.trace) {
log.info('subscribing to transactions');
}
function handleSubscribed() {
self._subscribed = true;
function handleSubscribed(res) {
if (self._remote.trace) {
log.info('subscribed to transactions');
}
self._subscribed = true;
callback(null, res);
};
function handleError(err) {
if (self._remote.trace) {
log.info('failed to subscribe to transactions', self._key, err);
}
callback(err);
};
var request = this._remote.requestSubscribe();
@@ -868,29 +929,44 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
* @param {String|Object} offer funds
*/
OrderBook.prototype.updateOfferFunds = function(account, fundedAmount) {
OrderBook.prototype.updateOfferFunds = function(account, balance) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(fundedAmount), 'Funded amount 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) {
// Update funds for account's offer
var previousOffer = extend({}, offer);
var previousAmount = offer.taker_gets_funded;
if (offer.Account !== account) {
continue;
}
this.setFundedAmount(offer, fundedAmount);
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, previousAmount, fundedAmount);
this.emit(
'offer_funds_changed', offer,
previousOffer.taker_gets_funded,
offer.taker_gets_funded
);
}
}
};

View File

@@ -15,6 +15,7 @@
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var assert = require('assert');
var LRU = require('lru-cache');
var Server = require('./server').Server;
var Request = require('./request').Request;
@@ -83,6 +84,7 @@ function Remote(opts, trace) {
this.trusted = Boolean(opts.trusted);
this.state = 'offline'; // 'online', 'offline'
this._server_fatal = false; // True, if we know server exited.
this._allow_partial_history = (typeof opts.allow_partial_history === 'boolean') ? opts.allow_partial_history : true;
this.local_sequence = Boolean(opts.local_sequence); // Locally track sequence numbers
this.local_fee = (typeof opts.local_fee === 'boolean') ? opts.local_fee : true;// Locally set fees
@@ -92,6 +94,8 @@ function Remote(opts, trace) {
this.fee_cushion = (typeof opts.fee_cushion === 'number') ? opts.fee_cushion : 1.2;
this.max_fee = (typeof opts.max_fee === 'number') ? opts.max_fee : Infinity;
this.max_attempts = (typeof opts.max_attempts === 'number') ? opts.max_attempts : 10;
this._ledger_current_index = void(0);
this._ledger_hash = void(0);
this._ledger_time = void(0);
@@ -208,7 +212,7 @@ function Remote(opts, trace) {
function listenerAdded(type, listener) {
if (type === 'transaction_all') {
if (!self._transaction_subs && self._connected) {
if (!self._transaction_subs && self.isConnected()) {
self.request_subscribe('transactions').request();
}
self._transaction_subs += 1;
@@ -220,7 +224,7 @@ function Remote(opts, trace) {
function listenerRemoved(type, listener) {
if (type === 'transaction_all') {
self._transaction_subs -= 1;
if (!self._transaction_subs && self._connected) {
if (!self._transaction_subs && self.isConnected()) {
self.request_unsubscribe('transactions').request();
}
}
@@ -242,7 +246,7 @@ function Remote(opts, trace) {
};
if (opts.ping) {
this.once('connect', pingServers);
this.on('connect', pingServers);
}
function reconnect() {
@@ -337,13 +341,11 @@ Remote.isValidLedgerData = function(message) {
return (typeof message === 'object')
&& (typeof message.fee_base === 'number')
&& (typeof message.fee_ref === 'number')
&& (typeof message.fee_base === 'number')
&& (typeof message.ledger_hash === 'string')
&& (typeof message.ledger_index === 'number')
&& (typeof message.ledger_time === 'number')
&& (typeof message.reserve_base === 'number')
&& (typeof message.reserve_inc === 'number')
&& (typeof message.txn_count === 'number');
&& (typeof message.reserve_inc === 'number');
};
/**
@@ -513,12 +515,9 @@ Remote.prototype.reconnect = function() {
log.info('reconnecting');
;(function nextServer(i) {
self._servers[i].reconnect();
if (++i < self._servers.length) {
nextServer(i);
}
})(0);
this._servers.forEach(function(server) {
server.reconnect();
});
return this;
};
@@ -552,12 +551,9 @@ Remote.prototype.connect = function(online) {
this._should_connect = true;
;(function nextServer(i) {
self._servers[i].connect();
if (++i < self._servers.length) {
nextServer(i);
}
})(0);
this._servers.forEach(function(server) {
server.connect();
});
return this;
};
@@ -576,7 +572,7 @@ Remote.prototype.disconnect = function(callback) {
var callback = (typeof callback === 'function') ? callback : function(){};
if (!this._connected) {
if (!this.isConnected()) {
callback();
return this;
}
@@ -621,16 +617,16 @@ Remote.prototype._handleMessage = function(message, server) {
switch (message.type) {
case 'ledgerClosed':
this._handleLedgerClosed(message);
this._handleLedgerClosed(message, server);
break;
case 'serverStatus':
this._handleServerStatus(message);
this._handleServerStatus(message, server);
break;
case 'transaction':
this._handleTransaction(message);
this._handleTransaction(message, server);
break;
case 'path_find':
this._handlePathFind(message);
this._handlePathFind(message, server);
break;
default:
if (this.trace) {
@@ -646,7 +642,7 @@ Remote.prototype._handleMessage = function(message, server) {
* @param {Object} message
*/
Remote.prototype._handleLedgerClosed = function(message) {
Remote.prototype._handleLedgerClosed = function(message, server) {
var self = this;
// XXX If not trusted, need to verify we consider ledger closed.
@@ -659,11 +655,19 @@ Remote.prototype._handleLedgerClosed = function(message) {
var ledgerAdvanced = message.ledger_index >= this._ledger_current_index;
if (ledgerAdvanced) {
if (isNaN(this._ledger_current_index) || ledgerAdvanced) {
this._ledger_time = message.ledger_time;
this._ledger_hash = message.ledger_hash;
this._ledger_current_index = message.ledger_index + 1;
this.emit('ledger_closed', message);
if (this.isConnected()) {
this.emit('ledger_closed', message, server);
} else {
this.once('connect', function() {
// Delay until server is 'online'
self.emit('ledger_closed', message, server);
});
}
}
};
@@ -673,8 +677,8 @@ Remote.prototype._handleLedgerClosed = function(message) {
* @param {Object} message
*/
Remote.prototype._handleServerStatus = function(message) {
this.emit('server_status', message);
Remote.prototype._handleServerStatus = function(message, server) {
this.emit('server_status', message, server);
};
/**
@@ -683,7 +687,7 @@ Remote.prototype._handleServerStatus = function(message) {
* @param {Object} message
*/
Remote.prototype._handleTransaction = function(message) {
Remote.prototype._handleTransaction = function(message, server) {
var self = this;
// XXX If not trusted, need proof.
@@ -730,8 +734,8 @@ Remote.prototype._handleTransaction = function(message) {
});
}
this.emit('transaction', message);
this.emit('transaction_all', message);
this.emit('transaction', message, server);
this.emit('transaction_all', message, server);
};
/**
@@ -740,7 +744,7 @@ Remote.prototype._handleTransaction = function(message) {
* @param {Object} message
*/
Remote.prototype._handlePathFind = function(message) {
Remote.prototype._handlePathFind = function(message, server) {
var self = this;
// Pass the event to the currently open PathFind object
@@ -748,7 +752,7 @@ Remote.prototype._handlePathFind = function(message) {
this._cur_path_find.notify_update(message);
}
this.emit('path_find_all', message);
this.emit('path_find_all', message, server);
};
/**
@@ -818,6 +822,10 @@ Remote.prototype.getServer = function() {
}
var connectedServers = this.getConnectedServers();
if (connectedServers.length === 0 || !connectedServers[0]) {
return null;
}
var server = connectedServers[0];
var cScore = server._score + server._fee;
@@ -860,7 +868,7 @@ Remote.prototype.request = function(request) {
if (!this._servers.length) {
request.emit('error', new Error('No servers available'));
} else if (!this._connected) {
} else if (!this.isConnected()) {
this.once('connect', this.request.bind(this, request));
} else if (request.server === null) {
request.emit('error', new Error('Server does not exist'));
@@ -923,21 +931,22 @@ Remote.prototype.requestServerInfo = function(callback) {
* @return {Request} request
*/
Remote.prototype.requestLedger = function(ledger, options, callback) {
Remote.prototype.requestLedger = function(options, callback) {
// XXX This is a bad command. Some variants don't scale.
// XXX Require the server to be trusted.
//utils.assert(this.trusted);
var request = new Request(this, 'ledger');
if (ledger) {
// DEPRECATED: use .ledger_hash() or .ledger_index()
//console.log('request_ledger: ledger parameter is deprecated');
request.message.ledger = ledger;
}
switch (typeof options) {
case 'undefined': break;
case 'function':
callback = options;
break;
case 'object':
if (!options) break;
Object.keys(options).forEach(function(o) {
switch (o) {
case 'full':
@@ -946,21 +955,21 @@ Remote.prototype.requestLedger = function(ledger, options, callback) {
case 'accounts':
request.message[o] = true;
break;
case 'ledger_index':
case 'ledger_hash':
request.message[o] = options[o];
break;
case 'closed' :
case 'current' :
case 'validated' :
request.message.ledger_index = o;
break;
}
}, options);
break;
case 'function':
callback = options;
options = void(0);
break;
default:
//DEPRECATED
if (this.trace) {
log.info('request_ledger: full parameter is deprecated');
}
request.message.full = true;
request.ledgerSelect(options);
break;
}
@@ -1191,16 +1200,16 @@ Remote.prototype.requestTx = function(hash, callback) {
/**
* Account request abstraction
*
* @this Remote
* @api private
*/
Remote.accountRequest = function(type, account, accountIndex, ledger, peer, callback) {
Remote.accountRequest = function(type, account, ledger, peer, callback) {
if (typeof account === 'object') {
var options = account;
callback = accountIndex;
callback = ledger;
ledger = options.ledger;
accountIndex = options.account_index || options.accountIndex;
account = options.accountID || options.account;
account = options.account || options.accountID;
peer = options.peer;
}
@@ -1213,18 +1222,10 @@ Remote.accountRequest = function(type, account, accountIndex, ledger, peer, call
var request = new Request(this, type);
var account = UInt160.json_rewrite(account);
request.message.ident = account; //DEPRECATED;
request.message.account = account;
request.ledgerSelect(ledger);
if (typeof accountIndex === 'number') {
request.message.index = accountIndex;
}
if (!/^(undefined|function)$/.test(typeof ledger)) {
request.ledgerChoose(ledger);
}
if (!/^(undefined|function)$/.test(typeof peer)) {
if (UInt160.is_valid(peer)) {
request.message.peer = UInt160.json_rewrite(peer);
}
@@ -1237,6 +1238,7 @@ Remote.accountRequest = function(type, account, accountIndex, ledger, peer, call
* Request account_info
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* @param [Function] callback
* @return {Request}
*/
@@ -1250,6 +1252,7 @@ Remote.prototype.requestAccountInfo = function(account, callback) {
* Request account_currencies
*
* @param {String} ripple address
* @param [String|Number] ledger identifier
* @param [Function] callback
* @return {Request}
*/
@@ -1263,14 +1266,13 @@ Remote.prototype.requestAccountCurrencies = function(account, callback) {
* Request account_lines
*
* @param {String} ripple address
* @param {Number] sub-account index
* @param [String|Number] ledger
* @param [String|Number] ledger identifier
* @param [String] peer
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountLines = function(account, accountIndex, ledger, peer, callback) {
Remote.prototype.requestAccountLines = function(account, peer, callback) {
// XXX Does this require the server to be trusted?
//utils.assert(this.trusted);
var args = Array.prototype.concat.apply(['account_lines'], arguments);
@@ -1281,19 +1283,16 @@ Remote.prototype.requestAccountLines = function(account, accountIndex, ledger, p
* Request account_offers
*
* @param {String} ripple address
* @param {Number] sub-account index
* @param [String|Number] ledger
* @param [String] peer
* @param [String|Number] ledger identifier
* @param [Function] callback
* @return {Request}
*/
Remote.prototype.requestAccountOffers = function(account, accountIndex, ledger, callback) {
Remote.prototype.requestAccountOffers = function(account, callback) {
var args = Array.prototype.concat.apply(['account_offers'], arguments);
return Remote.accountRequest.apply(this, args);
};
/**
* Request account_tx
*
@@ -1309,9 +1308,6 @@ Remote.prototype.requestAccountOffers = function(account, accountIndex, ledger,
* @param [Number] offset, defaults to 0
* @param [Number] limit
*
* @param [Function] filter
* @param [Function] map
* @param [Function] reduce
* @param [Function] callback
* @return {Request}
*/
@@ -1331,6 +1327,10 @@ Remote.prototype.requestAccountTx = function(options, callback) {
options.ledger_index_max = options.max_ledger;
}
if (options.binary && options.parseBinary === void(0)) {
options.parseBinary = true;
}
Object.keys(options).forEach(function(o) {
switch (o) {
case 'account':
@@ -1350,101 +1350,32 @@ Remote.prototype.requestAccountTx = function(options, callback) {
}
}, options);
function propertiesFilter(obj, transaction) {
var properties = Object.keys(obj);
return function(transaction) {
var result = properties.every(function(property) {
return transaction.tx[property] === obj[property];
});
return result;
};
};
function parseBinaryTransaction(transaction) {
var tx = { validated: transaction.validated };
tx.meta = new SerializedObject(transaction.meta).to_json();
tx.tx = new SerializedObject(transaction.tx_blob).to_json();
tx.tx.ledger_index = transaction.ledger_index;
tx.tx.hash = Transaction.from_json(tx.tx).hash();
return tx;
};
function accountTxFilter(fn) {
if (typeof fn !== 'function') {
throw new Error('Missing filter function');
request.once('success', function(res) {
if (options.parseBinary) {
res.transactions = res.transactions.map(Remote.parseBinaryTransaction);
}
request.emit('transactions', res);
});
var self = this;
function filterHandler() {
var listeners = self.listeners('success');
self.removeAllListeners('success');
self.once('success', function(res) {
if (options.parseBinary) {
res.transactions = res.transactions.map(parseBinaryTransaction);
}
if (fn !== Boolean) {
res.transactions = res.transactions.filter(fn);
}
if (typeof options.map === 'function') {
res.transactions = res.transactions.map(options.map);
}
if (typeof options.reduce === 'function') {
res.transactions = res.transactions.reduce(options.reduce);
}
if (typeof options.pluck === 'string') {
res = res[options.pluck];
}
listeners.forEach(function(listener) {
listener.call(self, res);
});
});
};
this.once('request', filterHandler);
return this;
};
request.filter = accountTxFilter;
if (typeof options.parseBinary !== 'boolean') {
options.parseBinary = true;
}
if (options.binary || (options.map || options.reduce)) {
options.filter = options.filter || Boolean;
}
if (options.filter) {
switch (options.filter) {
case 'inbound':
request.filter(propertiesFilter({ Destination: options.account }));
break;
case 'outbound':
request.filter(propertiesFilter({ Account: options.account }));
break;
default:
if (typeof options.filter === 'object') {
options.filter = propertiesFilter(options.filter);
}
request.filter(options.filter);
}
}
request.callback(callback);
request.callback(callback, 'transactions');
return request;
};
/**
* @param {Object} transaction
* @return {Transaction}
*/
Remote.parseBinaryTransaction = function(transaction) {
var tx = { validated: transaction.validated };
tx.meta = new SerializedObject(transaction.meta).to_json();
tx.tx = new SerializedObject(transaction.tx_blob).to_json();
tx.tx.ledger_index = transaction.ledger_index;
tx.tx.hash = Transaction.from_json(tx.tx).hash();
return tx;
};
/**
* Request the overall transaction history.
*
@@ -1583,10 +1514,14 @@ Remote.prototype.requestSubmit = function(callback) {
* @api private
*/
Remote.prototype._serverPrepareSubscribe = function(callback) {
Remote.prototype._serverPrepareSubscribe = function(server, callback) {
var self = this;
var feeds = [ 'ledger', 'server' ];
if (typeof server === 'function') {
callback = server;
}
if (this._transaction_subs) {
feeds.push('transactions');
}
@@ -1607,16 +1542,17 @@ Remote.prototype._serverPrepareSubscribe = function(callback) {
self.emit('random', utils.hexToArray(message.random));
}
if (message.ledger_hash && message.ledger_index) {
self._ledger_time = message.ledger_time;
self._ledger_hash = message.ledger_hash;
self._ledger_current_index = message.ledger_index+1;
self.emit('ledger_closed', message);
}
self._handleLedgerClosed(message, server);
self.emit('subscribed');
};
request.on('error', function(err) {
if (self.trace) {
log.info('Initial server subscribe failed', err);
}
});
request.once('success', serverSubscribed);
self.emit('prepare_subscribe', request);
@@ -1657,6 +1593,7 @@ Remote.prototype.requestLedgerAccept = function(callback) {
/**
* Account root request abstraction
*
* @this Remote
* @api private
*/
@@ -1676,7 +1613,7 @@ Remote.accountRootRequest = function(type, responseFilter, account, ledger, call
var request = this.requestLedgerEntry('account_root');
request.accountRoot(account);
request.ledgerChoose(ledger);
request.ledgerSelect(ledger);
request.once('success', function(message) {
request.emit(type, responseFilter(message));
@@ -2036,7 +1973,7 @@ Remote.prepareCurrencies = function(currency) {
}
if (currency.hasOwnProperty('currency')) {
newCurrency.currency = Currency.json_rewrite(currency.currency);
newCurrency.currency = Currency.json_rewrite(currency.currency, {force_hex:true});
}
return newCurrency;
@@ -2214,65 +2151,40 @@ Remote.prototype.requestConnect = function(ip, port, callback) {
/**
* Create a Transaction
*
* @param {String} source
* @param {String} TransactionType
* @param {Object} options
* @param [Function] callback
* @return {Request}
* @return {Transaction}
*/
Remote.prototype.transaction =
Remote.prototype.createTransaction = function(source, options, callback) {
Remote.prototype.createTransaction = function(type, options) {
if (arguments.length === 0) {
// Fallback
return new Transaction(this);
}
assert.strictEqual(typeof type, 'string', 'TransactionType must be a string');
assert.strictEqual(typeof options, 'object', 'Transaction options must be an object');
var transaction = new Transaction(this);
var transactionTypes = {
payment: 'payment',
accountset: 'accountSet',
trustset: 'trustSet',
offercreate: 'offerCreate',
offercancel: 'offerCancel',
claim: 'claim',
passwordfund: 'passwordFund',
passwordset: 'passwordSet',
setregularkey: 'setRegularKey',
walletadd: 'walletAdd',
sign: 'sign'
payment: 'payment',
accountset: 'accountSet',
trustset: 'trustSet',
offercreate: 'offerCreate',
offercancel: 'offerCancel',
setregularkey: 'setRegularKey',
sign: 'sign'
};
var transactionType;
var transactionConstructor = transactionTypes[type.toLowerCase()];
switch (typeof source) {
case 'object':
if (typeof source.type !== 'string') {
throw new Error('Missing transaction type');
}
transactionType = transactionTypes[source.type.toLowerCase()];
if (!transactionType) {
throw new Error('Invalid transaction type: ' + transactionType);
}
transaction = transaction[transactionType](source);
break;
case 'string':
transactionType = transactionTypes[source.toLowerCase()];
if (!transactionType) {
throw new Error('Invalid transaction type: ' + transactionType);
}
transaction = transaction[transactionType](options);
break;
if (!transactionConstructor) {
throw new Error('Invalid transaction type: ' + type);
}
var lastArg = arguments[arguments.length - 1];
if (typeof lastArg === 'function') {
transaction.submit(lastArg);
}
return transaction;
return transaction[transactionConstructor](options);
};
/**
@@ -2285,7 +2197,7 @@ Remote.prototype.createTransaction = function(source, options, callback) {
*/
Remote.prototype.feeTx = function(units) {
var server = this._getServer();
var server = this.getServer();
if (!server) {
throw new Error('No connected servers');
@@ -2304,7 +2216,7 @@ Remote.prototype.feeTx = function(units) {
*/
Remote.prototype.feeTxUnit = function() {
var server = this._getServer();
var server = this.getServer();
if (!server) {
throw new Error('No connected servers');
@@ -2323,7 +2235,7 @@ Remote.prototype.feeTxUnit = function() {
*/
Remote.prototype.reserve = function(owner_count) {
var server = this._getServer();
var server = this.getServer();
if (!server) {
throw new Error('No connected servers');

View File

@@ -208,6 +208,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':
@@ -217,10 +224,10 @@ Request.prototype.ledgerSelect = function(ledger) {
break;
default:
if (isNaN(ledger)) {
this.message.ledger_hash = ledger;
} else if ((ledger = Number(ledger))) {
this.message.ledger_index = ledger;
if (Number(ledger)) {
this.message.ledger_index = Number(ledger);
} else if (/^[A-F0-9]+$/.test(ledger)) {
this.message.ledger_hash = ledger;
}
break;
}

View File

@@ -33,8 +33,8 @@ Seed.prototype.parse_json = function (j) {
// XXX Should actually always try and continue if it failed.
} else if (j[0] === 's') {
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
} else if (j.length === 32) {
this._value = this.parse_hex(j);
} else if (/^[0-9a-fA-f]{32}$/.test(j)) {
this.parse_hex(j);
// XXX Should also try 1751
} else {
this.parse_passphrase(j);
@@ -93,9 +93,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 +108,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 +127,9 @@ Seed.prototype.get_key = function (account) {
var sec;
var key_pair;
var max_loops = 1000; // TODO
do {
i = 0;
do {
@@ -135,15 +141,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

@@ -117,7 +117,7 @@ SerializedType.prototype.parse_varint = function (so) {
*/
function append_byte_array(so, val, bytes) {
if (!isNumber(val)) {
throw new Error('Value is not a number');
throw new Error('Value is not a number', bytes);
}
if (val < 0 || val >= Math.pow(256, bytes)) {
@@ -345,7 +345,7 @@ var STAmount = exports.Amount = new SerializedType({
// Next eight bits: offset/exponent
hi |= ((97 + amount._offset) & 0xff) << 22;
// Remaining 52 bits: mantissa
// Remaining 54 bits: mantissa
hi |= amount._value.shiftRight(32).intValue() & 0x3fffff;
lo = amount._value.intValue() & 0xffffffff;
}
@@ -625,7 +625,13 @@ function serialize(so, field_name, value) {
// 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]];
serialized_object_type.serialize(so, value);
try {
serialized_object_type.serialize(so, value);
} catch (e) {
e.message += ' (' + field_name + ')';
throw e;
}
}
//Take the serialized object, figure out what type/field it is, and return the parsing of that.

View File

@@ -76,7 +76,6 @@ function Server(remote, opts) {
this._lastLedgerClose = NaN;
this._score = 0;
this._scoreWeights = {
ledgerclose: 5,
response: 1
@@ -135,7 +134,9 @@ function Server(remote, opts) {
}
});
self._request(self._remote.requestServerInfo());
var serverInfoRequest = self._remote.requestServerInfo();
serverInfoRequest.on('error', function() { });
self._request(serverInfoRequest);
});
};
@@ -185,7 +186,7 @@ Server.websocketConstructor = function() {
Server.prototype._setState = function(state) {
if (state !== this._state) {
if (this._remote.trace) {
log.info(this.getHostID(), 'set_state:', state);
log.info(this.getServerID(), 'set_state:', state);
}
this._state = state;
@@ -229,7 +230,7 @@ Server.prototype._checkActivity = function() {
if (delta > (1000 * 25)) {
if (this._remote.trace) {
log.info(this.getHostID(), 'reconnect: activity delta:', delta);
log.info(this.getServerID(), 'reconnect: activity delta:', delta);
}
this.reconnect();
}
@@ -275,7 +276,7 @@ Server.prototype._updateScore = function(type, data) {
if (this._score > 1e3) {
if (this._remote.trace) {
log.info(this.getHostID(), 'reconnect: score:', this._score);
log.info(this.getServerID(), 'reconnect: score:', this._score);
}
this.reconnect();
}
@@ -354,7 +355,7 @@ Server.prototype.reconnect = function() {
if (this.isConnected()) {
this.once('disconnect', reconnect);
this.disconnect();
} else {
} else {
reconnect();
}
}
@@ -408,7 +409,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));
}
};
@@ -623,30 +624,53 @@ Server.prototype._handlePathFind = function(message) {
* Handle subscription response messages. Subscription response
* messages indicate that a connection to the server is ready
*
* @param {Object} message
* @api private
*/
Server.prototype._handleResponseSubscribe = function(message) {
if (Server.isLoadStatus(message)) {
this._load_base = message.load_base || 256;
this._load_factor = message.load_factor || 256;
this._fee_ref = message.fee_ref;
this._fee_base = message.fee_base;
this._reserve_base = message.reserve_base;
this._reserve_inc = message.reserve_inc;
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.onlineStates.indexOf(message.server_status))) {
if (Server.isLoadStatus(message)) {
this._load_base = message.load_base || 256;
this._load_factor = message.load_factor || 256;
this._fee_ref = message.fee_ref || 10;
this._fee_base = message.fee_base || 10;
this._reserve_base = message.reserve_base;
this._reserve_inc = message.reserve_inc;
}
if (~Server.onlineStates.indexOf(message.server_status)) {
this._setState('online');
}
};
/**
* Check that server message indicates that server has complete ledger history
*
* @param {Object} message
* @return {Boolean}
*/
Server.hasFullLedgerHistory = function(message) {
return (typeof message === 'object')
&& (message.server_status === 'full')
&& (typeof message.validated_ledgers === 'string')
&& (message.validated_ledgers.split('-').length === 2);
};
/**
* Check that received message from rippled is valid
*
* @api private
* @param {Object} message
* @return {Boolean}
*/
Server.isValidMessage = function(message) {
@@ -655,14 +679,15 @@ Server.isValidMessage = function(message) {
};
/**
* Check that received serverStatus message contains
* load status information
* Check that received serverStatus message contains load status information
*
* @api private
* @param {Object} message
* @return {Boolean}
*/
Server.isLoadStatus = function(message) {
return (typeof message.load_base === 'number')
return (typeof message === 'object')
&& (typeof message.load_base === 'number')
&& (typeof message.load_factor === 'number');
};
@@ -683,10 +708,10 @@ Server.prototype._sendMessage = function(message) {
};
/**
* Submit a Request object.
* Submit a Request object
*
* Requests are indexed by message ID, which is repeated
* in the response from rippled WebSocket server
* Requests are indexed by message ID, which is repeated in the response from
* rippled WebSocket server
*
* @param {Request} request
* @api private

View File

@@ -60,7 +60,7 @@ function Transaction(remote) {
var self = this;
var remote = remote || { };
var remote = remote || void(0);
this.remote = remote;
@@ -69,7 +69,7 @@ function Transaction(remote) {
this._secret = void(0);
this._build_path = false;
this._maxFee = this.remote.max_fee;
this._maxFee = (typeof remote === 'object') ? this.remote.max_fee : void(0);
this.state = 'unsubmitted';
this.finalized = false;
@@ -167,6 +167,9 @@ Transaction.set_clear_flags = {
}
};
Transaction.MEMO_TYPES = {
};
Transaction.formats = require('./binformat').tx;
Transaction.prototype.consts = {
@@ -238,7 +241,7 @@ Transaction.prototype.finalize = function(message) {
};
Transaction.prototype._accountSecret = function(account) {
return this.remote.secrets[account];
return this.remote ? this.remote.secrets[account] : void(0);
};
/**
@@ -263,6 +266,10 @@ Transaction.prototype.feeUnits = function() {
*/
Transaction.prototype._computeFee = function() {
if (!this.remote) {
return void(0);
}
var servers = this.remote._servers;
var fees = [ ];
@@ -477,7 +484,7 @@ Transaction.prototype.clientID = function(id) {
};
Transaction.prototype.lastLedger = function(sequence) {
if (typeof sequence === 'number') {
if (typeof sequence === 'number' && isFinite(sequence)) {
this._setLastLedger = true;
this.tx_json.LastLedgerSequence = sequence;
}
@@ -595,6 +602,48 @@ Transaction.prototype.setFlags = function(flags) {
return this;
};
/**
* Add a Memo to transaction. Memos can be used as key-value,
* using the MemoType as a key
*
* @param {String} type
* @param {String} data
*/
Transaction.prototype.addMemo = function(type, data) {
if (!/(undefined|string)/.test(typeof type)) {
throw new Error('MemoType must be a string');
}
if (!/(undefined|string)/.test(typeof data)) {
throw new Error('MemoData must be a string');
}
function toHex(str) {
return sjcl.codec.hex.fromBits(sjcl.codec.utf8String.toBits(str));
};
var memo = { };
if (type) {
if (Transaction.MEMO_TYPES[type]) {
//XXX Maybe in the future we want a schema validator for
//memo types
memo.MemoType = Transaction.MEMO_TYPES[type];
} else {
memo.MemoType = toHex(type);
}
}
if (data) {
memo.MemoData = toHex(data);
}
this.tx_json.Memos = (this.tx_json.Memos || []).concat({ Memo: memo });
return this;
};
// Options:
// .domain() NYI
// .flags()
@@ -853,6 +902,10 @@ Transaction.prototype.submit = function(callback) {
var account = this.tx_json.Account;
if (!this.remote) {
return this.emit('error', new Error('No remote found'));
}
if (!UInt160.is_valid(account)) {
return this.emit('error', new RippleError('tejInvalidAccount', 'Account is missing or invalid'));
}

View File

@@ -20,6 +20,7 @@ function TransactionManager(account) {
this._remote = account._remote;
this._nextSequence = void(0);
this._maxFee = this._remote.max_fee;
this._maxAttempts = this._remote.max_attempts;
this._submissionTimeout = this._remote._submission_timeout;
this._pending = new PendingQueue();
@@ -344,7 +345,7 @@ TransactionManager.prototype._request = function(tx) {
var self = this;
var remote = this._remote;
if (tx.attempts > 10) {
if (tx.attempts > this._maxAttempts) {
return tx.emit('error', new RippleError('tejAttemptsExceeded'));
}

View File

@@ -575,8 +575,6 @@ VaultClient.prototype.generateDeviceID = function () {
VaultClient.prototype.resendEmail = blobClient.resendEmail;
VaultClient.prototype.updateProfile = blobClient.updateProfile;
VaultClient.prototype.recoverBlob = blobClient.recoverBlob;
VaultClient.prototype.deleteBlob = blobClient.deleteBlob;
@@ -585,5 +583,11 @@ VaultClient.prototype.requestToken = blobClient.requestToken;
VaultClient.prototype.verifyToken = blobClient.verifyToken;
VaultClient.prototype.getAttestation = blobClient.getAttestation;
VaultClient.prototype.updateAttestation = blobClient.updateAttestation;
VaultClient.prototype.getAttestationSummary = blobClient.getAttestationSummary;
//export by name
exports.VaultClient = VaultClient;

8
src/js/ripple/wallet.js Normal file
View File

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

View File

@@ -16,83 +16,241 @@ 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.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');
});
});
describe('from_human', function() {
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('0.1 XRP', function() {
assert.strictEqual(Amount.from_human("0.1 XRP").to_text_full(), '0.1/XRP');
});
it('0.1 XRP human', function() {
assert.strictEqual(Amount.from_human("0.1 XRP").to_human_full(), '0.1/XRP');
});
it('0.1 USD', function() {
assert.strictEqual(Amount.from_human("0.1 USD").to_text_full(), '0.1/USD/NaN');
});
it('0.1 USD human', function() {
assert.strictEqual(Amount.from_human("0.1 USD").to_human_full(), '0.1/USD/NaN');
});
it('10000 USD', function() {
assert.strictEqual(Amount.from_human("10000 USD").to_text_full(), '10000/USD/NaN');
});
it('10000 USD human', function() {
assert.strictEqual(Amount.from_human("10000 USD").to_human_full(), '10,000/USD/NaN');
});
it('USD 10000', function() {
assert.strictEqual(Amount.from_human("USD 10000").to_text_full(), '10000/USD/NaN');
});
it('USD 10000 human', function() {
assert.strictEqual(Amount.from_human("USD 10000").to_human_full(), '10,000/USD/NaN');
});
it('12345.6789 XAU', function() {
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_text_full(), '12345.6789/XAU/NaN');
});
it('12345.6789 XAU human', function() {
assert.strictEqual(Amount.from_human("12345.6789 XAU").to_human_full(), '12,345.6789/XAU/NaN');
});
it('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000', function() {
assert.strictEqual(Amount.from_human("12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000").to_text_full(), '12345.6789/XAU (-0.5%pa)/NaN');
});
it('12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000 human', function() {
assert.strictEqual(Amount.from_human("12345.6789 015841551A748AD2C1F76FF6ECB0CCCD00000000").to_human_full(), '12,345.6789/XAU (-0.5%pa)/NaN');
});
it('12345.6789 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("12345.6789 0000000000000000000000005553440000000000").to_text_full(), '12345.6789/USD/NaN');
});
it('12345.6789 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("12345.6789 0000000000000000000000005553440000000000").to_human_full(), '12,345.6789/USD/NaN');
});
it('10 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("10 0000000000000000000000005553440000000000").to_text_full(), '10/USD/NaN');
});
it('10 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("10 0000000000000000000000005553440000000000").to_human_full(), '10/USD/NaN');
});
it('100 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("100 0000000000000000000000005553440000000000").to_text_full(), '100/USD/NaN');
});
it('100 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("100 0000000000000000000000005553440000000000").to_human_full(), '100/USD/NaN');
});
it('1000 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("1000 0000000000000000000000005553440000000000").to_text_full(), '1000/USD/NaN');
});
it('1000 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("1000 0000000000000000000000005553440000000000").to_human_full(), '1,000/USD/NaN');
});
it('-100 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("-100 0000000000000000000000005553440000000000").to_text_full(), '-100/USD/NaN');
});
it('-100 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("-100 0000000000000000000000005553440000000000").to_human_full(), '-100/USD/NaN');
});
it('-1000 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("-1000 0000000000000000000000005553440000000000").to_text_full(), '-1000/USD/NaN');
});
it('-1000 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("-1000 0000000000000000000000005553440000000000").to_human_full(), '-1,000/USD/NaN');
});
it('-1000.001 0000000000000000000000005553440000000000', function() {
assert.strictEqual(Amount.from_human("-1000.001 0000000000000000000000005553440000000000").to_text_full(), '-1000.001/USD/NaN');
});
it('-1000.001 0000000000000000000000005553440000000000 human', function() {
assert.strictEqual(Amount.from_human("-1000.001 0000000000000000000000005553440000000000").to_human_full(), '-1,000.001/USD/NaN');
});
it('XAU 12345.6789', function() {
assert.strictEqual(Amount.from_human("XAU 12345.6789").to_text_full(), '12345.6789/XAU/NaN');
});
it('XAU 12345.6789 human', function() {
assert.strictEqual(Amount.from_human("XAU 12345.6789").to_human_full(), '12,345.6789/XAU/NaN');
});
it('101 12345.6789', function() {
assert.strictEqual(Amount.from_human("101 12345.6789").to_text_full(), '12345.6789/101/NaN');
});
it('101 12345.6789 human', function() {
assert.strictEqual(Amount.from_human("101 12345.6789").to_human_full(), '12,345.6789/101/NaN');
});
it('12345.6789 101', function() {
assert.strictEqual(Amount.from_human("12345.6789 101").to_text_full(), '12345.6789/101/NaN');
});
it('12345.6789 101 human', function() {
assert.strictEqual(Amount.from_human("12345.6789 101").to_human_full(), '12,345.6789/101/NaN');
});
});
describe('from_json', function() {
it('1 XRP', function() {
assert.strictEqual(Amount.from_json("1/XRP").to_text_full(), "1/XRP/NaN");
});
it('1 XRP human', function() {
assert.strictEqual(Amount.from_json("1/XRP").to_human_full(), "1/XRP/NaN");
});
});
describe('from_number', function() {
it('Number 1', function() {
assert.strictEqual(Amount.from_number(1).to_text_full(), '1/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Number 1 human', function() {
assert.strictEqual(Amount.from_number(1).to_human_full(), '1/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Number 2', function() {
assert.strictEqual(Amount.from_number(2).to_text_full(), '2/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Number 2 human', function() {
assert.strictEqual(Amount.from_number(2).to_human_full(), '2/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply 2 "1" with 3 "1", by product_human', function () {
assert.strictEqual(Amount.from_number(2).product_human(Amount.from_number(3)).to_text_full(), '6/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply 2 "1" with 3 "1", by product_human human', function () {
assert.strictEqual(Amount.from_number(2).product_human(Amount.from_number(3)).to_human_full(), '6/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply 3 USD with 3 "1"', function () {
assert.strictEqual(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_number(3)).to_text_full(), '9/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply 3 USD with 3 "1" human', function () {
assert.strictEqual(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_number(3)).to_human_full(), '9/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply -1 "1" with 3 USD', function () {
assert.strictEqual(Amount.from_number(-1).multiply(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full(), '-3/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply -1 "1" with 3 USD human', function () {
assert.strictEqual(Amount.from_number(-1).multiply(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full(), '-3/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply -1 "1" with 3 USD, by product_human', function () {
assert.strictEqual(Amount.from_number(-1).product_human(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full(), '-3/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
it('Multiply -1 "1" with 3 USD, by product_human human', function () {
assert.strictEqual(Amount.from_number(-1).product_human(Amount.from_json('3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full(), '-3/1/rrrrrrrrrrrrrrrrrrrrBZbvji');
});
});
describe('text_full_rewrite', function() {
it('Number 1', function() {
@@ -162,12 +320,21 @@ describe('Amount', function() {
it('parse dem', function() {
assert.strictEqual(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('parse dem human', function() {
assert.strictEqual(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('parse dem', function() {
assert.strictEqual(Amount.from_json('10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('parse dem human', function() {
assert.strictEqual(Amount.from_json('10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Parse 800/USD/mtgox', function () {
assert.strictEqual('800/USD/'+config.accounts['mtgox'].account, Amount.from_json('800/USD/mtgox').to_text_full());
});
it('Parse 800/USD/mtgox human', function () {
assert.strictEqual('800/USD/'+config.accounts['mtgox'].account, Amount.from_json('800/USD/mtgox').to_human_full());
});
it('Parse native 0', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').to_text_full());
});
@@ -211,7 +378,52 @@ describe('Amount', function() {
assert.strictEqual('0/111/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/111/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full());
});
it('Parse 0.0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
assert.strictEqual('0/XRP/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full());
assert.strictEqual('0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_text_full());
});
it('Parse native 0 human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').to_human_full());
});
it('Parse native 0.0 human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0.0').to_human_full());
});
it('Parse native -0 human', function () {
assert.strictEqual('0/XRP', Amount.from_json('-0').to_human_full());
});
it('Parse native -0.0 human', function () {
assert.strictEqual('0/XRP', Amount.from_json('-0.0').to_human_full());
});
it('Parse native 1000 human', function () {
assert.strictEqual('0.001/XRP', Amount.from_json('1000').to_human_full());
});
it('Parse native 12.3 human', function () {
assert.strictEqual('12.3/XRP', Amount.from_json('12.3').to_human_full());
});
it('Parse native -12.3 human', function () {
assert.strictEqual('-12.3/XRP', Amount.from_json('-12.3').to_human_full());
});
it('Parse 123./USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('123./USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse 12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('12,300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse 12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse 1.2300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('1.23/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('1.2300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse -0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse -0.0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-0.0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse 0.0/111/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('0/111/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/111/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
it('Parse 0.0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh human', function () {
assert.strictEqual('0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/12D/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').to_human_full());
});
});
describe('Amount to_json', function() {
@@ -370,6 +582,144 @@ describe('Amount', function() {
it('Divide EUR by XRP, neg, <1', function () {
assert.strictEqual('-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('2000')).to_text_full());
});
it('Negate native 123 human', function () {
assert.strictEqual('-0.000123/XRP', Amount.from_json('123').negate().to_human_full());
});
it('Negate native -123 human', function () {
assert.strictEqual('0.000123/XRP', Amount.from_json('-123').negate().to_human_full());
});
it('Negate non-native 123 human', function () {
assert.strictEqual('-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').negate().to_human_full());
});
it('Negate non-native -123 human', function () {
assert.strictEqual('123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').negate().to_human_full());
});
it('Clone non-native -123 human', function () {
assert.strictEqual('-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').clone().to_human_full());
});
it('Add XRP to XRP human', function () {
assert.strictEqual('0.0002/XRP', Amount.from_json('150').add(Amount.from_json('50')).to_human_full());
});
it('Add USD to USD human', function () {
assert.strictEqual('200.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').add(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Add 0 USD to 1 USD human', function() {
assert.strictEqual('1' , Amount.from_json('1/USD').add('0/USD').to_human());
});
it('Subtract USD from USD human', function() {
assert.strictEqual('99.52/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('150.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').subtract(Amount.from_json('50.5/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply 0 XRP with 0 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').multiply(Amount.from_json('0')).to_human_full());
});
it('Multiply 0 USD with 0 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('0')).to_human_full());
});
it('Multiply 0 XRP with 0 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').multiply(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply 1 XRP with 0 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('1').multiply(Amount.from_json('0')).to_human_full());
});
it('Multiply 1 USD with 0 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('0')).to_human_full());
});
it('Multiply 1 XRP with 0 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('1').multiply(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply 0 XRP with 1 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').multiply(Amount.from_json('1')).to_human_full());
});
it('Multiply 0 USD with 1 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('1')).to_human_full());
});
it('Multiply 0 XRP with 1 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').multiply(Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.equal('0.002/XRP', Amount.from_json('200').multiply(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.strictEqual('0.2/XRP', Amount.from_json('20000').multiply(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.strictEqual('20/XRP', Amount.from_json('2000000').multiply(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD, neg human', function () {
assert.strictEqual('-0.002/XRP', Amount.from_json('200').multiply(Amount.from_json('-10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD, neg, frac human', function () {
assert.strictEqual('-0.222/XRP', Amount.from_json('-6000').multiply(Amount.from_json('37/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply USD with USD human', function () {
assert.strictEqual('20,000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply USD with USD human', function () {
assert.strictEqual('200,000,000,000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('100000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with USD, result < 1 human', function () {
assert.strictEqual('100,000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with USD, neg human', function () {
assert.strictEqual('-48,000,000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-24000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with USD, neg, <1 human', function () {
assert.strictEqual('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0.1/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('-1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with XRP, factor < 1 human', function () {
assert.strictEqual('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('2000')).to_human_full());
});
it('Multiply EUR with XRP, neg human', function () {
assert.strictEqual('-500/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('5')).to_human_full());
});
it('Multiply EUR with XRP, neg, <1 human', function () {
assert.strictEqual('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').multiply(Amount.from_json('2000')).to_human_full());
});
it('Multiply XRP with XRP human', function () {
assert.strictEqual('0.0001/XRP', Amount.from_json('10').multiply(Amount.from_json('10')).to_human_full());
});
it('Divide XRP by USD human', function () {
assert.strictEqual('0.00002/XRP', Amount.from_json('200').divide(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide XRP by USD human', function () {
assert.strictEqual('0.002/XRP', Amount.from_json('20000').divide(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide XRP by USD human', function () {
assert.strictEqual('0.2/XRP', Amount.from_json('2000000').divide(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide XRP by USD, neg human', function () {
assert.strictEqual('-0.00002/XRP', Amount.from_json('200').divide(Amount.from_json('-10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide XRP by USD, neg, frac human', function () {
assert.strictEqual('-0.000162/XRP', Amount.from_json('-6000').divide(Amount.from_json('37/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide USD by USD human', function () {
assert.strictEqual('200/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide USD by USD, fractional human', function () {
assert.strictEqual('57,142.85714285714/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('35/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide USD by USD human', function () {
assert.strictEqual('20/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('100000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide EUR by USD, factor < 1 human', function () {
assert.strictEqual('0.1/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide EUR by USD, neg human', function () {
assert.strictEqual('-12/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-24000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide EUR by USD, neg, <1 human', function () {
assert.strictEqual('-0.1/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('-1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Divide EUR by XRP, result < 1 human', function () {
assert.strictEqual('0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('2000')).to_human_full());
});
it('Divide EUR by XRP, neg human', function () {
assert.strictEqual('-20/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('5')).to_human_full());
});
it('Divide EUR by XRP, neg, <1 human', function () {
assert.strictEqual('-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').divide(Amount.from_json('2000')).to_human_full());
});
it('Divide by zero should throw', function() {
assert.throws(function() {
Amount.from_json(1).divide(Amount.from_json(0));
@@ -607,12 +957,87 @@ describe('Amount', function() {
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');
});
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());
});
it('Multiply 0 USD with 0 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('0')).to_human_full());
});
it('Multiply 0 XRP with 0 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply 1 XRP with 0 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('1').product_human(Amount.from_json('0')).to_human_full());
});
it('Multiply 1 USD with 0 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('0')).to_human_full());
});
it('Multiply 1 XRP with 0 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('1').product_human(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply 0 XRP with 1 XRP human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('1')).to_human_full());
});
it('Multiply 0 USD with 1 XRP human', function () {
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('1')).to_human_full());
});
it('Multiply 0 XRP with 1 USD human', function () {
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.equal('0.002/XRP', Amount.from_json('200').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.strictEqual('0.2/XRP', Amount.from_json('20000').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD human', function () {
assert.strictEqual('20/XRP', Amount.from_json('2000000').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD, neg human', function () {
assert.strictEqual('-0.002/XRP', Amount.from_json('200').product_human(Amount.from_json('-10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply XRP with USD, neg, frac human', function () {
assert.strictEqual('-0.222/XRP', Amount.from_json('-6000').product_human(Amount.from_json('37/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply USD with USD human', function () {
assert.strictEqual('20,000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply USD with USD human', function () {
assert.strictEqual('200,000,000,000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('100000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with USD, result < 1 human', function () {
assert.strictEqual('100,000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full());
});
it('Multiply EUR with USD, neg human', function () {
assert.strictEqual(Amount.from_json('-24000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full(), '-48,000,000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply EUR with USD, neg, <1 human', function () {
assert.strictEqual(Amount.from_json('0.1/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('-1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_human_full(), '-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply EUR with XRP, factor < 1 human', function () {
assert.strictEqual(Amount.from_json('0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000')).to_human_full(), '0.0001/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply EUR with XRP, neg human', function () {
assert.strictEqual(Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('5')).to_human_full(), '-0.0005/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply EUR with XRP, neg, <1 human', function () {
assert.strictEqual(Amount.from_json('-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000')).to_human_full(), '-0.0001/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Multiply XRP with XRP human', 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');
});
});
describe('ratio_human', function() {
it('Divide USD by XAU (dem)', function () {
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').ratio_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '201.0049931765529/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Divide USD by XAU (dem) human', function () {
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').ratio_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_human_full(), '201.0049931765529/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
describe('_invert', function() {
@@ -625,6 +1050,15 @@ describe('Amount', function() {
it('Invert 0.02', function () {
assert.strictEqual(Amount.from_json('0.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_text_full(), '50/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Invert 1 human', function () {
assert.strictEqual(Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_human_full(), '1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Invert 20 human', function () {
assert.strictEqual(Amount.from_json('20/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_human_full(), '0.05/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Invert 0.02 human', function () {
assert.strictEqual(Amount.from_json('0.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_human_full(), '50/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
describe('from_quality', function() {
@@ -658,6 +1092,36 @@ describe('Amount', function() {
it('USD/XAU(dem) inverse', function () {
assert.strictEqual(Amount.from_quality('CDFD3AFB2F8C5DBEF75B081F7C957FF5509563266F28F36C5704A0FB0BAD8800', '015841551A748AD2C1F76FF6ECB0CCCD00000000', 'rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN', {inverse: true, base_currency: 'USD', reference_date: 443845330 + 31535000}).to_text_full(), '0.007675186123263489/XAU (-0.5%pa)/rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN');
});
it('BTC/XRP human', function () {
assert.strictEqual(Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'BTC'}).to_human_full(), '44,970/XRP');
});
it('BTC/XRP inverse human', function () {
assert.strictEqual(Amount.from_quality('37AAC93D336021AE94310D0430FFA090F7137C97D473488C4A0918D0DEF8624E', 'XRP', NaN, {inverse: true, base_currency: 'BTC'}).to_human_full(), '39,053.954453/XRP');
});
it('XRP/USD human', function () {
assert.strictEqual(Amount.from_quality('DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D05DCAA8FE12000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {base_currency: 'XRP'}).to_human_full(), '0.0165/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
});
it('XRP/USD inverse human', function () {
assert.strictEqual(Amount.from_quality('4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C22A840E27DCA9B', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {inverse: true, base_currency: 'XRP'}).to_human_full(), '0.010251/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
});
it('BTC/USD human', function () {
assert.strictEqual(Amount.from_quality('6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC9858038D7EA4C68000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {base_currency: 'BTC'}).to_human_full(), '1,000/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
});
it('BTC/USD inverse human', function () {
assert.strictEqual(Amount.from_quality('20294C923E80A51B487EB9547B3835FD483748B170D2D0A455071AFD498D0000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {inverse: true, base_currency: 'BTC'}).to_human_full(), '0.5/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
});
it('XAU(dem)/XRP human', function () {
assert.strictEqual(Amount.from_quality('587322CCBDE0ABD01704769A73A077C32FB39057D813D4165F1FF973CAF997EF', 'XRP', NaN, {base_currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000', reference_date: 443845330 + 31535000}).to_human_full(), '90,452.246928/XRP');
});
it('XAU(dem)/XRP inverse human', function () {
assert.strictEqual(Amount.from_quality('F72C7A9EAE4A45ED1FB547AD037D07B9B965C6E662BEBAFA4A03F2A976804235', 'XRP', NaN, {inverse: true, base_currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000', reference_date: 443845330 + 31535000}).to_human_full(), '90,442.196677/XRP');
});
it('USD/XAU(dem) human', function () {
assert.strictEqual(Amount.from_quality('4743E58E44974B325D42FD2BB683A6E36950F350EE46DD3A521B644B99782F5F', '015841551A748AD2C1F76FF6ECB0CCCD00000000', 'rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN', {base_currency: 'USD', reference_date: 443845330 + 31535000}).to_human_full(), '0.007710100231303007/XAU (-0.5%pa)/rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN');
});
it('USD/XAU(dem) inverse human', function () {
assert.strictEqual(Amount.from_quality('CDFD3AFB2F8C5DBEF75B081F7C957FF5509563266F28F36C5704A0FB0BAD8800', '015841551A748AD2C1F76FF6ECB0CCCD00000000', 'rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN', {inverse: true, base_currency: 'USD', reference_date: 443845330 + 31535000}).to_human_full(), '0.007675186123263489/XAU (-0.5%pa)/rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN');
});
});
describe('apply interest', function() {
@@ -679,5 +1143,23 @@ describe('Amount', function() {
demAmount.set_issuer("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
assert.strictEqual(demAmount.to_text_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it ('from_json apply interest 10 XAU human', function() {
var demAmount = Amount.from_json('10/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(demAmount.to_human_full(), '10/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
demAmount = demAmount.applyInterest(459990264);
assert.strictEqual(demAmount.to_human_full(), '9.294949401870435/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it ('from_json apply interest XAU human', function() {
var demAmount = Amount.from_json('1235.5/0158415500000000C1F76FF6ECB0BAC600000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(demAmount.to_human_full(), '1,235.5/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
demAmount = demAmount.applyInterest(459990264);
assert.strictEqual(demAmount.to_human_full(), '1,148.390998601092/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it ('from_human with reference date human', function() {
var demAmount = Amount.from_human('10 0158415500000000C1F76FF6ECB0BAC600000000', {reference_date:459990264});
demAmount.set_issuer("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
assert.strictEqual(demAmount.to_human_full(), '10.75853086191915/XAU (-0.5%pa)/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
});

View File

@@ -40,8 +40,8 @@ describe('Currency', function() {
});
it('from_json("1D2").to_human()', function() {
var r = currency.from_json("1D2");
assert(!r.is_valid());
assert.strictEqual('XRP', r.to_json());
assert(r.is_valid());
assert.strictEqual('1D2', r.to_json());
});
it('from_json("XAU").to_json() hex', function() {
var r = currency.from_json("XAU");
@@ -82,6 +82,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() {
@@ -111,8 +114,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 +151,7 @@ describe('Currency', function() {
var cur = currency.from_json("TIM");
assert.strictEqual(cur.to_human({full_name: null}), "TIM");
});
});
describe('from_hex', function() {

View File

@@ -82,8 +82,17 @@ describe('Message', function(){
});
it('should throw an error if given an invalid secret key', function(){
// Annoyingly non hex can be fed to the BigInteger(s, 16) constructor and
// it will parse as a number. Before the commit of this comment, this test
// involved a fixture of 32 chars, which was assumed to be hex. The test
// passed, but for the wrong wreasons. There was a bug in Seed.parse_json.
var secret_string = 'badsafRpB5euNL52PZPTSqrE9gvuFwTC';
// Seed.from_json only creates invalid seeds from empty strings or invalid
// base58 starting with an s, which it tries to base 58 decode/check sum.
// The rest will be assumed to be a passphrase.
// This is a bad b58 seed
var secret_string = 'sbadsafRpB5euNL52PZPTSqrE9gvuFwTC';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){

View File

@@ -21,6 +21,20 @@ describe('OrderBook', function() {
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
});
book = new Remote().createOrderBook({
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_gets: 'BTC',
currency_pays: 'XRP'
});
assert.deepEqual(book.toJSON(), {
taker_gets: {
currency: Currency.from_json('BTC').to_hex(),
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
taker_pays: {
currency: Currency.from_json('XRP').to_hex()
},
});
});
it('Check orderbook validity', function() {
@@ -51,26 +65,16 @@ describe('OrderBook', function() {
currency_pays: 'BTC'
});
var requestedTransferRate = false;
var requestedOffers = false;
book.subscribeTransactions = function() {
assert(requestedTransferRate);
assert(requestedOffers);
done();
};
book.requestTransferRate = function(callback) {
requestedTransferRate = true;
};
book.requestOffers = function() {
book.requestOffers = function(callback) {
requestedOffers = true;
var em = new process.EventEmitter();
setImmediate(function() {
em.emit('success', { offers: [ ] });
});
return em;
callback();
};
book.subscribe();
@@ -272,7 +276,6 @@ describe('OrderBook', function() {
assert.deepEqual(request.message, {
command: 'account_info',
id: void(0),
ident: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
account: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
request.emit('success', {
@@ -339,16 +342,21 @@ describe('OrderBook', function() {
value: '100',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
},
TakerPays: '123456'
};
book.setFundedAmount(offer, '100.1234');
assert.deepEqual(offer, {
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
is_fully_funded: true
});
taker_pays_funded: '123456'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - unfunded', function() {
@@ -359,62 +367,113 @@ describe('OrderBook', function() {
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '123456',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '99');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '99',
taker_pays_funded: '122221.44'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
taker_pays_funded: '100.1234'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'USD'
});
var offer = {
TakerGets: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
TakerPays: '123'
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '100',
taker_pays_funded: '122.8484050681459'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - zero funds', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: {
value: '100',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
},
TakerPays: '123456'
};
book.setFundedAmount(offer, '99');
book.setFundedAmount(offer, '0');
assert.deepEqual(offer, {
TakerGets: offer.TakerGets,
taker_gets_funded: '99',
is_fully_funded: false
});
});
it('Set funded amount - native currency - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100'
};
book.setFundedAmount(offer, '100');
assert.deepEqual(offer, {
TakerGets: '100',
taker_gets_funded: '100',
is_fully_funded: true
});
});
it('Set funded amount - native currency - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100'
};
book.setFundedAmount(offer, '99');
assert.deepEqual(offer, {
TakerGets: '100',
taker_gets_funded: '99',
is_fully_funded: false
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '0',
taker_pays_funded: '0'
});
});
@@ -686,7 +745,8 @@ describe('OrderBook', function() {
});
var meta = new Meta({
AffectedNodes: [{
AffectedNodes: [
{
ModifiedNode: {
FinalFields: {
Balance: {
@@ -720,7 +780,43 @@ describe('OrderBook', function() {
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
PreviousTxnLgrSeq: 343570
}
}]
},
{
ModifiedNode: {
FinalFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '-10'
},
Flags: 131072,
HighLimit: {
currency: 'USD',
issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH',
value: '100'
},
HighNode: '0000000000000000',
LowLimit: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '0'
},
LowNode: '0000000000000000'
},
LedgerEntryType: 'RippleState',
LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959',
PreviousFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '0'
}
},
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
PreviousTxnLgrSeq: 343570
}
}
]
});
assert.deepEqual(book.getBalanceChange(meta.getNodes()[0]), {
@@ -728,6 +824,12 @@ describe('OrderBook', function() {
balance: '10',
isValid: true
});
assert.deepEqual(book.getBalanceChange(meta.getNodes()[1]), {
account: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH',
balance: '10',
isValid: true
});
});
it('Get balance change - native currency', function() {
@@ -899,7 +1001,8 @@ describe('OrderBook', function() {
book.on('offer_funds_changed', function(offer, previousFunds, newFunds) {
assert.strictEqual(previousFunds, '100');
assert.strictEqual(newFunds, '10');
assert.strictEqual(newFunds, offer.taker_gets_funded);
assert.notStrictEqual(previousFunds, newFunds);
switch (++receivedFundsChangedEvents) {
case 1:
assert(!offer.is_fully_funded);
@@ -1033,7 +1136,8 @@ describe('OrderBook', function() {
book.on('offer_funds_changed', function(offer, previousFunds, newFunds) {
assert.strictEqual(previousFunds, '100');
assert.strictEqual(newFunds, '25');
assert.strictEqual(newFunds, offer.taker_gets_funded);
assert.notStrictEqual(previousFunds, newFunds);
switch (++receivedFundsChangedEvents) {
case 1:
assert(!offer.is_fully_funded);
@@ -1054,4 +1158,396 @@ describe('OrderBook', function() {
done();
});
});
it('Update funded amounts - no affected account', function(done) {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency_pays: 'USD'
});
var message = {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Balance: '25',
Flags: 0,
OwnerCount: 1,
Sequence: 2
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05',
PreviousFields: {
Balance: '100',
OwnerCount: 0,
Sequence: 1
},
PreviousTxnID: 'B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09',
PreviousTxnLgrSeq: 16154
}
}]
})
};
book._synchronized = true;
book._offers = [
{
Account: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124AF94ED1781B',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CA',
PreviousTxnID: '51C64E0B300E9C0E877BA3E79B4ED1DBD5FDDCE58FA1A8FDA5F8DDF139787A24',
PreviousTxnLgrSeq: 8265275,
Sequence: 1138918,
TakerGets: '50',
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '5'
},
index: 'DC003E09AD1306FBBD1957C955EE668E429CC85B0EC0EC17297F6676E6108DE7',
owner_funds: '162110617177',
quality: '0.000000005148984210454555'
}
];
book._offers.__defineGetter__(0, function() {
assert(false, 'Iteration of offers for unaffected account');
});
book.on('offer_changed', function() {
assert(false, 'offer_changed event emitted');
});
book.on('offer_funds_changed', function() {
assert(false, 'offer_funds_changed event emitted');
});
book.updateFundedAmounts(message);
setImmediate(done);
});
it('Update funded amounts - no balance change', function(done) {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency_pays: 'USD'
});
var message = {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Account: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV',
Balance: '78991384535796',
Flags: 0,
OwnerCount: 3,
Sequence: 188
},
LedgerEntryType: 'AccountRoot',
LedgerIndex: 'B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A',
PreviousTxnID: 'E9E1988A0F061679E5D14DE77DB0163CE0BBDC00F29E396FFD1DA0366E7D8904',
PreviousTxnLgrSeq: 195455
}
}]
})
};
book._synchronized = true;
book._offers = [
{
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124AF94ED1781B',
BookNode: '0000000000000000',
Flags: 0,
LedgerEntryType: 'Offer',
OwnerNode: '00000000000063CA',
PreviousTxnID: '51C64E0B300E9C0E877BA3E79B4ED1DBD5FDDCE58FA1A8FDA5F8DDF139787A24',
PreviousTxnLgrSeq: 8265275,
Sequence: 1138918,
TakerGets: '50',
taker_gets_funded: '100',
is_fully_funded: true,
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '5'
},
index: 'DC003E09AD1306FBBD1957C955EE668E429CC85B0EC0EC17297F6676E6108DE7',
owner_funds: '162110617177',
quality: '0.000000005148984210454555'
}
];
book.on('offer_changed', function() {
assert(false, 'offer_changed event emitted');
});
book.on('offer_funds_changed', function() {
assert(false, 'offer_funds_changed event emitted');
});
assert.strictEqual(typeof book.getBalanceChange, 'function');
book.getBalanceChange = function() {
assert(false, 'getBalanceChange should not be called');
};
book.addCachedFunds('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', '100');
book.updateFundedAmounts(message);
setImmediate(done);
});
it('Update funded amounts - deferred TransferRate', function(done) {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'USD',
issuer_gets: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency_pays: 'XRP'
});
var message = {
mmeta: new Meta({
AffectedNodes: [{
ModifiedNode: {
FinalFields: {
Balance: {
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
value: '10'
},
Flags: 131072,
HighLimit: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '100'
},
HighNode: '0000000000000000',
LowLimit: {
currency: 'USD',
issuer: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
value: '0'
},
LowNode: '0000000000000000'
},
LedgerEntryType: 'RippleState',
LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959',
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
PreviousTxnLgrSeq: 343570
}
}]
})
};
remote.request = function(request) {
assert.deepEqual(request.message, {
command: 'account_info',
id: undefined,
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
});
request.emit('success', {
account_data: {
Account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
Balance: '6156166959471',
Domain: '6269747374616D702E6E6574',
EmailHash: '5B33B93C7FFE384D53450FC666BB11FB',
Flags: 131072,
LedgerEntryType: 'AccountRoot',
OwnerCount: 0,
PreviousTxnID: '6A7D0AB36CBA6884FDC398254BC67DE7E0B4887E9B0252568391102FBB854C09',
PreviousTxnLgrSeq: 8344426,
Sequence: 561,
TransferRate: 1002000000,
index: 'B7D526FDDF9E3B3F95C3DC97C353065B0482302500BBB8051A5C090B596C6133',
urlgravatar: 'http:www.gravatar.com/avatar/5b33b93c7ffe384d53450fc666bb11fb'
}
});
assert.strictEqual(book._issuerTransferRate, 1002000000);
done();
};
book.addCachedFunds('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', '100');
book.updateFundedAmounts(message);
});
it('Request offers', function(done) {
var remote = new Remote();
var offers = {
offers: [
{
Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
PreviousTxnLgrSeq: 8342933,
Sequence: 195,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.1129232560043778'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '0.1129267125000245',
quality: '496.5',
taker_gets_funded: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.1127013098802639'
},
taker_pays_funded: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '55.95620035555103'
}
},
{
Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.2'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228'
}
]
};
remote.request = function(request) {
switch (request.message.command) {
case 'book_offers':
assert.deepEqual(request.message, {
command: 'book_offers',
id: void(0),
taker_gets: {
currency: '0000000000000000000000004254430000000000',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
taker_pays: {
currency: '0000000000000000000000005553440000000000',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
taker: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
setImmediate(function() {
request.emit('success', offers);
});
break;
}
};
var book = remote.createOrderBook({
currency_gets: 'BTC',
issuer_gets: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency_pays: 'USD',
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
});
book._issuerTransferRate = 1002000000;
var expected = [
{ Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
BookNode: '0000000000000000',
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
PreviousTxnLgrSeq: 8342933,
Sequence: 195,
TakerGets: { currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.1129232560043778'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '56.06639660617357'
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '0.1129267125000245',
quality: '496.5',
taker_gets_funded: '0.1127013098802639',
taker_pays_funded: '55.95620035555102',
is_fully_funded: false },
{ Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
BookNode: '0000000000000000',
Expiration: 461498565,
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '0.2'
},
TakerPays: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '99.72233516476456'
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228',
is_fully_funded: true,
taker_gets_funded: '0.2',
taker_pays_funded: '99.72233516476456'
}
]
book.on('model', function(model) {
assert.deepEqual(model, expected);
assert.strictEqual(book._synchronized, true);
done();
});
});
});

View File

@@ -144,7 +144,7 @@ describe('Remote', function () {
);
});
it('request constructors', function () {
describe('request constructors', function () {
beforeEach(function () {
callback = function () {}
remote = new Remote(options);
@@ -184,6 +184,44 @@ describe('Remote', function () {
var request = remote.request_unl_delete(null, {}, callback);
assert(request instanceof Request);
});
it('request account info with ledger index', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 9592219);
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_index, 9592219);
});
it('request account info with ledger hash', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
});
it('request account info with ledger identifier', function() {
var request = remote.requestAccountInfo('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'validated');
assert.strictEqual(request.message.command, 'account_info');
assert.strictEqual(request.message.account, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_index, 'validated');
});
it('request account balance with ledger index', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 9592219);
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_index, 9592219);
});
it('request account balance with ledger hash', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
});
it('request account balance with ledger identifier', function() {
var request = remote.requestAccountBalance('r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', 'validated');
assert.strictEqual(request.message.command, 'ledger_entry');
assert.strictEqual(request.message.account_root, 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS');
assert.strictEqual(request.message.ledger_index, 'validated');
});
})
it('create remote and get pending transactions', function() {

View File

@@ -326,6 +326,7 @@ describe('Request', function() {
var request = new Request(remote, 'server_info');
request.ledgerChoose();
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.ledger_index, void(0));
});
it('Select ledger - identifier', function() {
@@ -335,6 +336,7 @@ describe('Request', function() {
var request = new Request(remote, 'server_info');
request.ledgerSelect('validated');
assert.strictEqual(request.message.ledger_index, 'validated');
assert.strictEqual(request.message.ledger_hash, void(0));
});
it('Select ledger - index', function() {
@@ -344,6 +346,7 @@ describe('Request', function() {
var request = new Request(remote, 'server_info');
request.ledgerSelect(7016915);
assert.strictEqual(request.message.ledger_index, 7016915);
assert.strictEqual(request.message.ledger_hash, void(0));
});
it('Select ledger - hash', function() {
@@ -353,15 +356,23 @@ describe('Request', function() {
var request = new Request(remote, 'server_info');
request.ledgerSelect('B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.ledger_index, void(0));
});
it('Select ledger - hash', function() {
it('Select ledger - undefined', function() {
var remote = new Remote();
remote._connected = true;
var request = new Request(remote, 'server_info');
request.ledgerSelect('B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
assert.strictEqual(request.message.ledger_hash, 'B4FD84A73DBD8F0DA9E320D137176EBFED969691DC0AAC7882B76B595A0841AE');
request.ledgerSelect();
assert.strictEqual(request.message.ledger_hash, void(0));
assert.strictEqual(request.message.ledger_index, void(0));
request.ledgerSelect(null);
assert.strictEqual(request.message.ledger_hash, void(0));
assert.strictEqual(request.message.ledger_index, void(0));
request.ledgerSelect(NaN);
assert.strictEqual(request.message.ledger_hash, void(0));
assert.strictEqual(request.message.ledger_index, void(0));
});
it('Set account_root', function() {

View File

@@ -5,59 +5,71 @@ var config = require('./testutils').get_config();
describe('Seed', function() {
it('can generate many addresses', function () {
var seed = Seed.from_json("masterpassphrase");
// Note: Created with jRippleAPI code
// Link: https://github.com/pmarches/jRippleAPI/blob/master/src/jrippleapi/keys/RippleDeterministicKeyGenerator.java
var test_data = [
// Format:
// [passphrase, address, nth-for-seed, expected-public-key]
["masterpassphrase", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 0,
"0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020"],
["masterpassphrase", "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", 1,
"02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD"],
["masterpassphrase", "rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx", 2,
"0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC"],
["otherpassphrase", "rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ", 0,
"022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B"],
["otherpassphrase", "raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S", 5,
"03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994"],
["yetanotherpassphrase", "rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo", 0,
"0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036"],
["yetanotherpassphrase", "rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n", 1,
"023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC"]
];
// To reviewer/merger: Even generating keypairs for thi this small amount of
// addresses makes the test suite run SO SLOW. Consider cutting at the tail.
var first_nth_addresses = [
// 0th implied
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
// 1th implied
"r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP",
// 2th implied
"rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx",
// ...
"rN9HWHPAftXC6xNxJRqgVr1HexpUi6FBUa",
"rhwPfeEkeQh2vqoKGPBpPyS9nKmKZdpypu",
"rKdNfPfrzfisDCPCqK67YhDLezuVKGKXNm",
"rsn4NeGzMRvMn5ABQxmt9VNEkSknVneBK4",
"rUSJGFH1kQnaKpoAhkArfyw6HZVe8GT5w3",
"r3EYp6isx2Row5pu19C4Ujvp68oEEabhuA",
"rGiYpnAn9DTXiM78CCJcYAuL6QHEDdazHA",
"rVrRVeqqEJHAove5B9TqBAHEXBTzMi5eu",
"rJbarThDXYXxtCgDxRoTCDf2NoYdgSjuVk",
"rfuWNJ1TkQzoZZcc4K8wmtsUiGwURFbed1",
"rpuTqebfbZZdKEUV3bjecoViNL4W4gepWZ",
"r42ywHUco4C2AjgaSmYM7uX13Kbz6GdDKp",
"rnWb45MLd5hQiB6Arr94DdY2z95quL7XNf",
"rMukaQ87bfAeddcT16RtgS3RbFmaQWrSGu",
"rN6dMHU51K9y8eVMhjQMsQVp5gPwt3eYYx",
"rfRFyeJDNqpfZVFmjNj9tNzFVsCNAWKtNc",
"rMoGSX48KrT31HKx9KfMSnYhjvRw3YYzQW",
"rfTiEfbeVsYU7QEe1Zfogiz7h43x6ChKGC"
]
function assert_helper(account_id, expected) {
var keypair = seed.get_key(account_id);
assert.strictEqual(keypair.get_address().to_json(),
expected);
function assert_helper(seed_json, address_or_nth, expected) {
var seed = Seed.from_json(seed_json);
var keypair = seed.get_key(address_or_nth, 500);
assert.strictEqual(keypair.to_hex_pub(), expected);
}
for (var nth = 0; nth < first_nth_addresses.length; nth++) {
var expected = first_nth_addresses[nth], looking_for;
for (var nth = 0; nth < test_data.length; nth++) {
var seed_json = test_data[nth][0];
var address = test_data[nth][1];
var nth_for_seed = test_data[nth][2];
var expected = test_data[nth][3];
//`seed.get_key($ripple_address)` is arguably an ill concieved feature
// as it needs to generate `nth` many keypairs and generate hashed public
// keys (addresses) for equality tests ??
// Would need remote.set_secret(address, private_key_not_seed) ??
assert_helper((looking_for = expected), expected)
assert_helper(seed_json, address, expected);
// This isn't too bad as it only needs to generate one keypair `seq`
assert_helper((looking_for = nth), expected)
assert_helper(seed_json, nth_for_seed, expected);
};
});
it('should return the key_pair for a valid account and secret pair', function() {
var address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
var seed = Seed.from_json('shsWGZcmZz6YsWWmcnpfr6fLTdtFV');
var keyPair = seed.get_key(address);
assert.strictEqual(keyPair.get_address().to_json(), address);
assert.strictEqual(keyPair.to_hex_pub(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
});
it('should not find a KeyPair for a secret that does not belong to the given account', function() {
var address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
var secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
var seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
try {
seed.get_key(address);
assert(false, 'should throw an error');
} catch(e) {
assert.strictEqual(e.message, 'Too many loops looking for KeyPair yielding '+address+' from '+secret);
}
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -2,6 +2,7 @@ var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var types = utils.load_module('serializedtypes');
var amountConstants = require('../src/js/ripple/amount').consts;
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
var config = require('./testutils').get_config();
@@ -550,6 +551,11 @@ describe('Serialized types', function() {
});
assert.strictEqual(so.to_hex(), 'D5438D7EA4C68000015841551A748AD23FEFFFFFFFEA028000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367');
});
it('Serialize max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject();
types.Amount.serialize(so, amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
assert.strictEqual(so.to_hex(), 'EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
});
it('Parse 1 XRP', function () {
var so = new SerializedObject('4000000000000001');
assert.strictEqual(types.Amount.parse(so).to_json(), '1');
@@ -582,6 +588,10 @@ describe('Serialized types', function() {
var so = new SerializedObject('94838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
assert.strictEqual(types.Amount.parse(so).to_text_full(), '-1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
it('Parse max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
var so = new SerializedObject('EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
assert.strictEqual(types.Amount.parse(so).to_text_full(), amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
});
});
describe('Account', function() {

View File

@@ -478,6 +478,103 @@ describe('Server', function() {
Server.websocketConstructor = websocketConstructor;
});
it('Connect - partial history disabled', function(done) {
var wss = new ws.Server({ port: 5748 });
wss.once('connection', function(ws) {
ws.once('message', function(message) {
var m = JSON.parse(message);
assert.deepEqual(m, {
command: 'subscribe',
id: 0,
streams: [ 'ledger', 'server' ]
});
ws.send(JSON.stringify({
id: 0,
status: 'success',
type: 'response',
result: {
fee_base: 10,
fee_ref: 10,
ledger_hash: '1838539EE12463C36F2C53B079D807C697E3D93A1936B717E565A4A912E11776',
ledger_index: 7053695,
ledger_time: 455414390,
load_base: 256,
load_factor: 256,
random: 'E56C9154D9BE94D49C581179356C2E084E16D18D74E8B09093F2D61207625E6A',
reserve_base: 20000000,
reserve_inc: 5000000,
server_status: 'syncing',
validated_ledgers: '3175520-3176615'
}
}));
wss.close();
});
});
var server = new Server(new Remote({ allow_partial_history: false }), 'ws://localhost:5748');
server.reconnect = function() {
setImmediate(function() {
done();
});
};
server.once('connect', function() {
assert(false, 'Should not connect');
});
server.connect();
});
it('Connect - syncing state', function(done) {
// Test that fee and load defaults are not overwritten by
// undefined properties on server subscribe response
var wss = new ws.Server({ port: 5748 });
wss.once('connection', function(ws) {
ws.once('message', function(message) {
var m = JSON.parse(message);
assert.deepEqual(m, {
command: 'subscribe',
id: 0,
streams: [ 'ledger', 'server' ]
});
ws.send(JSON.stringify({
id: 0,
status: 'success',
type: 'response',
result: {
load_base: 256,
load_factor: 256,
server_status: 'syncing'
}
}));
wss.close();
});
});
var server = new Server(new Remote(), 'ws://localhost:5748');
server.once('connect', function() {
assert(server.isConnected());
assert.strictEqual(server._load_base, 256);
assert.strictEqual(server._load_factor, 256);
assert.strictEqual(server._fee_base, 10);
assert.strictEqual(server._fee_ref, 10);
done();
});
server.connect();
});
it('Reconnect', function(done) {
var server = new Server(new Remote(), 'ws://localhost:5748');
server._connected = true;

View File

@@ -26,8 +26,9 @@ describe('Signing', function() {
assert(_isNaN(new Seed().parse_json('').to_json()));
});
it('hex string', function() {
// 32 0s is a valid hex repr of seed bytes
var str = new Array(33).join('0');
assert(_isNaN(new Seed().parse_json(str).to_json()));
assert.strictEqual((new Seed().parse_json(str).to_json()), 'sp6JS7f14BuwFY8Mw6bTtLKWauoUs');
});
it('passphrase', function() {
var str = new Array(60).join('0');

View File

@@ -229,6 +229,11 @@ describe('Transaction', function() {
assert.strictEqual(transaction._computeFee(), '72');
});
it('Compute fee, no remote', function() {
var transaction = new Transaction();
assert.strictEqual(transaction._computeFee(10), void(0));
});
it('Compute fee - no connected server', function() {
var remote = new Remote();
@@ -292,9 +297,7 @@ describe('Transaction', function() {
var s3 = new Server(remote, 'wss://s-west.ripple.com:443');
s3._connected = true;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
s3._load_factor = (256 * 7) + 1;
// Is this ever possible? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
var s4 = new Server(remote, 'wss://s-west.ripple.com:443');
s4._connected = true;
@@ -373,6 +376,16 @@ describe('Transaction', function() {
done();
});
it('Complete transaction, local signing, no remote', function(done) {
var transaction = new Transaction();
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoij';
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
assert(transaction.complete());
done();
});
it('Complete transaction - untrusted', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote);
@@ -813,6 +826,10 @@ describe('Transaction', function() {
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
assert(!transaction._setLastLedger);
transaction.lastLedger(NaN);
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
assert(!transaction._setLastLedger);
transaction.lastLedger(12);
assert.strictEqual(transaction.tx_json.LastLedgerSequence, 12);
assert(transaction._setLastLedger);
@@ -1017,6 +1034,53 @@ describe('Transaction', function() {
transaction.setFlags('test');
});
it('Add Memo', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', 'testvalue');
transaction.addMemo('testkey2', 'testvalue2');
transaction.addMemo('testkey3');
transaction.addMemo(void(0), 'testvalue4');
var expected = [
{ Memo: {
MemoType: new Buffer('testkey').toString('hex'),
MemoData: new Buffer('testvalue').toString('hex')
}},
{ Memo: {
MemoType: new Buffer('testkey2').toString('hex'),
MemoData: new Buffer('testvalue2').toString('hex')
}},
{ Memo: {
MemoType: new Buffer('testkey3').toString('hex')
}},
{ Memo: {
MemoData: new Buffer('testvalue4').toString('hex')
} }
];
assert.deepEqual(transaction.tx_json.Memos, expected);
});
it('Add Memo - invalid MemoType', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(1);
}, /^Error: MemoType must be a string$/);
});
it('Add Memo - invalid MemoData', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo('key', 1);
}, /^Error: MemoData must be a string$/);
});
it('Construct AccountSet transaction', function() {
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
@@ -1456,6 +1520,18 @@ describe('Transaction', function() {
transaction.submit(submitCallback);
});
it('Submit transaction - submission error, no remote', function(done) {
var transaction = new Transaction();
transaction.once('error', function(error) {
assert(error);
assert.strictEqual(error.message, 'No remote found');
done();
});
transaction.submit();
});
it('Submit transaction - invalid account', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');

View File

@@ -20,6 +20,7 @@ var exampleData = {
email_token : '77825040-9096-4695-9cbc-76720f6a8649',
activateLink : 'https://staging.ripple.com/client/#/register/activate/',
device_id : "ac1b6f6dbca98190eb9687ba06f0e066",
identity_id : "17fddb71-a5c2-44ce-8b50-4b381339d4f2",
blob: {
url: 'https://id.staging.ripple.com',
id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
@@ -103,12 +104,29 @@ var recoverRes = {
result: 'success'
}
}
var getProfileRes = {
"result":"success",
"addresses":[],
"attributes":[{
"attribute_id":"4034e477-ffc9-48c4-bcbc-058293f081d8",
"identity_id":"17fddb71-a5c2-44ce-8b50-4b381339d4f2",
"name":"email",
"type":"default",
"domain":null,
"value":"example@example.com",
"visibility":"public",
"updated":null
}
]
};
var blob = new Blob();
blob.url = exampleData.blob.url;
blob.id = exampleData.blob.id;
blob.device_id = exampleData.device_id;
blob.key = exampleData.blob.key;
blob.identity_id = exampleData.blob.identity_id;
blob.data = exampleData.blob.data;
blob.revision = exampleData.blob.data.revision;
@@ -133,14 +151,12 @@ var mockDelete;
if (!online) {
mockRippleTxt = nock('https://ripple.com')
.persist()
.get('/ripple.txt')
.reply(200, rippleTxtRes, {
'Content-Type': 'text/plain'
});
mockRippleTxt2 = nock('https://' + exampleData.domain)
.persist()
.get('/ripple.txt')
.reply(200, rippleTxtRes, {
'Content-Type': 'text/plain'
@@ -153,21 +169,21 @@ if (!online) {
'Content-Type': 'text/plain'
});
mockRegister = nock('https://id.staging.ripple.com').persist();
mockRegister = nock('https://id.staging.ripple.com');
mockRegister.filteringPath(/(v1\/user\?signature(.+))/g, 'register/')
.post('/register/')
.reply(200, { result: 'error', message: 'User already exists' }, {
'Content-Type': 'application/json'
});
mockDelete = nock('https://id.staging.ripple.com').persist();
mockDelete = nock('https://id.staging.ripple.com');
mockDelete.filteringPath(/(v1\/user\/(.+))/g, 'delete/')
.delete('/delete/')
.reply(200, { result: 'success' }, {
'Content-Type': 'application/json'
});
mockBlob = nock('https://id.staging.ripple.com').persist();
mockBlob = nock('https://id.staging.ripple.com');
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.username.toLowerCase())
.reply(200, JSON.stringify(authInfoRes.body), {
'Content-Type': 'application/json'
@@ -185,47 +201,40 @@ if (!online) {
'Content-Type': 'application/json'
});
mockRename = nock('https://id.staging.ripple.com/v1/user/').persist();
mockRename = nock('https://id.staging.ripple.com/v1/user/');
mockRename.filteringPath(/((.+)\/rename(.+))/g, 'rename/')
.post('rename/')
.reply(200, {result:'success',message:'rename'}, {
'Content-Type': 'application/json'
});
mockUpdate = nock('https://id.staging.ripple.com/v1/user/').persist();
mockUpdate.filteringPath(/((.+)\/update(.+))/g, 'update/')
mockUpdate = nock('https://id.staging.ripple.com/v1/user/');
mockUpdate.filteringPath(/((.+)\/updatekeys(.+))/g, 'update/')
.post('update/')
.reply(200, {result:'success',message:'updateKeys'}, {
'Content-Type': 'application/json'
});
mockRecover = nock('https://id.staging.ripple.com/').persist();
mockRecover = nock('https://id.staging.ripple.com/')
mockRecover.filteringPath(/((.+)user\/recov\/(.+))/g, 'recov/')
.get('recov/')
.reply(200, recoverRes.body, {
'Content-Type': 'application/json'
});
mockVerify = nock('https://id.staging.ripple.com/v1/user/').persist();
mockVerify = nock('https://id.staging.ripple.com/v1/user/');
mockVerify.filteringPath(/((.+)\/verify(.+))/g, 'verify/')
.get('verify/')
.reply(200, {result:'error', message:'invalid token'}, {
'Content-Type': 'application/json'
});
mockEmail = nock('https://id.staging.ripple.com/v1/user').persist();
mockEmail = nock('https://id.staging.ripple.com/v1/user');
mockEmail.filteringPath(/((.+)\/email(.+))/g, 'email/')
.post('email/')
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
mockProfile = nock('https://id.staging.ripple.com/v1/user').persist();
mockProfile.filteringPath(/((.+)\/profile(.+))/g, 'profile/')
.post('profile/')
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
}
describe('Ripple Txt', function () {
@@ -419,6 +428,7 @@ describe('VaultClient', function () {
});
});
/*
describe('#updateProfile', function () {
it('should update profile parameters associated with a blob', function (done) {
this.timeout(10000);
@@ -442,7 +452,7 @@ describe('VaultClient', function () {
});
});
});
*/
});
@@ -693,7 +703,95 @@ describe('Blob', function () {
});
});
});
describe('identityVault', function() {
it('#identity - Get Attestation', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
options.type = 'identity';
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/identity(.+))/g, '')
.post('/')
.reply(200, {
result: 'success',
status: 'verified',
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
}, {'Content-Type': 'application/json'});
client.getAttestation(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.blinded, 'string');
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
done();
});
});
it('#identity - Update Attestation', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
options.type = 'identity';
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/identity\/update(.+))/g, '')
.post('/')
.reply(200, {
result: 'success',
status: 'verified',
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
}, {'Content-Type': 'application/json'});
client.updateAttestation(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.blinded, 'string');
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
done();
});
});
it('#identity - Get Attestation Summary', function (done) {
var options = {
url : blob.url,
auth_secret : blob.data.auth_secret,
blob_id : blob.id,
};
nock('https://id.staging.ripple.com')
.filteringPath(/(v1\/attestation\/summary(.+))/g, '')
.get('/')
.reply(200, {
result: 'success',
attestation: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2ZGI3MzgxIn0%3D.eyJwcm9maWxlX3ZlcmlmaWVkIjpmYWxzZSwiaWRlbnRpdHlfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2lkLnJpcHBsZS5jb20iLCJzdWIiOiIwNDMzNTA0ZS0yYTRmLTQ1NjktODQwMi1lYWI2YTU0YTgzYjUiLCJleHAiOjE0MTI4MTc2NjksImlhdCI6MTQxMjgxNTgwOX0%3D.Jt14Y2TsM7fKqGWn0j16cPldlYqRr7%2F2dptBsdZuZhRGRTREO4TSpZZhBaU95WL3M9eXIfaoSs8f2pTOa%2BBGAYHZSZK4%2FLqeWdDH8zz8Bx9YFqGije1KmHQR%2FeoWSp1GTEfcq5Oho4nSHozHhGNN8IrDkl8woMvWb%2FE1938Y5Zl2vyv7wjlNUF4ND33XWzJkvQjzIK15uYfaB%2FUIsNW32udfHAdkigesdMDNm%2BRGBqHMDZeAMdVxzrDzE3m8oWKDMJXbcaLmk75COfJrLWYiZCHd7VcReyPEZegwEucetZJ9uDnoBcvw0%2B6hIRmjTN6Gy1eeBoJaiDYsWuOwInbIlw%3D%3D',
}, {'Content-Type': 'application/json'});
client.getAttestationSummary(options, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.result, 'success');
assert.strictEqual(typeof resp.attestation, 'string');
assert.strictEqual(typeof resp.decoded.header, 'object');
assert.strictEqual(typeof resp.decoded.payload, 'object');
assert.strictEqual(typeof resp.decoded.signature, 'string');
done();
});
});
});
//only do these offline
if (!online) {
@@ -709,7 +807,7 @@ describe('Blob', function () {
});
it('#2FA_get2FA', function (done) {
blob.get2FA(exampleData.masterkey, function(err, resp) {
blob.get2FA(function(err, resp) {
assert.ifError(err);
assert.strictEqual(typeof resp, 'object');
assert.strictEqual(typeof resp.result, 'string');